/* * 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, [email protected] */ #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); }
Department of Computer Science
University of California at Davis
Davis, CA 95616-8562