good@pixar (Life is hard. And then you drive. -- Ferrari billboard) (08/25/86)
Here it is, the program that lets me choose all those goofy $NAMEs that show up after my name. It's also good for generating poetry, rumors, lightbulb jokes, and even the names of high-tech companies. Enjoy. #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # ncode.c # ncode.1 # ncode.sample # This archive created: Sun Aug 24 23:44:55 1986 # By: pixar!good () export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \SHAR_EOF > 'README' You should now have README, ncode.c, ncode.1 and ncode.sample. This source is known to compile and run on 4.2 BSD. With the possible exception of the calls to srandom() and random() I suspect the rest is very portable. Compile it "cc ncode.c -O -o ncode" and then try ncode ncode.sample For more fun, try ncode -n 10 ncode.sample And for a good education say ncode -g "I work for CODE\" Then you might want to read the man page, ncode.1, to learn about how it works. You can make it pretty with "nroff -man ncode.1". If all of this leaves you hopelessly confused, send mail to {sun,ucbvax}!pixar!good and I'll send you more confusing sample files. If I feel like it. --Craig ...{ucbvax,sun}!pixar!good SHAR_EOF fi if test -f 'ncode.c' then echo shar: "will not over-write existing file 'ncode.c'" else cat << \SHAR_EOF > 'ncode.c' /* * ncode.c -- a random text constructor * pixar!good * based on a story by pixar!mark */ #include <stdio.h> #define DATALEN 4096 /* max number of lines allowed in input file */ #define BUFSIZ 2048 /* max number of chars allowed in a line */ char *data[DATALEN]; /* array of pointers to strings from file, one line each */ char *malloc(); char *rindex(); long random(); struct gstruct { char *name; /* points to name of a group in data[] */ int count; /* how many elements of data[] belong to this group */ int index; /* index of element of data[] where group starts */ }; struct gstruct groups[DATALEN]; int ngroups; /* number of elements in groups[] */ int findex; /* number of elements loaded into data[] */ main (ac,av) int ac; char *av[]; { char *prog, *fname = 0; int howmany = 1; /* times through main loop */ char *groupname = "CODE"; prog = rindex(*av,'/'); /* name of program */ prog = ( prog == NULL ) ? *av : ++prog ; ac--;av++; while(ac && **av == '-'){ if (strcmp(*av,"-n") == 0){ ac--;av++; howmany = atoi(*av); if (howmany <= 0){ fprintf(stderr, "%s: -n: need positive integer\n",prog); exit(1); } ac--;av++; } else if (strcmp(*av,"-g") == 0){ ac--;av++; groupname = *av; /* use instead of "CODE" */ if (! groupname ){ fprintf(stderr, "%s: -g: need group name\n",prog); exit(1); } ac--;av++; } else { printf( "Usage %s [-n n ] [-g groupname] codefile\n",prog); exit(0); } } if (!ac){ fprintf(stderr, "Usage %s [-n n ] [-g groupname] codefile\n",prog); exit(1); } fname = *av; findex = 0; if( init(fname) != 0 ){ fprintf(stderr,"%s: init error\n",prog); exit(1); } if ( scan() != 0 ){ fprintf(stderr,"%s: scan error\n",prog); exit(1); } srandom(getpid()); /* seed the number generator */ while ( howmany ){ expand(groupname,strlen(groupname)); howmany--; } } init(fname) char *fname; { FILE *fp; char buf[BUFSIZ]; char *s; /* * Read whole file into data[] */ fp = fopen(fname,"r"); if ( fp == NULL ) { perror(fname); return 1; } while ( fgets(buf,BUFSIZ,fp) != NULL ){ if ( findex > DATALEN ){ fprintf(stderr,"init: findex reached %d\n",findex); return 1; /* no point in going on */ } for(s=buf;*s!='\0';s++) /* nuke newline */ if(*s=='\n') *s='\0'; if( buf[0] == '#' && (strncmp(buf,"#include",8)==0) ){ for(s= &buf[8]; *s!='\0' && (*s==' '||*s=='\t');s++) ; /* skipping white space */ if (init(s) != 0){ /* expects a path name */ return 1; } continue; /* don't put in the #include line! */ } data[findex]=(char *)malloc(strlen(buf)+1); if (data[findex] != NULL) strcpy(data[findex], buf); else { printf("init: bad malloc\n"); return 1; /* something went wrong with malloc */ } findex++; } fclose(fp); return 0; } /* * Scan data[] marking and counting groups */ scan() { register i, gcnt, gindex; /* special case: first line always a group name */ groups[0].name = data[0]; groups[0].index = 0; ngroups = 1; i = 1; gindex = 0; gcnt = 0; while ( i < findex ){ if ( data[i][0] == '%' ){ groups[gindex].count = gcnt; gcnt = 0; /* close out prev group */ ngroups++; i++; /* start next group */ gindex++; groups[gindex].name = data[i]; groups[gindex].index = i; }else{ gcnt++; } i++; } return 0; } expand(s,lim) char s[]; int lim; { register i, j, k, done, n, r; i = j = 0; while ( s[i] != 0 && i < lim ){ done = 0; while ( ! done && j <= lim ){ if ( isawhite(s[j]) ){ /* chase down remaining white space */ for (k=j; k<=lim && s[k] && isawhite(s[k]);k++){ ; } n = isagroup(&s[i], j-i); if ( n >= 0 ){ r = (groups[n].index + 1 + rnd(groups[n].count)); expand( data[r], strlen(data[r])); outstring(&s[j], k-j); } else { outstring(&s[i], k-i); } done++; i = j = k; /* should be on next word, if any */ } j++; } } } /* * Return index into groups[] array if a group name, -1 if just a word */ isagroup(s,lim) char s[]; int lim; { register i; static char gbuf[BUFSIZ]; strncpy(gbuf,s,lim); gbuf[lim] = '\0'; /* strncpy might not do this */ for(i=0; i<=ngroups; i++ ){ if (groups[i].name && strcmp(gbuf,groups[i].name) == 0){ return i; /* hit */ } } return -1; /* fail */ } /* * Output string, handling splices */ outstring(s,lim) char s[]; int lim; { register i = 0; while ( s[i] != '\0' && i < lim ){ switch (s[i]){ case '|': break; /* splice: no output */ case '\\': putchar('\n'); break; default: putchar(s[i]); break; } i++; } } /* * Return random number 0 to limit */ rnd(limit) int limit; { int i; if (limit > 0){ return (random() % limit); } return 0; /* better than a floating exception if lim == 0 */ } /* * Return 1 if one of our "white" characters. A white character is * any character which can bound a group name, so punctuation marks * are included. */ isawhite(c) char c; { if ( c == '\0' || /* traditional white space */ c == ' ' || c == '\t' || c == '|' || /* "splice" character */ c == '\\' || /* becomes a newline */ c == '.' || /* common punctuation */ c == '-' || c == ':' || c == ';' || c == ',' || c == '!' || c == '?' || c == '[' || c == ']' || c == '{' || c == '}' || c == '(' || c == ')' || c == '\'' || c == '\"' || c == '`' ) return 1; return 0; } SHAR_EOF fi if test -f 'ncode.1' then echo shar: "will not over-write existing file 'ncode.1'" else cat << \SHAR_EOF > 'ncode.1' .TH NCODE 1 "Pixar" .SH NAME ncode - stochastic text construction .SH SYNOPSIS .B ncode [-n number] [-g groupname] codefile .SH DESCRIPTION .I Ncode reads in a file of a certain format and randomly constructs text based on the organization of the file. Other files may be recursively included by putting #include pathname on any line of the file. This is useful when you want to use a file of basic definitions, or groups, in different configurations. The -n flag is used to run the program through the main loop multiple times. A "group" name is defined as the word on the first line of the file and, more commonly, the word on each line following a line starting with "%". The members of a group are all lines between the group name and the next "%". When a group name is encountered, surrounded by any of a set of characters called "white space" in this context, it is randomly expanded into one of its members. Group names are not allowed to contain any white space, to prevent terminal confusion on the part of the program. The -g flag allows you to start the expanding process on a group name other than the default, which is "CODE". The argument may be a group name, or an entire string including group names, just as if it were a line in the file. It is legal to start on any group in the file, and groups may be referenced before or after the place in the file where they are defined. In the case of duplicate group definitions, the first one occurring is the only one used. For example, here is a sample group definition: .nf NOUN lamp house car % .fi The line "See the NOUN." could be randomly expanded to say "See the lamp." The characters considered "white" for the purpose of bounding a group name, besides what is normally considered white space, are currently: | \\ . - : ; , ! ? [ ] { } () ' " ` Two of those characters have special meanings to .I ncode. The "|" symbol allows you to "splice" things to a group name. When it is encountered, no character is printed on output. The "\\" causes a newline to be printed on output. The simplest application would be for a "fortune" program, but .I ncode could also be used for more complex things such as a rumor generating file. The group definitions will be left as an exercise for the reader, but the following example should prove illuminating: .nf CODE It was rumored today that COMPANY will be bought by COMPANY for PRICE\\. PERSON, POSITION of COMPANY, said that PRODUCT will be announced DATE\\. .fi Note that every string to be expanded must be on only one line of the file. Very long lines, currently to 2048 characters, are allowed. The maximum number of total lines in the file and all #include files is currently 4096. If you want to randomly generate the Great American Novel in one pass you'll have to change two #define lines in the source. .SH BUGS No bugs. Only features that you haven't figured out how to use yet. .SH DIAGNOSTICS Standard perror() stuff. Pretty self explanatory. A bogus input file might benignly yield cryptic results. .SH AUTHOR Craig Good SHAR_EOF fi if test -f 'ncode.sample' then echo shar: "will not over-write existing file 'ncode.sample'" else cat << \SHAR_EOF > 'ncode.sample' PRE Zy Cryo Iso Isa Vidi Tera Micro Video Dyna Amphi Omni Tele Graphi Uni Multi Pixa Mega Nano Techni Giga Para Geo Compu Xeno Digi Intel Meta Robo Intelli Ray Ortho Electro Aero Pyra Centi Deci Milli Data Termi % SUF tex tron tronic meter science test systems sonic sonics sys fonics phon phonics thon theon con sat vac matics dyne mips matic tek tech corp plex tel set vision point % CORP Corp. Corp. Inc. Inc. Inc. Ltd. Ltd. GmBH % THINGS Scientific Industries Systems Information Systems Graphics Technologies High Technology Advanced Technologies Products Manufacturing Propulsion Cryogenics % LTR A A A A A B C D E E E E E F G G H I I I I I J K L M N O O O O O P P Q R S T U U U U U V W X X X X X X Y Y Y Y Z Z Z % CODE LTR|LTR|LTR, CORP\ LTR|LTR|LTR PRE|SUF, CORP\ LTR|LTR|LTR THINGS, CORP\ PRE|SUF\ PRE|SUF\ PRE|SUF THINGS\ PRE|SUF THINGS, CORP\ % SHAR_EOF fi exit 0 # End of shell archive -- --Craig ...{ucbvax,sun}!pixar!good