Topic: File operations.
This page last updated: Sunday September 27, 1998 01:07This 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.
- 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.
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 */ );
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 */ );
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:
#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*/ } }
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; }