CST 8152 - Assignment Two

Topic: File operations.

This page last updated: Sunday September 27, 1998 01:07

Deliverables for this assignment:

  1. Due Date: 12 noon, Monday February 9
  2. Hand in, on paper, the fully documented source listing of your program (including your own .h files).
  3. The submission must follow the online course submission standards, including an Assignment Submission Label and a Table of Contents.
  4. Include a description or listing of your input test file(s) (and output, if appropriate) showing how you tested your program.  Don't kill a hundred trees; submit descriptions and short excerpts of your testing input and output if the files are large.   The intent of your test submissions is to convince me that your program works.
  5. No demonstration is required; however, I am always pleased to see working code during lab hours.

Purpose:

This is a review of C language file operations.  You build a generalized file opening interface using routines from the previous assignment and use it to open an input stream and read and print lines from the stream in a manner similar to the previous assignment.

This file opening interface will be the same one that opens the input files for your interpreter in subsequent assignments.

Instructions:

Part 0 (optional but highly recommended)
Get the MEM package and install it in a simple program.  Learn how it works.   Then install it in your Assignment 1, and use it all subsequent assignments, including this one.
Part 1
In Assignment 1, the my_prompt() function read characters only from stdin.  Modify the function using the new function prototype, given below, so that it reads characters from the open file descriptor passed as a new first argument.  (You can test it by passing stdin as the first argument.)  Remove the superfluous Buffer pointer return value from the function prototype and definition.
Part 2
Simplify all the bf_ Buffer function prototypes by removing the superfluous Buffer pointer return values.  All the bf_ functions will either succeed or exit; they will never return a failure status.  Use the simplified function prototypes (or similar prototypes), given below.
Part 3
Write a function my_open() that opens an input or output file by name, returning the associated file pointer on success, NULL on failure.  If no name is given, the function will prompt for the input file name.  Use the function prototype and functional specification given below.
Part 4
Take note of the new bf_addstr() function I give you, and the utility functions modestr() and fopenmode().  Copy and use these if you need them.
Part 5
Use the given main() program, below, to drive your assignment.  It uses the my_open() function to prompt for a file name and open it for reading.  It passes the returned file pointer to my_prompt() in a loop that reads and prints lines from the open input stream, as in Assignment 1.

Part 1: Modifications to my_prompt()

static void my_prompt(
    FILE *fd,       /* IN: from where to read input */
    char *prompt,   /* IN: prompt string to issue, if a tty */
    Buffer *lbuf    /* IN/OUT: where to put the input */
);

Part 2: Simplified Buffer prototypes

static Buffer *bf_alloc(/* RET: an initialized, empty buffer */
    int size,       /* IN: initial maxsize to allocate */
    int incr        /* IN: increment by which to grow maxsize */
);
static void bf_addch(
    Buffer *buf,    /* IN/OUT: an initialized buffer */
    char ch         /* IN: the character to append to the buffer */
);
static void bf_addstr(
    Buffer *buf,    /* IN/OUT: an initialized buffer */
    char *str       /* IN: string to add to end of buffer */
);
static void bf_clear(
    Buffer *buf     /* IN/OUT: an initialized buffer */
);
static void bf_valid(
    Buffer *buf     /* IN: an initialized buffer */
);
static void bf_free(
    Buffer *buf     /* IN: an initialized buffer */
);

Part 3: The my_open() function specification

typedef enum { FALSE, TRUE } Boolean;
typedef enum MyOpenMode {
    MY_READONLY,    /* open for reading */
    MY_WRITE,       /* open for overwriting */
    MY_APPEND       /* open for appending */
} MyOpenMode;
static FILE *my_open(  /* RET: ptr to open file stream or NULL */
    Buffer *fbuf,      /* IN/OUT: file name to open (may be empty) */
    char *prompt,      /* IN: NULL or prompt to use if stdin is a tty */
    MyOpenMode mode,   /* IN: mode to open file: read/write/append */
    Boolean stripwhite /* IN: TRUE: strip leading and trailing whtsp */
);

Here are the steps in what my_open() must do:

  1. If the passed Buffer data structure argument fbuf contains no data (it is an empty buffer, cursize <= 1), we must try to fill it with a file name entered by the user:
  2. At this point in the function, the fbuf Buffer has either the file name that was passed in with the function call, or it has a file name (possibly empty) collected by the my_prompt() function in Step 1.
  3. If stripwhite is TRUE, strip leading and trailing white space from the line (the file name) in the fbuf Buffer.
  4. If the fbuf Buffer is now empty, indicating that the user entered no file name, return NULL.
  5. If the fbuf Buffer contains simply a dash ("-") as the file name, return the current standard input file pointer, if the mode indicates that the file is to be opened for input, or the current standard output file pointer, if the mode indicates that the file is to be opened for output.   No files need to be opened.
  6. At this point in the function, the fbuf Buffer must contain a non-empty, non-dash file name.  Try to open the file using contents of the fbuf Buffer as the file name.  You may find the utility function fopenmode() (given below) helpful to select the correct second argument to fopen().  Report any failure in opening the file on standard error using the C Library functions perror() or strerror() and return NULL.  (You may use eprintf() to notify the user of the failure.)
  7. Return the opened file pointer on success.

Part 4: Some utility functions

#define MAX(a,b) ((a)>(b) ? (a) : (b))

/* Append the characters in the str to the Buffer.
 * The NUL at the end is not copied; only the characters are.
 * The buffer grows by the larger of the string length and "incr".
 */
        static void
bf_addstr(
    Buffer *buf,   /* IN/OUT: an initialized buffer */
    char *str      /* IN: string to add to end of buffer */
){
    int len;

    bf_valid(buf);

    if( str == NULL || str[0] == '\0' ){
        eprintf("bf_addstr: Ignoring null pointer or string");
        return;
    }

    len = strlen(str);
    if( (buf->cursize + len) > buf->maxsize ){
        len = MAX(buf->incr, len);
        buf->buf = (char *)myrealloc(
            buf->buf, buf->maxsize += len);
    }
    strncpy(buf->buf + buf->cursize, str, len);
    buf->cursize += len;
}

 

/* Returns an English description of the given mode.
 */
    static char *
modestr(            /* RET: English for the I/O operation */
    MyOpenMode mode /* IN: mode of I/O operation */
){
    switch( mode ){
    case MY_READONLY: return "reading";
    case MY_WRITE:    return "writing";
    case MY_APPEND:   return "appending";
    default:
        eprintf("modestr: Unknown mode %d", mode);
        abort();
        /*NOTREACHED*/
    }
}
/* Returns the appropriate mode string suitable for fopen().
 */
    static char *
fopenmode(            /* RET: fopen() string for the I/O operation */
    MyOpenMode mode   /* IN: mode of I/O operation */
){
    switch( mode ){
    case MY_READONLY: return "r";
    case MY_WRITE:    return "w";
    case MY_APPEND:   return "a";
    default:
        eprintf("modestr: Unknown mode %d", mode);
        abort();
        /*NOTREACHED*/
    }
}

Part 5: The driver: MAIN

static char *cmdname = "UNKNOWN";

    int
main(int argc, char **argv) {
    FILE *fd;           /* descriptor of input stream */
    Buffer *lbuf;       /* input line buffer */
    Buffer *fbuf;       /* file name buffer */

    cmdname = argv[0];  /* save command name for eprintf use */

    /* Allocate a File Name Buffer.
     * Loop: Prompt for an input file and try to open it.
     * If the open fails and there was a file name given, ask again.
     * With no file name, simply say goodbye and exit.
     */
    fbuf = bf_alloc(FNAME_INIT, FNAME_INCR);

    do {
        bf_clear(fbuf);
        fd = my_open(fbuf, NULL, MY_READONLY, TRUE);
    } while ( fd == NULL && fbuf->buf[0] != '\0' );

    if( fd == NULL ){
        eprintf("No file name.  Goodbye.");
        return RET_SUCCESS;
    }

    /* Allocate an Input Line Buffer.
     * Loop: Fill the Buffer with one line at a time read
     * from the fd and print it.
     */
    lbuf = bf_alloc(INLINE_INIT, INLINE_INCR);

    do {
        bf_clear(lbuf);
        my_prompt(fd, "Enter a string: ", lbuf);
        printf("String is '%s', size %d\n",
            lbuf->buf, lbuf->cursize);
    } while( ! EQ(lbuf->buf,"done") && ! feof(fd) );

    printf("Buffer maximum size grew to %d characters\n",
        lbuf->maxsize);

    /* Detect I/O errors on output stream without closing it */
    if( fflush(stdout) == EOF ){
        extern int errno;
        eprintf("Unable to flush stdout: %s", strerror(errno));
    }

    /* Free the Buffers */
    bf_free(lbuf);
    bf_free(fbuf);

    return RET_SUCCESS;
}

Ian D. Allen CST8152 Home Page

This page last updated: Sunday September 27, 1998 01:07