Patrick Craig > Tips and Tricks

Patrick's Tips and Tricks

By choosing to use any of the techniques described below, you accept responsibilty for any problems they may cause.


Windows

  1. Windows 95 Start button bug
  2. Adding shortcuts to the Start menu
  3. Windows Explorer shortcuts with starting directory
  4. Recovering text files from corrupt floppy disks
  5. stdout from Windows programs

Unix

  1. Converting ASCII files from DOS to unix format
  2. Shell scripts

Java

  1. Building Java projects with Microsoft Visual C++
  2. Don't forget Mac OS X

Windows

  1. Windows 95 Start button bug
  2. I won a T-shirt for reporting this to a computing magazine in 1996! I suggest you read all of the following before decided to try it out.

    Hold down the Ctrl key and press Esc to make the Start button active (Start menu appears - this works on all Windows versions with a Start button and means you don't need to buy a keyboard with a "Start Menu" button). Hold down the Alt key (Start menu disappears but button is still active) and press the space bar. The Start button "system menu" appears. Press the up arrow key to select the Close option and press return - the Start button disppears! (only on Windows 95, on other versions the shutdown menu appears). To log off or shutdown without a Start button, click on the task bar (e.g. where the Start button used to be) and press Alt+F4. You can also move the Start button on Windows 95 by selecting the Move option from the system menu.

  3. Adding shortcuts to the Start menu
  4. Adding items to the main Start menu:

    Select the item in Windows Explorer and drag it onto the Start button. See below for how to remove items added in this way.

    Adding and removing items from Start menu and submenus.

    Right click on the task bar and select "Properties". Select the second tab in the popup dialog box and use the Add and Remove buttons to add and remove items from the Start menu.

  5. Windows Explorer shortcuts with starting directory
  6. It's often useful to create a shortcut to Explorer that starts in a specific directory. You can do this using the /e option of Explorer, e.g. if you're using Windows NT and you want Explorer to start in directory C:\users\jimdandy, change the shortcut command line to:

    C:\WINNT\EXPLORER.EXE /e,C:\users\jimdandy

  7. Recovering text files from corrupt floppy disks
  8. If when you try to copy files from a floppy disk you get a read error and it only copies a small portion of the file, find a linux machine and try using the mcopy command, e.g.

    mcopy a:/myfile.txt .

    This happened to me with the MineSaver source code. When I tried to copy from the floppy in Windows I only got the top few lines of source. When I copied the file using mcopy on Linux, I got the complete file with a line of garbage at the point where the file was truncated on Windows.

  9. stdout from Windows programs
  10. When I started writing Windows programs I didn't know how to use a debugger so all my previous debugging was done using printf statements. I was therefore a bit put out when it seemed that Windows programs didn't have any stdout and printf statements had no effect. My solution was to use the MessageBox function instead, but this gets a bit messy if you need a lot of debugging output to find the bug.

    It wasn't until much later (and after I had worked out how to use a debugger!) that I discovered that it is possible to see the output from printf statements in Windows programs. On way of doing this is to run the program from a DOS prompt and send the output to a file, e.g.

    myprog > debug.log

    Normally you want to see the output from the program as it is produced. You can do this by piping the output to more:

    myprog | more

    This works, but because stdout is normally buffered you don't see any output until the buffer is full or the program terminates. To get round this problem, you can make stdout unbuffered. This example program demonstrates this technique:

    #include <stdio.h>
    #include <windows.h>
    
    int WINAPI WinMain(HINSTANCE  hInstance,HINSTANCE  hPrevInstance,LPSTR  lpCmdLine,int  nShowCmd)
    {
    	int nResponse;
    	setbuf(stdout,NULL);  /* Make stdout unbuffered */
    	do
    	{
    		nResponse = MessageBox(NULL,"Press Yes or No for output, Cancel to exit\n",
    			"Windows stdout", MB_YESNOCANCEL);
    		switch (nResponse)
    		{
    		case IDYES:
    			printf("Yes button pressed\n");
    			break;
    		case IDNO:
    			printf("No button pressed\n");
    			break;
    		}
    	}
    	while (nResponse != IDCANCEL);
    	return 0;
    }
    

    The only remaining problem is that the more command only prints one page of output at a time so you have to press the space bar at the end of each page (although this could actually be an advantage in some situations). One solution is to create a very simple C program that just outputs whatever is piped into it, e.g.
    #include <stdio.h>
    
    int main()
    {
    	while (!feof(stdin)) putchar(getchar());
    	return 0;
    }
    
    If you called this program winpipe you would then run:

    myprog | winpipe

Unix

  1. Converting ASCII files from DOS to unix format
  2. Assuming the file is on a unix machine, there are several ways of doing this. The methods I have used in chonological order are:

    1. ftp the file to a DOS machine in binary mode and then ftp it back in ASCII mode. This works but is a bit of a pain if you have to process many files.
    2. Write a simple C program to strip out the extra carriage return characters. This is probably the most efficient solution, but I invariably lost the program and its source and had to rewrite it whenever I needed to process a large number of files.
    3. Use standard unix tools, e.g. tr:
    cat dos.txt | tr -d '\r' > unix.txt

  3. Shell scripts
  4. Shell scripts are just ASCII files that you make executable using the chmod command, e.g.

    chmod +x myscript

    Most of my scripts for doing batch jobs are based on the following template:

    #! /bin/sh
    
    for i in `ls`
    do
    	echo $i
    done
    
    NOTE : The ` character is not a '

    This script just prints out the contents of the current directory so is not very useful. A few modifications allows you to convert all the ASCII files called *.txt in the current directory from DOS to unix format:

    #! /bin/sh
    
    for i in `ls *.txt`
    do
    	echo Converting $i
    	cat $i | tr -d '\r' > $i.tmp
    	mv -f $i.tmp $i
    done
    

    "Find in files" script. This searches text files for a given pattern. It searches the given directory and all subdirectories. If no directory is given it uses the current directory.
    Example usage:
    fif /usr/include/sys/ malloc
    /usr/include/sys//malloc.h
    56: *   @(#)malloc.h    8.5 (Berkeley) 5/3/95
    67: * flags to malloc
    289: * The malloc/free primatives used
    
    /usr/include/sys//mbuf.h
    230:/* Need to include malloc.h to get right options for malloc  */
    231:#include    
    
    /usr/include/sys//namei.h
    186: * size a power of two to optimize malloc's. Minimum reasonable
    
    The script:

    #! /bin/sh
    
    #Function to explain script usage
    usage()
    {
            echo "Usage: $0 [directory] pattern"
            echo "  searches for pattern in ascii files"
            exit 1
    }
    
    #Check correct number of arguments (1 or 2)
    if [ $# -ne 1 -a $# -ne 2 ]
    then
            usage
    fi
    
    #If only one argument (pattern) use current directory as directory
    if [ $# -eq 2 ]
    then
            start=$1
            shift
    else
            start="."
    fi
    
    pattern=$1
    
    #Do the work
    for found in `find $start -type f`
    do
    #       Check if the file is text
            (file $found | grep text) > /dev/null
            if [ $? -eq 0 ]
            then
    #               See if the file contains the pattern
                    (grep "$pattern" $found) > /dev/null
                    if [ $? -eq 0 ]
                    then
    #                       Found one or more matches print filename and matches
                            echo $found
                            grep -n "$pattern" $found
                            echo
                    fi
            fi
    done
    
    Finally, the most complex script I have ever written. I used to use this before I used CVS for source control. This script merges two source code trees. You give the source and destination directories as arguments and when it finds source files that differ, it prints out the differences given by diff and then gives you the option of overwritting the destination file, or merging by hand using xdiff. Before using the script you have to configure it for the machine, by adding the name returned from "hostname" to the case statement at the begining of the script.
    #! /bin/sh
    
    #
    #       script to merge source files
    #
    #       author patrick@nag-j.co.jp 12.3.99
    #
    #       Requires GNU diff (for -q) and X11/Motif xdiff
    #
    #       Set the compare and merge variables in the following case
    #       statement to point to these executables on your machine
    #
    #       If your GNU diff distinguishes between binary and acsii
    #       files by saying Binary files x and y differ rather than
    #       just Files x and y differ with the -q option, you can set
    #       binaryok to 1
    #
    
    binaryok=0
    diffargs="-wb"
    recusivediff="-rq"
    mergequiet="-D"
    mergeargs="-wb"
    
    #  *** Add your machine to this case statement ***
    case `hostname` in
    hal )
            compare=/bin/diff
            merge="/usr/sbin/xdiff"
            binaryok=1
                    ;;
    server )
            compare=/usr/local/bin/diff
            merge="/usr/local/bin/xdiff"
                    ;;
    extreme )
            compare=/usr/local/bin/diff
            merge="/usr/bin/X11/demos/mgdiff"
            mergequiet="-quit"
            mergeargs="-args -wb"
                    ;;
    * )
            echo $0: Sorry not configured for this machine
            echo "  If you have GNU diff and X11/Motif xdiff installed add"
            echo "  this machine to the case statement at the begining of"
            echo "  " $0
            exit 1
                    ;;
    esac
    
    usage()
    {
            echo "Usage: $0 [-lnsIm] [-aargs] [-dargs] [-xargs] [-efile] source dest"
            echo "  -I  -- Ignore Imakefiles"
            echo "  -M  -- Ignore Makefiles"
            echo "  -l  -- list mode. Just lists files that differ"
            echo "  -n  -- (nowarnings) Does not warn when file to be overwritten is newer"
            echo "  -s  -- (superuser) Allows source to be overwritten (recover)"
            echo "  -aargs  -- pass args to diff and xdiff"
            echo "  -dargs  -- pass args to diff"
            echo "  -xargs  -- pass args to xdiff"
            echo "  -efile  -- echo everything to file"
            exit 1
    }
    
    echotolog ()
    {
            if [ $logfile ]
            then
                    echo "$*" >> $logfile
            fi
    }
    
    printout()
    {
            echo "$*"
            echotolog "$*"
    }
    
    printnonl()
    {
            printf "$*"
            if [ $logfile ]
            then
                    printf "$*" >> $logfile
            fi
    }
    
    getresponse ()
    {
            read ans
            echotolog $ans
    }
    
    testcontinue ()
    {
            printnonl " Continue anyway (y,[n])?"
            getresponse
            case "$ans" in
                    y*)     :
                            ;;
                    *)      exit 1
                            ;;
            esac
    }
    
    finalcopy()
    {
            cp -f $1 $2
            if [ $? -eq 0 ]
            then
                    printout $2
                    printout Overwritten by
                    printout $1
            else
                    printnonl Copy failed.
                    testcontinue
            fi
    }
    
    copyfile()
    {
            if [ $nowarning -eq 1 ]
            then
                    finalcopy $2 $1
            else
                    tail=`basename $1`
                    found=`find $1 -name $tail -newer $2`
                    if [ $found ]
                    then
                            printnonl "***Are you sure?" $1 is newer than $2 "(y,[n])?"
                            getresponse
                            case "$ans" in
                                    y*)     finalcopy $2 $1
                                            ;;
                            esac
                    else
                            finalcopy $2 $1
                    fi
            fi
    }
    
    
    
    if [ $# -lt 2 ]
    then
            usage
            exit 1
    fi
    
    nowarning=0
    listmode=0
    superuser=0
    while [ $# -gt 2 ]
    do
            case $1 in
            -* )
                    options=`expr "$1" : '-\(.*\)'`
                            ;;
            * )
                    options=$1
                            ;;
            esac
            case $options in
            e* )
                    logfile=`expr "$options" : 'e\(.*\)'`
                            ;;
            a* )
                    args=`expr "$options" : 'a\(.*\)'`
                    userdargs="$userdargs $args"
                    userxargs="$userxargs $args"
                            ;;
            d* )
                    args=`expr "$options" : 'd\(.*\)'`
                    userdargs="$userdargs $args"
                            ;;
            x* )
                    args=`expr "$options" : 'x\(.*\)'`
                    userxargs="$userxargs $args"
                            ;;
            * )
            case $options in
            *[!lnsIM]* )
                    echo $0: Unknown option $1
                    usage
                            ;;
            esac
            case $options in
            *[l]* )
                    listmode=1
                            ;;
            esac
            case $options in
            *[n]* )
                    nowarning=1
                            ;;
            esac
            case $options in
            *[s]* )
                    superuser=1
                            ;;
            esac
            case $options in
            *[I]* )
                    skipImake="-xImakefile"
                            ;;
            esac
            case $options in
            *[M]* )
                    skipMake="-xMakefile"
                            ;;
            esac
            ;;
            esac
            shift
    done
    
    if [ $listmode -ne 1 ]
    then
    #       check DISPLAY is set and xdiff works
            $merge $mergequiet $0 $0
    
            if [ $? -ne 0 ]
            then
                    printnonl Will not be able to merge files.
                    testcontinue
            else
                    canmerge=1
            fi
    fi
    
    if [ $listmode -eq 1 ]
    then
            echo List mode
    fi
    
    if [ $nowarning -eq 1 ]
    then
            echo Warnings disabled
    fi
    
    if [ $superuser -eq 1 ]
    then
            echo You are superuser
    fi
    
    if [ $skipImake ]
    then
            echo Ignoring Imakefiles
    fi
    
    if [ $skipMake ]
    then
            echo Ignoring Makefiles
    fi
    
    if [ $logfile ]
    then
            echo Echoing to $logfile
    fi
    
    if [ "$userdargs" ]
    then
            echo Extra diff args $userdargs
    fi
    
    if [ "$userxargs" ]
    then
            echo Extra xdiff args $userxargs
    fi
    
    echo Comparing files. Please wait...
    
    if [ $listmode -eq 1 ]
    then
            if [ binaryok -eq 1 ]
            then
                    printout `$compare $recusivediff $diffargs $userdargs $skipImake $skipMake $1 $2 | grep -v "Only in"\
                            | grep -v "Binary files"  | awk '{print $2,$4 }'`
            else
                    file1="_"
                    for file2 in `$compare $recusivediff $diffargs $userdargs $skipImake $skipMake $1 $2 | grep -v "Only in"\
                            | awk '{print $2,$4 }'`
                    do
                            if [ $file1 = "_" ] ; then file1=$file2 ;
                            else
                                    $compare $diffargs $userdargs $file1 $file2 | grep "Binary files" > /dev/null
                                    if [ $? -ne 0 ]
                                    then
                                            printout $file1 $file2
                                    fi
                                    file1="_"
                            fi
                    done
            fi
            exit 0
    fi
    
    file1="_"
    for file2 in `$compare $recusivediff $diffargs $userdargs $skipImake $skipMake $1 $2 | grep -v "Only in"\
            | grep -v "Binary files"  | awk '{print $2,$4 }'`
    do
            if [ $file1 = "_" ] ; then file1=$file2 ;
            else
                    continue=1
                    if [ binaryok -eq 0 ]
                    then
                            $compare $diffargs $userdargs $file1 $file2 | grep "Binary files" > /dev/null
                            if [ $? -eq 0 ]
                            then
                                    continue=0
                            fi
                    fi
                    if [ $continue -eq 1 ]
                    then
                            printout \\n\\n\\n
                            $compare $diffargs $userdargs $file1 $file2
                            if [ $logfile ]
                            then
                                    $compare $diffargs $userdargs $file1 $file2 >> $logfile
                            fi
                            ($compare $diffargs $userdargs $file1 $file2 | awk '{ print $1 }' | grep ">") > /dev/null
                            noex1=$?
                            ($compare $diffargs $userdargs $file1 $file2 | awk '{ print $1 }' | grep "<") > /dev/null
                            noex2=$?
                            if [ $noex1 -ne 1 -o $noex2 -ne 1 ]
                            then
                                    ls -og $file1 | awk '{printf "\n\n< %s\n    Date : ",$NF} \
                                            {for (i=4; i < NF;i++) printf " %s",$i }\
                                            { print "   Size : "$3 }'
                                    if [ $noex1 -eq 0 -a $noex2 -eq 0 ]
                                    then
                                            printout "          May need merging with"
                                    elif [ $noex1 -eq 0 ]
                                    then
                                            printout "          Has less code than"
                                    else
                                            printout "          Contains extra code not in"
                                    fi
                                    ls -og $file2 | awk '{printf "> %s\n    Date : ",$NF} \
                                            {for (i=4; i < NF;i++) printf " %s",$i }\
                                            { print "   Size : "$3 }'
                                    printout
                                    printout Overwrite $file2 "(yes,[no]"\\c
                                    if [ $canmerge ]
                                    then
                                            printout ,merge\\c
                                    fi
                                    if [ $superuser -eq 1 ]
                                    then
                                            printout ",recover"\\c
                                    fi
                                    printout ")?"\\c
                                    getresponse
                                    case "$ans" in
                                            y*)     copyfile $file2 $file1
                                                            ;;
                                            r*)     if [ $superuser -eq 1 ]
                                                    then
                                                            printout Overwrite $file1 "(yes,[no])?"\\c
                                                            getresponse
                                                            case "$ans" in
                                                            y*)     copyfile $file1 $file2
                                                                    ;;
                                                            esac
                                                    fi
                                                            ;;
                                            m*)     if [ $canmerge ]
                                                    then
                                                            chmod +w $file2
                                                            if [ $superuser -eq 1 ]
                                                            then
                                                                    chmod +w $file1
                                                            fi
                                                            $merge $mergeargs $userxargs $file1 $file2
                                                            $compare $diffargs $userdargs $file1 $file2 > /dev/null
                                                            if [ $? -eq 0 ]
                                                            then
                                                                    printout $file1
                                                                    printout Merged with
                                                                    printout $file2
                                                            else
                                                                    printout Not merged
                                                            fi
                                                            chmod -w $file2
                                                            if [ $superuser -eq 1 ]
                                                            then
                                                                    chmod -w $file1
                                                            fi
                                                    fi
                                                            ;;
                                    esac
                            fi
                    fi
                    file1="_"
            fi
    done
    

Java

  1. Building Java projects with Microsoft Visual C++
  2. These instructions are for Microsoft Visual C++ 6.0 and the Sun Java 2 version 1.3.1 SDK, but they can probably be adapted to other versions of Visual Studio/C++ and other versions of Java or even compilers for other languages.

  3. Don't forget Mac OS X
  4. A few tips to make your Java program "Mac friendly"