Assignment 3, due Sept. 13

Solutions

Part of the homework for 22C:112, Fall 2013
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

On all assignments, your name must be legible as it appears on your University ID card! Assignments are due at the start of class on the day indicated (usually Friday). Exceptions will be by advance arrangement unless there is what lawyers call "an act of God" (something outside your control). Homework must be turned in on paper, either in class or in the teaching assistant's mailbox. Never push late work under someone's door!

  1. Background: Look at the code for the minimally usable shell, mush.c distributed here:

    http://homepage.cs.uiowa.edu/~dwjones/opsys/notes/mush.txt

    a) As written, the code only allows blanks (that is ' ') as delimiters between the arguments of a shell command. What routine would need to be changed and how many lines of code would you need to add or change in order to make the code permit both blanks and tabs (that is, '\t') as delimiters. Do not give the code, just give the routine name and number of lines. (0.5 points)

    In parseargv() change

    while (command[j] == ' ') j++;
    

    to read

    while ((command[j] == ' ')||(command[j] == '\t')) j++;
    

    and change

    while ((command[j] != ' ')
    &&     (command[j] != '\000')) {
    

    to read

    while ((command[j] != ' ')
    &&     (command[j] != '\t')
    &&     (command[j] != '\000')) {
    

    b) The call to execve in launch() passes NULL as its third parameter (the environment). How would you change this so that it passes the environment that was passed to the caller? Hint: You might want to use man execve and man environ to learn more. Do not give variable definitions. Just give the modified code for the execve statement. (0.5 points)

    Change:

    execve( argv[0], argv, NULL );
    

    to read:

    execve( argv[0], argv, environ );
    

  2. Background: Consider this fragment of bash shell script:
    if [ -x thisfile ]
    then
        thisfile
    else
        if [ -x thatfile ]
        then
            thatfile
        else
            echo failure
        fi
    fi
    exit
    

    In bash, the [ -x f ] predicate returns true if its argument f is the name of an executable file.

    A problem: Write equivalent C code, using execve() to launch either thisfile or thatfile or output the error message failure. Hint: This can be done in 4 lines of reasonably formatted C code; examine how launch() in mush.c works. code would it take> (1.0 points)

    Here is a first hack at a solution:

    execve( "thisfile", NULL, NULL );
    execve( "thatfile", NULL, NULL );
    puts( "failure" );
    exit();
    

    That is a bit rough, since the executed files might expect properly formed arguments and might depend on the environment. Here is a mature solution:

    char *thisargv[] = { "thisfile", NULL };
    char *thatargv[] = { "thatfile", NULL };
    execve( thisargv[0], thisargv, environ );
    execve( thatargv[0], thatargv, environ );
    puts( "failure" );
    exit( EXIT_FAILURE );
    

  3. Background: Consider the C standard library routines fseek() and ftell(); these are implemented by middleware on top of the Linux kernel call lseek(). The man shell command documents all of these. Note that the C standard library routines fputc() and fgetc() could be implemented as follows, where file variables are simply pointers to the integer file descriptor (a very bad design):
    int fputc( int c, FILE *f ) {
         write( *f, &(unsigned char)c, 1 );
         return c;
    }
    
    int fgetc( FILE *f ) {
         unsigned char c;
         if (read( *f, &c, 1 ) == 1) return (int) c;
         return EOF; /* if it did not return a character */
    }
    

    A Problem: Write very brief (and equally stupid) C implementations for fseek() and ftell(). Do not handle error conditions. The body of each of your routines is likely to be just one line. (1 point)

    Here is the stupid code:

    int fseek( FILE * f, long offset, int whence ) {
    	lseek( *f, offset, whence );
    	return 0;
    }
    
    long int ftell( FILE * f ) {
    	return lseek( *f, 0, SEEK_CUR );
    }