Assignment 5, due Sept. 30

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: Consider a C program containing the statement: fputc( 'x', stdout ); to send output to an archaic character sequential printer attached to a Linux machine and configured as standard output of the running program. Eventually, the execution of this statement causes the 'x' character to be printed on the page, advancing the print head one step, but there are several steps involved in this:
    1. fputc( 'x', stdout ); is called.
    2. write() is called.
    3. 'x' is put in the output data register of the printer port.
    4. 'x' is copied from the user's buffer to a system buffer.
    5. 'x' is added to the user's output buffer.
    6. 'x' is printed on the output page.

    A problem In what order do the above steps happen? (The first and final steps above are given in the correct order.) (0.5 points)

  2. Background: Consider the old IBM PC serial port defined in the notes for Sept. 20. Assuming that the baud rate has already been set, the only registers that matter are COM1DATA and COM1LSR. Assume the following definitions:
    #define COM1DATA 0x3F8
    #define COM1LSR  0x3FD
    #define RxRD     (1 << 0)
    #define RxOE     (1 << 1) /* overrun error */
    #define RxPE     (1 << 2) /* parity error */
    #define RxFE     (1 << 3) /* framing error */
    #define RxBR     (1 << 4) /* break */
    #define TxDE     (1 << 5)
    #define TxID     (1 << 6)

    Overrun error indicates that new data arrived before the previous data was read. The old data is lost, but the received data in COM1DATA is valid. In the case of parity and framing errors, the data in COM1DATA is not valid. parity and framing errors, the data in COM1DATA is not valid. The break condition does not itself represent an error, but is an auxilary indicator indicating that an overrun error was very long.

    As in the notes, assume that you can use inp(r) to read data from device register r and outp(r,d) to output the data d to device registerr.

    a) Write getcom1(), a routine to directly implement a read from com1, ignoring the possibility of error. Keep it simple! Minimize your code. Few or no comments are needed. (0.5 points)

    b) Revise your solution to part a) so that, in the event that the COM1DATA is invalid because of an error, the invalid value is ignored. That is, in the event of such an error, getcom1() should continue to wait for valid input. (0.5 points)

  3. Background: Consider this code based on the classic UNIX implementation of putc():
    typedef struct file {
            int fd;
            int count;
            int size;
            char * buf;
    } FILE;
    #define putc(ch,f) (                                    \
            buf[ f->count++ ] = ch                          \
            f->count >= f->size?(                           \
                    write( f->fd, f->buf, f->size ),        \
                    f->count = 0                            \
            ):(                                             \
                    0                                       \
            ),                                              \
    #define putchar(ch) (putc( ch, stdout ))
    void fputc( char ch, FILE * f ) {
            putc( ch, f );

    In C, source lines ending with backslash (\) are concatenated to the following line. This allows a #define to span multiple lines. In C, expressons of the form (a?b:c) are conditional; if In C, expressons of the form a is true, the value is b, otherwise, the value is c. In C, x->y refers to the field named y of the structure pointed to by the pointer x.

    a) When a file f is opened for output, what is the correct initial value for f->count? (0.5 points).

    b) Why not use a constant size for the buffer? That is, why would it be useful to wait until the file is opened to decide what size buffer to allocate? (0.5 points).

    c) Give an appropriate implementation of fflush() for this implementation of files. (Ignore the special behavior defined for a null parameter.)


Machine Problem III

Due Monday, Oct. 7

You will have to modify a version of mush.c as modified by the requirements of mp2. If you have not solved mp2 yourself, you should do so even if you end up using the MP2 solution distributed to the class.

Modify mush.c so support the following features:

  1. Any argument parsed from the command line that begins with a dollar sign should be replaced with the value of the corresponding string from the environment. As a result, the command echo $PATH should end up working as it does under tcsh. This should be almost trivial.

  2. Add support for the built-in shell command exit. This should be similarly easy, and is included to force you to create a framework allowing you to implement other built-in commands. Your version of exit should not accept any arguments and should call exit() in the C standard library, passing it EXIT_SUCCESS.

  3. Add support for the built-in shell command setenv. This should work in much the way it does under tcsh, except that you are not to worry about quoted strings as arguments. You can use the setenv() routine from the C standard library to do this, and it is surprisingly easy. Your code must detect an incorrect number of arguments and respond appropriately.

You may need to look up and understand the man page for each routine you use. Your solution should conform, as much as is reasonable, to the manual of style. Your solution must be in a file named mp3.c and you should submit it using the coursework submission tools, as for mp1 and mp2 but in the mp3 directory for the course.


Here are some comments in response to questions I received after class:

The syntax of your shell command should be setenv VAR value (with no additional arguments, and certainly not an "overwrite" argument). Do not worry about the complex behavior of setenv with no arguments. Your concern is using it to set the value of environment variables.

Note that after using the shell command setenv VAR value, echo $VAR should output value. After setenv VAR, with no value given, then $VAR is replaced with an empty string. This is not the same as $VAR being undefined.

Play with the existing shell? What does it do with echo $WHOZZIT (where the shell variable WHOZZIT is an example of an undefined variable). Your shell should do something similar.

How does it work for blank lines? They are ignored by the standard Unix shells, and the solution distributed for MP2 also ignores them (more by accident than by design). Your solution should continue to ignore them, but this may take an added line of code, since the string comparison to test for built-in commands is likely to need the first pointer in argv to be non-null.

The strings package (the one containing strcat() and strlen() also contains strcmp() for comparing strings. strcmp(a,b) returns zero (false) if the strings are identical and nonzero (true) if they are not identical.