Lecture #8: Standard I/O Library

Reading: none
Handouts: Print (Pseudo)random Saying
  1. Greetings and felicitations
  2. Structure: a portable interface to the underlying system calls
    1. how it represents files (streams with an offset); FILE *
    2. stdin, stdout, stderr
  3. Opening and Closing
    1. fopen("zyx", "r"); mode is r (read), w (write), a (append), r+ (read and write, file offset is 0), w+ (read and write, truncate file if it exists), a+ (w+ but append and not truncate); note some systems have "b" appended as binary; why?
    2. freopen("file", "r", stdin); now reads from stdin really read from file
    3. fclose(fp); close file pointer
  4. Reading and Writing Lines
    1. formatted output: printf, fprintf, sprintf (cover d, x, o, f, e, g, c, s)
    2. formatted input: scanf, sscanf, fscanf (cover d, x, o, f, [], c, s)
    3. fgets, gets, fputs, puts (remember: f keeps newline or adds it)
  5. Reading and Writing Characters
    1. getc, getchar, fgetc
    2. putc, putchar, fputc
    3. ungetc
  6. Reading and Writing Blocks
    1. fread(ptr, size, number, fp), fwrite(ptr, size, number, fp)
  7. Random I/O
    1. fseek(fp, offset, whence); rewind(fp) same as fseek(fp, 0L, SEEK_SET); relative positioning OK; need errno to find error
    2. ftell(fp)
  8. Go through randmsg

Print (Pseudo)random Saying

/*
 * PRS -- generate a pseudorandom saying
 *
 * use: prs file		--- print saying chosen at random from file
 *      prs -b file	--- put file into the right format
 *			   	    (file is a sequence of sayings
 *			   	    separated by a single line ENDSAYING;
 *			   	    only first MAXSAYING sayings are used
 *	prs -d file	--- print out debugging information about
 *			   	    file
 * author: Matt Bishop, bishop@cs.ucdavis.edu
 */
#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>

/*
 * file format
 * <the sayings, separated with a line containing only "---">
 * <the index table, each long being the offset from the
 *  beginning of the file; all entries unsigned long>
 * <the number of entries in the index table, unsigned long>
 * <the magic number MAGIC, unsigned long>
 *
 * beginning of file --->		text saying #1
 *			   		---
 *					text saying #2
 *					<0 as an unsigned long>
 *					<20 as an unsigned long>
 *					<2 as an unsigned long>
 *					<35 as an unsigned long>
 * end of file --------->		<MAGIC as an unsigned long>
 */
#define MAGIC 0x01030289	/* the magic number */
unsigned long magic = MAGIC;	/* variable for magic */
unsigned long npos;	/* size of index table */
unsigned long tab;		/* position of index table */
char *progname = "prs";	/* program name */

/*
 * macros and such
 */
#define ENDSAYING	"---\n"		/* end of saying; MUST end in \n */
#define MAXSAYING	1024		/* max number of sayings per file */
#define MAXLINE	1024		/* max line length */
#define DEBUG_COLS	5		/* this many offsets/col for debug */
#define DEFFILE	"/home/ecs40r/lib/sayings" /* default file */
	
/*
 * types and such
 */
enum eact {
	MAKE,		/* create a randsay file */
	DEBUG,	/* print out a randsay file in full */
	SHOW,		/* show a saying */
};
extern int optind;	/* next arg index to be processed */
				/* usage message */
#define USAGE	fprintf(stderr, "prs [ -b | -d ] [ file ]\n")

/*
 * unprototyped system functions (sigh)
 */
int gettimeofday(struct timeval *, struct timezone *);
int getopt(int, char **, char *);
void srandom(int);
long random(void);

/*
 * generate random saying file (see above for format)
 * arguments:	FILE *fp	points to saying file
 * actions: generates a sayings file for this program
 * errors: * if it can't write out the binary data
 * 	      * too many sayings -- only first MAXSAYING used
 */
void makefile(FILE *fp)
{
	unsigned long pos[MAXSAYING];	/* array of indices */
	char saying[MAXLINE];		/* saying */
	register int i;			/* counter in a for loop */

	/*
	 * Process the file, recording  offset into the
	 * position array
	 */
	for(npos = 0; npos < MAXSAYING && !feof(fp); npos++){
		/* get saying position */
		pos[npos] = ftell(fp);
		/* skip over the saying */
		i = 0;
		while (fgets(saying, MAXLINE, fp) != NULL &&
				strcmp(ENDSAYING, saying) != 0)
			i++;
		/* ignore empty sayings */
		if (i == 0)
			npos--;
	}
	/*
	 * array will be appended to the file
	 */
	errno = 0;
	if (fseek(fp, 0L, SEEK_END) != -1 && errno != 0){
		fprintf(stderr, "Can't find end of file.\n");
		return;
	}
	tab = ftell(fp);
	/*
	 * now write out the index table, size, index
	 * table location, and magic number
	 */
	if (fwrite(pos, sizeof(unsigned long), npos, fp) != npos ||
			fwrite(&npos, sizeof(npos), 1, fp) != 1 ||
			fwrite(&tab, sizeof(tab), 1, fp) != 1 ||
			fwrite(&magic, sizeof(magic), 1, fp) != 1){
		fprintf(stderr, "Could not write index.\n");
		return;
	}
}


/*
 * print debugging information
 * arguments:	FILE *fp	points to saying file
 *			char *fname	file name
 * actions: prints the sayings plus positional information
 * errors: * barfs if magic number wrong or missing
 *	      * barfs if can't find the table, index, or
 *		   the string
 */
void debugfile(FILE *fp, char *fname)
{
	unsigned long pos;	/* current position in file */
	char saying[MAXLINE];
	int i, k;

	/*
	 * print the magic number, index table,
	 * and index table size
	 */
	errno = 0;
	if ((fseek(fp, -(sizeof(magic)+sizeof(npos)+sizeof(tab)),
			SEEK_END) == -1 && errno != 0) ||
			fread(&npos, sizeof(npos), 1, fp) != 1 ||
			fread(&tab, sizeof(tab), 1, fp) != 1 ||
			fread(&magic, sizeof(magic), 1, fp) != 1){
		perror(fname);
		return;
	}
	printf("magic humber 0x%08lx\ntable at 0x%08lx, %ld entr%s\n",
		magic, tab, npos, npos == 1 ? "y" : "ies");
	/*
	 * now print out the index table entries, 5 per line
	 */
	errno = 0;
	if (fseek(fp, tab, SEEK_SET) == -1 && errno != 0){
		perror(fname);
		return;
	}
	for(i = k = 0; i < npos; i++){
		/* read the index entry; error if you fail */
		if (fread(&pos, sizeof(unsigned long), 1, fp) != 1){
			printf(" ********");
			perror(fname);
			continue;
		}
		/* print it out */
		printf(" 0x%08lx", pos);
		/* newline if appropriate */
		if (k == DEBUG_COLS){
			putchar('\n');
			k = 0;
		}
		else
			k++;
	}
	/*
	 * print the sayings, led by offsets and without terminators
	 */
	if (i != 0)
		putchar('\n');
	(void) rewind(fp);
	while(!feof(fp) && (pos = ftell(fp)) < tab){
		/* print offset */
		printf("offset 0x%08lx:\n", pos);
		/* print saying */
		while(fgets(saying, MAXLINE, fp) != NULL &&
				strcmp(ENDSAYING, saying) != 0)
			printf("   %s", saying);
	}
}

/*
 * print message
 * arguments:	FILE *fp	points to saying file
 *			int n		number to compute  index from
 * actions: prints the saying
 * errors: * barfs if magic number wrong or missing
 *	      * barfs if can't find the table, index, or
 *		   the string
 */
void sayprint(FILE *fp, int n)
{
	char saying[MAXLINE];		/* buffer for saying */

	/*
	 * get magic number
	 */
	errno = 0;
	if ((fseek(fp, -sizeof(magic), SEEK_END) == -1 && errno != 0) ||
				fread(&magic, sizeof(magic), 1, fp) != 1){
		fprintf(stderr, "Can't read magic number.\n");
		return;
	}
	/*
	 * is the magic number right?
	 */
	if (magic != MAGIC){
		fprintf(stderr, "Magic number wrong.\n");
		return;
	}
	/*
	 * get the size and address (offset) of the table
	 * and position pointer at this place
	 */
	errno = 0;
	if ((fseek(fp, -(sizeof(magic)+sizeof(npos)+sizeof(tab)),
			SEEK_END) == -1 && errno != 0) ||
			fread(&npos, sizeof(npos), 1, fp) != 1 ||
			fread(&tab, sizeof(tab), 1, fp) != 1){
		fprintf(stderr, "Can't find index or index size\n");
		return;
	}
	/*
	 * get the address of the saying to print
	 */
	errno = 0;
	if ((fseek(fp, tab+ (n % npos) * sizeof(unsigned long),
			SEEK_SET) == -1 && errno != 0) ||
			fread(&tab, sizeof(tab), 1, fp) != 1){
		fprintf(stderr, "Can't find address of saying\n");
		return;
	}
	/*
	 * get the saying itself and print it (phew!)
	 */
	errno = 0;
	if (fseek(fp, tab, SEEK_SET) == -1 && errno != 0){
		fprintf(stderr, "Can't get to saying\n");
		return;
	}
	while(fgets(saying, MAXLINE, fp) != NULL &&
				strcmp(saying, "---\n") != 0)
		printf("%s", saying);
}
	
/*
 * seeds the random number generator
 * arguments:	none
 * actions: uses microseconds, PID, and parent PID to
 *	       seed the system's pseudorandom number generator
 * errors: * can't get time of day (should never happen)
 */
void seedrng(void)
{
	struct timeval tod;	/* use time of day (usec) */
	struct timezone tz;	/* time zone (unused) */

	/* begin with microseconds */
	if (gettimeofday(&tod, &tz) < 0)
		perror("gettimeofday");
	/* mix with PID and parent PID */
	tod.tv_usec ^= ((getpid()<<16)|getppid());
	srandom((int) tod.tv_usec);
}

/*
 * the main routine
 */
int main(int argc, char *argv[])
{
	register FILE *fp;	   /* ptr to input/saying file */
	register int c;		   /* the option */
	enum eact action = SHOW;  /* what to do */

	/*
	 * set the program name
	 */
	progname = argv[0];

	/*
	 * do the options
	 */
	while((c = getopt(argc, argv, "bd")) != EOF){
		switch(c){
		case 'b':		/* build */
			action = MAKE;
			break;
		case 'd':		/* debug */
			action = DEBUG;
			break;
		case '?':		/* huh? */
			USAGE;
			return(0);
		}
	}

	/*
	 * everything from here on requires
	 * a file, so open it
	 */
	if (optind == argc)
		/* no file named -- use default */
		argv[optind] = DEFFILE;
	if ((fp = fopen(argv[optind], "r+")) == NULL){
		perror(argv[optind]);
		return(1);
	}

	/*
	 * now do it!
	 */
	switch(action){
	case MAKE:			/* make the file */
		makefile(fp);
		break;
	case DEBUG:			/* print debugging version */
		debugfile(fp, argv[optind]);
		break;
	case SHOW:			/* print message */
		/* generate random number and print it */
		seedrng();
		sayprint(fp, (int) random());
		break;
	default:			/* ??? */
		fprintf(stderr, "Internal error: action ILLEGAL (%d)\n",
					action);
		break;
	}

	/*
	 * bye!
	 */
	return(0);
}

You can also see this document as an RTF document, a Postscript document, or a plain ASCII text document.
Send email to cs40@csif.cs.ucdavis.edu.

Department of Computer Science
University of California at Davis
Davis, CA 95616-8562



Page last modified on 11/11/97