/******************************************************************************************************** * dbprint.c * * * * Bob Goeke MIT Center for Space Research 617-253-1910 * * * * Rev. Status: 0 Initial Release; generally cribbed from lcompress.c 3-23-87 * * 1 Add more error checking 4-1-87 * * 2 Allow line wrapping for FW soft new lines 4-30-87 * * 3 Tab character insertion fixed 11-10-87* * 4 Use intermediate output line store; add \? format command 11-12-87* * 5 Fix getfield() so single char entry gets through 3-8-88 * * Go to C86Plus; note gets() now has only one arg and returns *char(!) * * 6 Generalize paren character 3-24-89 * * 7 Allocate max memory for format file size 3-9-90 * * 8 Fix bug in \? wiping out following lines 4-17-90 * * 9 Take out FW soft-newline "feature" 4-19-90 * * 2.0 No real change; transported to UNIX-land 3-28-91 * ********************************************************************************************************/ static char rcshdr[] = "$Header: /acis/a1/database/cstuff/RCS/dbprint.c,v 2.5 2013/07/19 17:02:25 goeke Exp goeke $"; /*************************************************************************** $Log: dbprint.c,v $ Revision 2.5 2013/07/19 17:02:25 goeke Added pod Debug/Verbose flag now -d; help flag is -h * Revision 2.4 2002/01/25 21:41:34 goeke * Added -w flag to wrap lines in output * * Revision 2.3 2001/05/22 19:17:01 goeke * On Solaris, using gcc, using "char" instead of "unsigned char" . * * Revision 2.2 1994/03/31 20:53:13 goeke * Fix help message to indicate h=help * * Revision 2.1 1992/05/15 23:49:27 goeke * First formal RCS-controlled version; make help banner report RCS info * * Revision 1.1 1992/05/15 23:33:00 goeke * Initial revision * **************************************************************************/ #include /* #include */ #include #include #include #include #define NAME "dbprint" #define TITLE "D(ata)B(ase)PRINT $Revision: 2.5 $ $Date: 2013/07/19 17:02:25 $" #define STRING 128 /* max number of characters in a path name */ #define WIDTH 8192 /* max width of input line */ #define ESCAPE '\\' /* we use this as the excape char in the format file */ #define QUESTION '?' /* we use this to indicate conditional line output */ #define ARG '%' /* used to indicate using the command line argument for a field*/ /* #define SNL '\377' /* the FinalWord character for a soft newline */ /* #define CONTINUE " " /* a soft continuation line */ #define ERROR(A,B) { fprintf(stderr,"%s: ",NAME); \ fprintf(stderr,(A),(B)); } #define DEBUG(A,B) if(Debug) ERROR( (A), (B) ) #define TRAP(A,B) { ERROR( (A), (B) ) help(); } unsigned int Debug = 0; /* used to display work in progress */ char *Wildcard; char Zilch = '\0'; unsigned int Wrap = WIDTH; realwork(name) char *name; { FILE *formptr,*fopen(),*freopen(); struct stat fileinfo; extern unsigned char *malloc(); char buffer[WIDTH],*c; char *style,*k; char output[WIDTH],*o,*b; char field[STRING]; /* holds a field name */ char temp[WIDTH],*n; /* holds the contents of a single field; sized same as input!*/ /* (otherwise it could overflow if field not specified right)*/ char delimit; /* holds the current field delimiter */ char paren; /* hold terminating paren character */ unsigned int linput,toggle; unsigned int j,m,q,z; unsigned int nlflag,maybe; toggle = 1; /* the beginning of file is an implied new line */ /******************************************************************************************************** start things off by setting up the format in the style array ********************************************************************************************************/ if( stat(name,&fileinfo) ) { perror(NAME); TRAP("could not stat '%s'\n",name) } DEBUG("format file size: %ld\n",(fileinfo.st_blocks)*512L) if( !(style = malloc( (fileinfo.st_blocks)*512 )) ) TRAP("format file size exceeds available memory space\n",NULL) if( !(formptr=fopen(name,"r")) ) /* open the format file */ TRAP("could not open format file '%s'\n",name) DEBUG("accessing format information from '%s'\n",name) for( q=0; EOF!=(j=fgetc(formptr)); q++ ) style[q] = j; /* this gets the format file into an array for multiple reads below */ style[q] = NULL; /******************************************************************************************************** now for processing the text ********************************************************************************************************/ buffer[WIDTH-1] = NULL; linput = 0; while( fgets(buffer,WIDTH,stdin) ) /* for the entire file */ { linput++; if(buffer[WIDTH-1]) /* if the fgets got too much */ { ERROR("input line %d too long\n",linput) TRAP("maximum allowed line length is %d characters\n",WIDTH-1) } buffer[strlen(buffer)] = NULL; /* knock off the trailing newline */ o = output; /* this is the output string pointer */ nlflag = 0; /* this gets set to indicate previous char was newline */ maybe = 0; /* when set, inhibits output */ for( k=style; *k; k++) { if( *k!=ESCAPE ) /* check for the special character */ { switch(*k) { case('\n'): /* if this a new line? */ if( !nlflag ) /* was the previous character a new line */ { nlflag = 1; *o = NULL; if( !maybe ) { *o++ = '\n'; *o = NULL; trimoutput(output); } /* printf("%s\n",output); */ } maybe = 0; /* must reset these two or the next line */ o = output; /* would be concatenated! (rev 1.8) */ break; default: *(o++) = *k ; if( !isspace(*k) ) /* don't set line flag for whitespace */ nlflag = 0; break; } } else switch( *(++k) ) /* work on next character */ { case ESCAPE: /* we want a literal ESCAPE in output */ nlflag = 1; *(o++) = ESCAPE ; break; case QUESTION: /* put out line only if substitution happens */ maybe++; break; case 'n': /* this is an explicit newline */ nlflag = 1; *o = NULL; if( !maybe ) { *o++ = '\n'; *o = NULL; trimoutput(output); } /* printf("%s\n",output); */ maybe = 0; o = output; break; case 't': /* this is an explicit tab */ nlflag = 0; *(o++) = '\t' ; break; case 'f': /* this is an explicit formfeed */ nlflag = 0; *o = NULL; if( !maybe ) { *o++ = '\f'; *o = NULL; trimoutput(output); } /* printf("%s\f",output); */ maybe = 0; o = output; break; case ARG: /* use the command line argument */ /* for a field name here */ if( *Wildcard==NULL ) /* if there was no argument, skip out */ break; if( getfield(buffer,Wildcard,temp,*Wildcard) ) { /* we pump it out only if something was found! */ nlflag = 0; n = temp; while( *n ) { /*************************************************************** if( *n==SNL ) for(b=CONTINUE; *b; ) *(o++) = *(b++); else ***************************************************************/ *(o++) = *n; n++; } } break; default: if( isalnum(*k) ) /* first pass remaining alpha-numerics */ { nlflag = 0; *(o++) = *k ; break; } /* but funny stuff quotes a field name */ switch( *k ) /* and find its mate */ { case '(': paren = ')'; break; case '[': paren = ']'; break; case '<': paren = '>'; break; default: paren = *k; } delimit = *(++k); /* save the delimiter */ for( m=0; *k!=paren && *k!=EOF && *k!='\n' && m=STRING-1 ) TRAP("input field name to long:\n%s\n",field) if( *k==EOF || *k=='\n' ) TRAP("unbalanced escape characters in format file %s",name) if( getfield(buffer,field,temp,delimit) ) { /* we pump it out only if something was found! */ nlflag = 0; n = temp; while( *n ) { /******************************************************** if( *n==SNL ) { *o = NULL; printf("%s\n%s",output,CONTINUE); o = output; } else ********************************************************/ *(o++) = *n; n++; } maybe = 0; } break; } } /* NB that the format file needs a final carriage return if it wants it */ *o = NULL; if( maybe==0 ) { trimoutput(output); } /* printf("%s",output); */ else if( Debug ) fprintf(stderr,"=====>>>>>%s",output); } return(0); } getfield(data,field,out,sepchar) /* This function takes a 'data' string, finds the first 'field' match, * and returns the field contents in the 'out' string. * Returns 0 if no field, or nothing in field, is found; 1 otherwise. */ char *data, *field, *out, sepchar; { char c; register a,k; int start; k=0; a = testrecord(data,field); /* returns pos. of field */ if (a==-1) return(0); a += strlen(field); /* skip over field name */ while (isspace(data[a++])); /* skip leading white */ start = --a; while (((c=data[a++])!=sepchar) && (c!=NULL)) out[k++] = c; /* find end of this */ --a; while (isspace(data[--a])) /* skip trailing white */ ; out[++a - start] = '\0'; if (a - start <= 0) return(0); else return(1); } trimoutput(stuff) char *stuff; { register jj; if (!*stuff) return(0); for (jj=0; *stuff; jj++) { putchar(*stuff++); if ( jj>Wrap && isspace(*stuff) ) { putchar('\n'); while(isspace(*++stuff)) ; jj=0; } } return(0); } testrecord(record,test) char *record, *test; { register k,j,i; for (i=0; record[i]!='\0'; i++) { for (j=i, k=0; test[k]!='\0' && record[j]==test[k]; j++, k++) ; if (test[k]=='\0') return(i); } return(-1); } main(argc,argv) int argc; char *argv[]; { char *formfile; formfile = &Zilch; Wildcard = &Zilch; for( argc--,argv++; argc>0; argc--,argv++ ) /* sweep the entire argument list */ { if( **argv == '-' ) /* if this argument is a flag of some type */ { while(*(++(*argv))) /* sweep flags */ switch(**argv) { case 'X': case 'x': argc--; argv++; if( argc<=0 ) TRAP("string argument must follow 'x' flag",NULL) Wildcard = *argv; goto next; case 'W': case 'w': argc--; argv++; if( argc<=0 ) TRAP("numeric argument must follow 'w' flag",NULL) Wrap = atoi(*argv); goto next; break; case 'H': case 'h': fullpoop(); break; /* this is never reached! */ case 'D': case 'd': Debug++; break; default: ERROR("unknown flag '%c' invoked.\n",**argv) help(); } /* switch */ } /* if(**argv=='-') */ else if( *formfile ) TRAP("only one format file may be specified\n",NULL) else formfile = *argv; next: continue; } /* for(all arguments) */ if( *Wildcard ) DEBUG("command line formatting argument is '%s'\n",Wildcard) if( !(*formfile) ) TRAP("no format file specified; cannot proceed!\n",NULL) else DEBUG("format file is '%s'\n",formfile) if( realwork(formfile) ) exit(1); /* return 1 on error */ else exit(0); /* normal exit */ } fullpoop() { fprintf(stderr,"\t%s\n",TITLE); fprintf(stderr,"\tPublic domain software written by 'goeke@space.mit.edu'\n"); fprintf(stderr,"\tBob Goeke, MIT Center for Space Research, Cambridge, MA 02139\n\n"); fprintf(stderr,"Does a formated dump of a data base file according to a template found in\n"); fprintf(stderr,"\ta format file. The data base must be of a form where each record is on\n"); fprintf(stderr,"\ta separate line; each field is preceded by a unique name, the first\n"); fprintf(stderr,"\tcharacter will be used to determine the end of that field and the\n"); fprintf(stderr,"\tbeginning of the next.\n"); fprintf(stderr,"The format file may contain any text, but every string delimited so:\n"); fprintf(stderr,"\t'\\(string)' will be substituted for by the contents of the data field\n"); fprintf(stderr,"\tso named. (Forms such as '\\[string]', '\\', or '\\:string:' are\n"); fprintf(stderr,"\talso OK.) Normally adjacent blank lines are suppressed.\n"); fprintf(stderr,"\tA '\\n' symbol in the format forces a new line to occur in the output,\n"); fprintf(stderr,"\twhile a '\\t' forces a tab and a '\\f' forces a form feed.\n"); fprintf(stderr,"\tA '\\?' symbol at the beginning of a format line causes that line to be\n"); fprintf(stderr,"\toutput only if a valid substitution occurs in the line.\n"); fprintf(stderr,"\tA '\\%%' in the format is replaced by the field specified by the\n"); fprintf(stderr,"\tcommand line argument.\n\n"); help(); } help() { fprintf(stderr,"usage: %s [-hd] [-x string] [-w #] format_filename output\n",NAME); fprintf(stderr,"flag x|X uses following string to substitue for '\\%%' in format\n"); fprintf(stderr,"flag w|W uses following number to wrap output at next whitespace after #\n"); fprintf(stderr," h|H - a full help message is output\n"); fprintf(stderr," d|D - debugging information is displayed\n"); exit(1); } /*************************************************************************************************************** Notes: 11/24/86 Change to the more general case of reading a data file which contains the field descriptors and the associated formatting commands for decompression. 3/20/87 Make the formatting very general indeed. Work to do: rewrite the help messages and use one of the calling parameters a committee name (or several such?) which the format file indicates by (say) %1. 3/23/87 Must change the field specifiers to a general case of starting with some non-alpha set, then putting this set into the escape seeking strings . . . . or every '\' is a field escape; but then how do we do newlines? 3/26/87 Made the following work: *** The March Test *** \n \(=name) \(=ln) \t][ \% ][ \(=addr) \(=add2) \\foo \(=town) \(=zip) $ \f 4/1/87 Fixed problem of overflowing the 'temp' buffer. 4/30/87 Added ability to wrap lines which have FW soft newline in them. 11/12/87 Put in the stuff for buffering the lines, but did it to the whole form at once! 3/24/89 Make it possible to use (), [], <> pairs or any single non-alphanumeric char to designate fields ***************************************************************************************************************/ /*************************************************************************************************************** Pod follows # NB the CSS addition makes possible the desired indent with # =over/=item...=item/=back construct when used in an HTML context *********************** =for html DB -- dbprint =head2 NAME dbprint -- print each record in a database according to a template =head2 USAGE dbprint [-d[ebug] [-h[elp] [-w[rap] # [-x[change] string format_filename output =head2 FLAGS -d prints debugging info to STDERR -h prints help message -w wraps the line at the next available whitespace in string -x exchange "string" for any occurance of "\%" in template =head2 DESCRIPTION This program prints to STDOUT a formated dump of a compressed data base file according to a template found in named format file. Each record is the data base is processed successively with no intervening characters or whitespace between records. The data base file must conform to the standards set by B. The format file may contain any text, but every string delimited so: =over =item C<\(string)> will be substitued for by the value of the data field so named. (Forms such as C<\[string]>, C<<< \ >>>, or C<\:string:> are also OK. Adjacent blank lines are suppressed. =back =over =item C<\n \t \f> in the template will force a new line/tab/formfeed respectively to occur in the output. =back =over =item C<\?> at the beginning of a template line causes that line to be output only if a valid substitution occurs elsewhere in the line. =back =over =item C<\%> anywhere in the template will be replaced by the the value of the field specified by the B<-x> flag in the command line. =back =head2 BUGS None reported yet. =head2 SEE ALSO =head4 High Level programs dbnormal dbreport =head4 Low Level programs =head2 AUTHOR Bob Goeke =head2 RCS Information $Id: dbprint.c,v 2.5 2013/07/19 17:02:25 goeke Exp goeke $ =cut ***************************************************************************************************************/