/* Name : TOKEN.C Author : Kim Moser Date : 09/24/89 System : IBM PC / Borland Turbo-C 2.0 Descrip: For every line read from stdin, outputs argv[] (to stdout), replacing each instance of '&n' with the n'th string parsed from the line read from stdin. Usage : TOKEN [-options] {[][&][]} OPTION MEANING n0|1|2 n0=never append newline n1=append newline only when something was output (default) n2=always append newline s["[@]"] Separate parameters with string (or, if '@' specified, file named "separator") Instances of '\n' in will be converted to a newline, and \t will be converted to a tab. M Suppress messages W Suppress warnings Note: "string" may contain instance(s) of "&n" (1 <= n <= 9), in any order. For example, if a file containing the lines: John Doe Jane Doe Olivia Newton John were piped to TOKEN, which was invoked as follows: TOKEN -M My name is &2, &1. the following output would be generated: My name is Doe, John. My name is Doe, Mary. My name is Newton, Olivia. Example: piping a text file containing the lines Hello there. Are you happy? What could be more fun? to TOKEN, when invoked as follows: TOKEN echo %1 %2 %3 This is a test. will result in the following file being generated: echo Hello There. This is a test. echo Are you happy? This is a test. echo What could be This is a test. */ #include #include #include #include /* isdigit() */ #include /* filelength() */ #define MAXSTR 9 /* Max strings looked at per line from stdin */ int MSG=1; /* Whether to print messages */ int WARN=1; /* Whether to print warnings */ char SEPARATOR[255]; /* String which will separate command line arguments as they are output (defaults to " "). If SEPARATOR[0] is set to '@', then SEPARATOR+1 points to filename to be used as separator, and contents of file will be read into SEP. */ int inRAM=0; /* Whether separator file has been read into *SEP */ char *SEP; /* Points to where separator has been read into RAM */ long FLEN; /* Length of separator file */ long OFFSETS[MAXSTR]; /* Offsets (to stdin) of where each string starts */ int APPEND_EOL=1; /* Whether to print a newline after each line */ /* 0=never, 1=only when chars output for this line, 2=always */ int ENDFOUND=0; /* Set to 1 when EOF found */ long START=0; /* File pos of first char in next string (set by getoffsets()) */ int NUMSTRINGS; /* How many offsets this line has. Set by getoffsets(). */ void credits(void) /* Displays (to stderr) credits */ { fputs("TOKEN v1.0 09/28/89 Copyright (C) 1989 Kim Moser All Rights Reserved\n", stderr); } void usage(void) /* Displays (to stderr) usage */ { credits(); fputs("Usage: TOKEN [-options] {[][&][]}\n", stderr); fputs(" where 0 <= n <=9\n", stderr); fputs("\n OPTION MEANING\n", stderr); fputs(" n0|1|2 n0: never append a newline after each line\n", stderr); fputs(" n1: append a newline after each line only if characters\n", stderr); fputs(" were output for that line\n", stderr); fputs(" n2: always append a newline after each line\n", stderr); fputs("s\"[@]\" Separate parameters with string \"sep\"\n", stderr); fputs(" (or, if '@' specified, contents of file named \"sep\")\n", stderr); fputs(" M Suppress messages\n", stderr); fputs(" W Suppress warnings\n", stderr); fputs("\nFor every line read from stdin, outputs strings on command line,\n", stderr); fputs("replacing instances of \"&\" with the n'th string parsed from the\n", stderr); fputs("line read from stdin.\n", stderr); fputs("\nWarns if is larger than the index of the last string parsed from\n", stderr); fputs("any line, or if the line contains more than 9 strings.\n", stderr); exit(-1); } long where(void) /* Gets file pos of stdin; If error, prints message to stderr and calls exit(), else returns file pos. */ { long r; if ( (r=ftell(stdin)) == -1L ) { fputs("\nUnable to read file pointer of stdin.\n", stderr); exit(-1); } return r; } void here(p) long p; /* Sets file pos of stdin to 'p'; If error, prints message to stderr and exits. */ { if ( fseek(stdin,p,SEEK_SET) ) { if (WARN) fprintf(stderr, "\nUnable to set stdin's file pointer to %d.\n", p); exit(-1); } } void printsep(void) /* If SEPARATOR is a filename (i.e. begins with '@' then sends contents of that file to stdout, else sends SEPARATOR verbatim to stdout */ { FILE *fp; long i; int prev, ch=0; if (SEPARATOR[0]=='@') { /* It's a filename */ if (!inRAM) { inRAM=1; /* Open in BINARY mode: */ if ( (fp=fopen(SEPARATOR+1,"rb"))==NULL ) { if (WARN) fprintf(stderr, "Error opening separator file '%s'.\n", SEPARATOR+1); exit(-1); } if ( (FLEN=filelength(fileno(fp)))==-1 ) { if (WARN) fprintf(stderr, "Error getting length of separator file '%s'.\n", SEPARATOR+1); exit(-1); } if ( (SEP=(char*)malloc((size_t)FLEN))==NULL ) { if (WARN) fprintf(stderr, "Error allocating %ld bytes for separator file '%s'.\n", FLEN, SEPARATOR+1); exit(-1); } if ( (i=fread(SEP,1,FLEN,fp))!=FLEN ) { if (WARN) fprintf(stderr, "Able to read only %ld of %ld bytes from separator file '%s'.\n", i, FLEN, SEPARATOR+1); exit(-1); } if ( fclose(fp) ) { if (WARN) fprintf(stderr, "Error closing separator file '%s'.\n", SEPARATOR+1); exit(-1); } } } /* Print separator: */ for (i=0; (inRAM ? iand< if s[0] abruptly ends (indicating that separator is in s[1]). 'max' is argc. Returns index of last param looked at. */ { int i=1, k=1; int j; /* Index into SEPARATOR */ int r=2; /* Result */ /* Assume */ while(s[k][i]) { switch(s[k][i]) { case 's': /* Separator follows */ /* Is separator in next string? */ if (!s[k][i+1]) { /* Separator is in next string */ k++; i=0; r=3; } else { /* Separator is in same string */ i++; } if (r<=max) { /* We're still in a valid param */ j=0; /* Read until EOS: */ while (s[k][i]) { SEPARATOR[j++]=s[k][i++]; } SEPARATOR[j]='\0'; } else { /* We went past argv[argc-1] while looking for separator, so assume separator is blank */ SEPARATOR[0]='\0'; } return r; case 'n': /* Expect '0', '1', or '2': */ APPEND_EOL=s[k][++i]-'0'; if (APPEND_EOL<0 || APPEND_EOL>2) { usage(); } break; case 'M': /* Suppress messages */ MSG = 0; break; case 'W': /* Suppress warnings */ WARN = 0; break; default: usage(); } i++; } return r; } void main(argc, argv) int argc; char **argv; { int i; int start=1; /* argv with which to start */ /* Assume */ strcpy(SEPARATOR, " "); /* Init to defaut */ start=1; /* Start at argv[1] (assume) */ /* If any params specified, parse them: */ if ( (argc>1) && (argv[1][0]=='-') ) { start = params(argv, argc); } if (MSG) { /* Display settings: */ credits(); fputs("\nSETTINGS:\n", stderr); fprintf(stderr, " %s warnings\n", WARN?"Display":"Suppress"); fprintf(stderr, " %s messages\n", MSG?"Display":"Suppress"); fputs(" Separator: ", stderr); if (SEPARATOR[0]=='@') { fprintf(stderr, "contents of file '%s'\n", SEPARATOR+1); } else { fprintf(stderr, "\"%s\"\n", SEPARATOR); } fputs(" Output newline: ", stderr); switch(APPEND_EOL) { case 0: fputs("Never\n", stderr); break; case 1: fputs("Only if line is not empty\n", stderr); break; case 2: fputs("After each line\n", stderr); break; default: fprintf(stderr, "Internal error: APPEND_EOL is neither 0, 1, nor 2.\n"); exit(-1); } } while(1) { SOFARLINE=0; /* Assume */ /* Clear array of file offsets: */ for (i=0; i