# /* * coobld * */ /*)BUILD */ #ifdef DOCUMENTATION title coobld Build Cookie Data File index Build cookie data file synopsis coobld [options] file_list description Build the internal data file for fortune cookies. If no file argument is given, the file "cookie.txt" will be looked for on the current account. The file_list may contain wild-cards. .s The following options are defined: .lm +8 .s.i -8;-d Debug .s.i -8;-v Verify (print each cookie line as it is read) Cookie File Format Cookie text files have the following format: .nf Each cookie is terminated by a line containing "%%" in column 1 and 2. %% By default, a cookie that starts with one or more spaces is considered to be a "poem" and will not be reformatted. %% To attribute a cookie. terminate it by terminating the sentence with a '.', '?', or '!' then append the author's name as shown. -- Author's name. It is recommended that author's names start in column 48 where possible. Note that the cookie may continue, which allows adding footnotes or rebuttals. %% .s.f Cookies are stored on a disk file in a format compatible with the limited amount of random access capabilities available on "standard" C libraries. The format is, of course, called "Cookie access method." The output file is written by fwrite() and must be read by fread(). .s The cookie file has the following format: .s.nf Record 1: long ncookie; /* number of cookies */ int nindex; /* Index table size */ int subindex; /* How many subindexes */ int sindex; /* sizeof index table */ char date[26]; /* When file created */ .s Record 2: long main_index[tabdim]; /* Seek location for */ /* each subindex entry */ .s Record 3 .. tabdim+2 long sub_index[tabdim]; /* Cookie seek address */ .s Cookie records have just plain text, one space between words. Each cookie is terminated by a line containing "%%". .s Thus, to find cookie N, proceed as follows: .s.nf Read record 1. Allocate space for index[]. Read record 2 into index. .s Read index[N / ncookies] into index. Read from index[N % ncookies]. .s.f Note: if the maximum cookie contains 2047 bytes, the index table may be dimensioned (2047 / sizeof (long)) (== 256) and the maximum number of cookies equals 256 * 256 == 65536. If this is done, and only one cookie is to be read, only one buffer is needed, defined as: .s.nf union { char text[TEXTSIZE]; long index[TEXTSIZE / sizeof (long)]; } cookie; author Martin Minow bugs probably. #endif #include #define TRUE 1 #define FALSE 0 #define EOS 0 #ifdef rsx #define R_MODE "run" #define W_MODE "wun" #else #ifdef rt11 #define R_MODE "rn" #define W_MODE "wn" #else #define R_MODE "r" #define W_MODE "w" #endif #endif #define SIGNAL '%' extern long ftell(); FILE *indexfp; /* Indexes stored here */ FILE *dummyfp; /* Dummy output file */ FILE *outfp; /* Output file (cookie.fil) */ #define INPUT_FILE "cookie.txt" /* * The following may need work on RT11 to insert file sizes */ char *text_file = "ctext.tmp"; char *dummy_file = "cdummy.tmp"; char *index_file = "cindex.tmp"; char *cookie_file = "cookie.fil"; struct header { long ncookie; /* Number of cookies */ int nindex; /* Dimension of top_index[] */ int subindex; /* Number of subindex entries */ int sindex; /* Sizeof index for alloc */ char date[28]; /* Date cookie file built */ } header; char text[513]; /* Working text */ long *sub_index; /* Indexes stored here */ long *top_index; /* Top level indexes go here */ long firstindex; /* -> top index in indexfp */ long firstcookie; /* -> first cookie in dummyfp */ int verbose = 0; int debug = 0; main(argc, argv) int argc; char *argv[]; { register char *ap; register int i; int nfiles; FILE *file_open(); nfiles = 0; for (i = 1; i < argc; i++) { ap = argv[i]; if (*ap++ == '-') { argv[i] = NULL; for (; *ap != EOS; ap++) { switch (tolower(*ap)) { case 'd': debug++; break; case 'v': verbose++; break; default: fprintf(stderr, "?Unknown option '%c'\n", *ap); } } } else { nfiles++; } } /* * Copy raw cookies to a temp file. Collect how many and * dimension of indexes. */ if (nfiles == 0) { argc = 2; argv[1] = INPUT_FILE; } maketext(argc, argv); /* * Build dummy cookie file */ dummyfp = file_open(dummy_file, W_MODE); indexfp = file_open(index_file, W_MODE); makedummy(argc, argv); fclose(dummyfp); fclose(indexfp); /* * Build real cookie file */ outfp = file_open(cookie_file, W_MODE); dummyfp = file_open(dummy_file, R_MODE); indexfp = file_open(index_file, R_MODE); makecookie(); fclose(outfp); cdelete(dummyfp); cdelete(indexfp); } cdelete(fd) FILE *fd; /* * Close or delete the file */ { char work[40]; if (debug) fclose(fd); else { fgetname(fd, work); fclose(fd); delete(work); } } FILE * file_open(filename, mode) char *filename; char *mode; /* * Open the file, die if failure */ { register FILE *fd; if ((fd = fopen(filename, mode)) == NULL) { perror(filename); error("?COOBLD-F-Can't %s \"%s\"\n", (*mode == 'w') ? "create" : "open", filename); } return(fd); } maketext(argc, argv) int argc; char *argv[]; /* * Read files obtaining the counts. * * Header is set as follows: * * header.ncookie Number of cookies (long) * header.nindex Index dimension * header.subindex Number of subindex entries * header.sindex Sizeof index[] * header.date ctime() */ { register int len; long subsquare; char *cpystr(); register int bigbytes; /* Longest record bytes */ int tvec[2]; FILE *infd; register int nfiles; int i; int nrecords; bigbytes = 0; time(&tvec); cpystr(&header.date, ctime(&tvec)); header.ncookie = 0; header.nindex = 0; subsquare = 0; len = 0; /* * Read all cookies to count them and get the max. length. */ for (i = 1; i < argc; i++) { if (argv[i] == NULL) continue; if ((infd = fwild(argv[i], "r")) == NULL) { perror(argv[i]); continue; } for (nfiles = 0; fnext(infd); nfiles++) { fgetname(infd, text); printf("%s:", text); nrecords = 0; while (!feof(infd)) { fgets(text, sizeof text, infd); nrecords++; if (feof(infd) || (text[0] == SIGNAL && text[1] == SIGNAL)) { if (len == 0) continue; if (len > bigbytes) bigbytes = len; len = 0; header.ncookie++; if (subsquare < header.ncookie) { header.nindex++; subsquare = header.nindex * header.nindex; } } else { len += strlen(text) + 2; } } printf("\t%d\n", nrecords); } if (nfiles == 0) { printf("No files match \"%s\"\n", argv[i]); } } header.subindex = (header.ncookie + header.nindex - 1) / header.nindex; header.sindex = header.nindex * sizeof (long); printf("%ld cookies read, the longest has %d bytes\n", header.ncookie, bigbytes); printf("top index = %d, sub index = %d, index area size = %d\n", header.nindex, header.subindex, header.sindex); } makedummy(argc, argv) int argc; char *argv[]; /* * Build a dummy cookie file in two separate files: * dummyfp Gets the cookie data * indexfp Gets the indices. * * This way, we don't have to reposition the file, nor do we * have to read and write the same file. */ { register int subi; /* Index into sub_index[] */ register int topi; /* Index into top_index[] */ register int len; /* Input record length */ int i; FILE *infd; long count; long dummy_loc; count = 0; if ((top_index = calloc(header.sindex, 1)) == NULL || (sub_index = calloc(header.sindex, 1)) == NULL) error("Can't allocate index buffers -- %d bytes\n", header.sindex); put(&header, sizeof header, dummyfp, "dummy header"); put(&header, sizeof header, indexfp, "index header"); put(sub_index, header.sindex, dummyfp, "dummy top index"); put(sub_index, header.sindex, indexfp, "index top index"); firstindex = ftell(indexfp); for (subi = header.subindex; --subi >= 0;) { put(sub_index, header.sindex, dummyfp, "dummy sub index"); } dummy_loc = firstcookie = ftell(dummyfp); subi = 0; topi = 0; len = 0; for (i = 1; i < argc; i++) { if (argv[i] == NULL) continue; if ((infd = fwild(argv[i], "r")) == NULL) continue; while (fnext(infd) != NULL) { while (!feof(infd)) { if (fgets(text, sizeof text, infd) == NULL || (text[0] == '%' && text[1] == '%')) { if (len == 0) continue; len = 0; count++; if (subi >= header.nindex) { if (topi >= header.nindex) { error("Too many cookies, max is %d ** 2\n", header.nindex); } top_index[topi] = ftell(indexfp); topi++; put(sub_index, header.sindex, indexfp, "sub index"); subi = 0; } sub_index[subi] = dummy_loc; subi++; #ifdef rsx fput("%%\n", 4, dummyfp); #else fputs("%%\n", dummyfp); #endif dummy_loc = ftell(dummyfp); } else { #ifdef rsx fput(text, strlen(text) + 1, dummyfp); #else fputs(text, dummyfp); #endif len += strlen(text); } if (ferror(dummyfp)) { perror("writing text to temp file"); error("Output error"); } } } } /* * Put the last subindex record */ while (subi < header.nindex) { sub_index[subi] = -1; subi++; } top_index[topi] = ftell(indexfp); topi++; put(sub_index, header.sindex, indexfp, "last sub index"); while (topi < header.nindex) { top_index[topi] = -1; topi++; } printf("Work files built, %ld cookies, %d index levels\n", count, header.nindex); if (count != header.ncookie) error("Expected %ld cookies, read %ld\n", header.ncookie, count); } makecookie() /* * Write outfp with cookie file, using * * indexfp Index file (has sub-indexes) * dummyfp Cookie work file * */ { register int i; register int bytect; long itemct; put(&header, sizeof header, outfp, "cookie header"); put(top_index, header.sindex, outfp, "cookie top index"); itemct = 0; if (fseek(indexfp, firstindex, 0) != 0) error("Can't seek to %ld on index file\n", firstindex); if (fseek(dummyfp, firstcookie, 0) != 0) error("Can't seek to %ld on cookie file\n", firstcookie); for (i = 0; i < header.subindex; i++) { get(sub_index, header.sindex, indexfp, "sub index"); put(sub_index, header.sindex, outfp, "cookie sub index"); } printf("%d index records written\n", header.nindex + 1); #ifdef rsx while (fget(text, sizeof text, dummyfp), !feof(dummyfp)) { fput(text, strlen(text) + 1, outfp); #else while (fgets(text, sizeof text, dummyfp) != NULL) { fputs(text, outfp); #endif } if (ferror(outfp)) { perror("writing output"); error("Output error"); } } /* * Raw I/O routines */ get(whereto, size, fd, why) char *whereto; /* Where to read to */ int size; /* Buffer size */ FILE *fd; /* Input file descriptor */ char *why; /* Who is reading for error */ /* * Read into the buffer. Return the number of bytes read. * All errors are fatal. */ { register int i; #ifdef rsx if ((i = fget(whereto, size, fd)) != size || ferror(fd)) { perror("coobld fget error"); error("Reading %s, expected %d bytes, read %d bytes\n", why, size, i); } #else if ((i = fread(whereto, size, 1, fd)) != 1 || ferror(fd)) { perror("coobld fread error"); error("Reading %s, expected 1 item, %d bytes, read %d items\n", why, size, i); } #endif } put(wherefrom, size, fd, why) char *wherefrom; /* Where to write from */ int size; /* Number of bytes to write */ FILE *fd; /* Output file descriptor */ char *why; /* Who is writeing for error */ /* * Write from the buffer. All errors are fatal. */ { register int i; #ifdef rsx if ((i = fput(wherefrom, size, fd)) != size || ferror(fd)) { perror("coobld fput error"); error("Error writing %d bytes to %s, %d bytes written\n", size, why, i); } #else if ((i = fwrite(wherefrom, size, 1, fd)) != 1 || ferror(fd)) { perror("coobld fwrite error"); error("Error writing 1 item of %d bytes to %s, %d items\n", size, why, i); } #endif } /* * For debugging only */ dump(indextable, why) long indextable[]; char *why; { register int i; printf("\n%s\n", why); for (i = 0; i < header.nindex; i++) printf("%3d %ld\n", i, indextable[i]); }