1
/************************************************************************/
5
/* Main program module. */
9
/* 3420 NW Elmwood Dr. */
11
/* Corvallis, Oregon 97339 */
17
/* Corvallis, OR 97339 */
20
/* This program is hereby placed in the public domain. In */
21
/* contrast to other claims of "public domain", this means no */
22
/* copyright is claimed and you may do anything you like with PP, */
23
/* including selling it! As a gesture of courtesy, please retain */
24
/* the authorship information in the source code and */
28
/* PP [inputfile] -[cdeilostuvz?] */
29
/* -c 0|1|2|3|4|5|6 Enable desired option: */
30
/* 0 -> Replace arguments in strings. */
31
/* 1 -> Expand macros inside #pragma asm. */
32
/* 2 -> Allow recursive comments. */
33
/* 3 -> Rescan macro expansions for */
35
/* 4 -> Allow macro definitions and undefs */
37
/* 5 -> Perform Trigraph translation. */
38
/* 6 -> Allow C++ style "//" eol comments */
39
/* -d: define symbols (-d symbol[=value].) */
40
/* -e: Don't abort on errors. */
41
/* -i <list>: Specify path for #include searches. */
42
/* -l <t>: Control line # emission to the Output */
43
/* file. If <t> == 'n' don't emit any */
44
/* explicit line # info. If <t> == 'a', */
45
/* then use the abbreviated form: */
46
/* "#<n> |filename|" */
47
/* If <t> == 'l' then use the "long" form: */
48
/* "#line <n> |filename|" */
49
/* The "long" form is the default. */
50
/* -o: Specify output file name (def: <infile>.pp) */
51
/* -s: Product statistics info at end (DEBUG only.)*/
52
/* -t: Specify add/remove token characters. */
53
/* -u: Undefine an initial symbol (e.g. CPM) */
54
/* -v: Invert VERBOSE toggle. */
55
/* -z: Output DEBUG messages (DEBUG only.) */
56
/* -?: Print usage information. */
58
/* If [inputfile] is absent, standard input is assumed. */
61
/* PP is a general purpose preprocessor that handles */
62
/* ANSI C style preprocessor directives. */
68
/* Inspired by the PP.C program by: */
72
/* Newton Centre, MA 02159 */
75
/* which, in turn, was based upon the Ratfor */
76
/* version found in K&P's Software Tools. */
78
/* 30-Mar-85 GO First entered. */
81
/* The convention used within this program is to */
82
/* capitalize global variable names. Function */
83
/* names, per the C convention, are not capitalized. */
85
/* Future features: */
86
/* Generalize on a means for generating the output */
87
/* name. Currently ".pp" is appended to the input */
88
/* file name, after the input file extension (if any) */
89
/* is removed. Dave Regan suggests prepending a '$' */
90
/* to the input file name and using that as the output */
91
/* name, retaining the input file's extension (if any). */
92
/* (e.g. ABC.XYZ becomes $ABC.XYZ) Must think on this. */
94
/* Some performance enhancement may be gained by using */
95
/* a better symbol table allocation or simply a better */
96
/* hashing function. I will have to check some */
97
/* reference materials... */
99
/* Functions contained in this module: */
101
/* main Main module. */
102
/* getnext Get next parameter from argv/argc. */
103
/* init Initialize the preprocessor. */
104
/* usage Print usage information. */
106
/* * Unix is a trademark/footnote of Bell Laboratories. */
107
/* * CP/M is a trademark of Digital Resarch, Inc. */
108
/* * Q/C is a trademark of Quality Computer Systems. */
109
/* * VMS is a trademark of Digital Equipment Corporation. */
111
/************************************************************************/
113
#define MAIN /* This is the main() module */
119
extern unsigned _stklen = 32767;
120
#endif /* __TURBOC__ */
122
/************************************************************************/
126
/* The main program. */
128
/************************************************************************/
131
#pragma optimize("e",off) /* Disable global reg optimizing */
132
#pragma optimize("g",off) /* Disable global common subs */
133
#pragma optimize("l",off) /* Disable loop optimizations */
138
PreProcess(argc,argv)
145
static char *one_string = "1";
147
register int t; /* General holder for token */
148
register struct symtab *p; /* Ptr into symbol table */
149
register struct ppdir *sp; /* Ptr to predefined symbol */
158
int skip; /* Boolean for option loop */
160
init(); /* Initialize preprocessor */
162
ifile = /* No input file specified */
163
ofile = FALSE; /* No output file specified */
171
while((! skip) && (*++s != '\0'))
175
/* -[c 0|1|2|3|4|5|6] */
178
s2 = getnext(s,&argc,&argv,NO);
190
s2 = "comment_recurse";
212
pragopt(EMPTY,FALSE,s2);
216
/* -[d symbol [= value]] */
219
s = getnext(s,&argc,&argv,NO);
220
s2 = strchr(s,'='); /* Location of val */
222
*s2++ = '\0'; /* Terminate string */
224
s2 = one_string; /* Default */
226
if(lookup(s,NULL) != NULL)
227
warning("Symbol already defined: ",s);
229
sbind(s,s2,NO_PARAMS);
231
skip = TRUE; /* Skip to next param */
234
/* -[e] don't abort on errors */
240
/* -iI <#include search path> */
244
fatal("Too many pathnames","");
245
Ipath[Ipcnt++] = getnext(s,&argc,&argv,NO);
246
skip = TRUE; /* Skip to next param */
249
/* -[l a|l|n] Spec #line output mode */
252
s2 = getnext(s,&argc,&argv,NO);
273
skip = TRUE; /* Skip to next param */
276
/* -[o file] spec output file name */
279
s2 = getnext(s,&argc,&argv,NO);
280
strcpy(Outfile,s2); /* Copy filename */
282
skip = TRUE; /* Skip to next param */
286
/* -[s] give statistics at end */
289
Stats = Verbose = TRUE; /* Implies Verbose */
292
/* -[t Astr|Rstr] Add or delete chars from LETTER class */
295
s2 = getnext(s,&argc,&argv,NO);
312
for(; *s2 != '\0'; s2++)
315
typetab[*s2 + 1] |= C_L;
317
typetab[*s2 + 1] &= ~C_L;
320
skip = TRUE; /* Skip to next param */
326
s = getnext(s,&argc,&argv,NO);
328
if(lookup(s,NULL) == NULL)
329
warning("Symbol not defined: ",s);
332
skip = TRUE; /* Skip to next param */
335
/* -[v] verbose mode toggle */
342
/* -[z] enable debug */
346
printf("Debug is on\n");
350
usage(FALSE); /* Give usage info and quit */
353
fprintf(STDERR,"FATAL: Bad option: %s\n",s);
360
/* Try to get input file */
362
if(! inc_open(s,-1,0)) /* Open file here */
363
#else /* HOST != H_CPM */
364
if(! inc_open(s)) /* Open input file */
365
#endif /* HOST == H_CPM */
367
fatal("Unable to open input file: ",s);
369
ifile = TRUE; /* Got an input file */
373
/* Too many file names given */
378
Nextch = A_trigraph ? trigraph : gchbuf; /* Next char source */
382
/* Must have at least an input file name */
388
/* No output name given; use input name and modify it */
389
strcpy (Outfile,Filestack[0]->f_name);
390
/* terminate the file name before any extension */
391
if((s = strrchr(Outfile,'.')) != NULL)
393
strcat(Outfile,".pp");
397
if(strcmp(Outfile,Filestack[0]->f_name) == EQUAL)
398
fatal("Input and output filenames are the same: ",Outfile);
399
else if((Output = fopen(Outfile,"w")) == NULL)
400
fatal("Unable to create output file: ",Outfile);
404
/* Create a bigger than average buffer */
405
if((s = malloc(OUTBUFSIZE)) == NULL)
408
setbsize(Output,OUTBUFSIZE);
409
#endif /* HOST == H_CPM */
413
printf("%s%s\n\n","PP Preprocessor, ",VERSION);
414
printf("Output will be on <%s>\n",Outfile);
415
printf("*** Read %s\n",Filestack[Filelevel]->f_name);
418
Do_name = TRUE; /* Force name output on #line */
420
init_path(); /* Initialize search path */
421
Ipath[Ipcnt] = NULL; /* Terminate last include path */
423
for(Lastnl = TRUE, t = gettoken(GT_STR); t != EOF;
424
t = gettoken(GT_STR))
426
if((Ifstate != IFTRUE) && (t != '\n') && istype(t,C_W))
429
else if(Lastnl && (t == DIRECTIVE_CHAR))
431
t = getnstoken(GT_STR);
434
if((sp = predef(Token,pptab)) != NULL)
437
* If unconditionally do it or if emitting code...
439
if(sp->pp_ifif || (Ifstate == IFTRUE))
441
/* Do #func */ (void) (*(sp->pp_func))
445
else if(Ifstate == IFTRUE)
446
non_fatal("Illegal directive","");
448
scaneol(); /* Suck till EOL ('\n' next) */
452
non_fatal("Bad directive","");
456
pushback('\n'); /* Leave for fetch to get */
458
else if((t != EOF) && (Ifstate == IFTRUE))
460
#if (TARGET == T_QC) OR (TARGET == T_QCX) OR (TARGET == T_TCX)
461
if(t == LETTER && Macexpand)
462
#else /* !((TARGET == T_QC) OR (TARGET == T_QCX) OR (TARGET == T_TCX)) */
464
#endif /* (TARGET == T_QC) OR (TARGET == T_QCX) OR (TARGET == T_TCX) */
466
if((p = lookup(Token,NULL)) != NULL)
468
/* Call macro */ (void) docall(p,NULL,NULL);
471
/* Just output token if nothing */ puttoken(Token);
477
Lastnl = TRUE; /* Turn on if '\n' */
478
else if(! istype(t,C_W))
479
Lastnl = FALSE; /* Turn off if !ws */
484
while((t != '\n') && (t != EOF))
486
/* Absorb to EOL if False #ifxx */
487
t = gettoken(GT_STR);
495
/* Unterminated #if */
496
non_fatal("Unterminated conditional","");
501
printf("\nActive symbols at end:\t%d\tMaximum symbols used:\t%d\n",
504
printf("Free memory: %5u\n",maxsbrk());
505
#endif /* HOST == H_CPM */
508
if(Verbose || Errors)
511
printf("\n%d errors detected\n",Errors);
513
printf("\nNo errors detected\n");
519
printf("\nSymbol table bucket statistics:");
520
for(i = 0; i < NUMBUCKETS; i++)
523
printf("\n"); /* New line */
524
for(n = 0, p = Macros[i]; p != 0; p = p->s_link, n++)
525
; /* Count items in list */
526
printf(" %3d:%-3d",i,n);
532
if((Output != stdout) && (fclose(Output) == EOF))
533
fatal("Unable to close output file: ",Outfile);
537
exit(Eflag ? 0 : Errors);
538
#endif /* SLPP_LIBRARY */
542
#pragma optimize("e",on) /* Enable global reg optimizing */
543
#pragma optimize("g",on) /* Enable global common subs */
544
#pragma optimize("l",on) /* Enable loop optimizing */
547
/************************************************************************/
551
/* Get next parameter from parameter line and return its address. */
552
/* If the character following the current parameter (cp) is non- */
553
/* null, then return that parameter, otherwise look ahead in the */
554
/* argv list to fetch the next one. Give a usage() error if the */
555
/* parameter list is empty or if the fetched parameter's first */
556
/* character is a '-' and the <swvalid> parameter is NO. */
558
/************************************************************************/
561
getnext(cp,argc,argv,swvalid)
565
int swvalid; /* True if -x token valid */
571
/* Parameters remain -- use next one */
572
--*argc; /* Count it down */
573
cp = *++*argv; /* Return its address */
576
usage(TRUE); /* Otherwise give usage error */
579
if(!swvalid && (*cp == '-'))
580
usage(TRUE); /* Complain if switch starts w/'-' */
585
/************************************************************************/
589
/* Initialize the preprocessor. */
591
/************************************************************************/
596
static char *one_string = "1";
600
#if (HOST != H_CPM) AND (HOST != H_MPW)
601
time_t long_time; /* time_t def'd in "time.h" */
602
#endif /* (HOST != H_CPM) AND (HOST != H_MPW) */
603
char str[TOKENSIZE + 1];
610
Verbose = FALSE; /* Set verbose state */
612
Eflag = FALSE; /* Say to abort on errors */
613
Lineopt = LINE_EXP; /* Default to "long" #line form */
615
#if (TARGET == T_QC) OR (TARGET == T_QCX) OR (TARGET == T_TCX)
616
Do_asm = FALSE; /* Not inside #pragma asm/endasm */
617
Macexpand = TRUE; /* Macro expansion enabled */
618
Asmexpand = FALSE; /* Disabled inside asm/endasm */
619
#endif /* (TARGET == T_QC) OR (TARGET == T_QCX) OR (TARGET == T_TCX) */
621
Outline = 1; /* Line number of next output line */
623
Filelevel = -1; /* Current file level */
626
(struct pbbuf *) malloc(sizeof(struct pbbuf) * PUSHBACKSIZE);
631
Pbbufp->pb_type = PB_TOS; /* Top of stack marker */
633
A_astring = /* Replace args within strings */
634
A_crecurse = /* No recursive comments */
635
A_eolcomment = /* No eol comments */
636
A_rescan = /* No macro gen'd directives */
637
A_stack = /* No stacking of macro def's */
638
A_trigraph = FALSE; /* No Trigraph translation */
640
Nsyms = /* Number of symbols generated */
641
Errors = /* Zero the error counter */
642
Iflevel = /* #if stack pointer */
643
Ipcnt = /* Number of -i paths */
644
Unique = 0; /* Zero unique # counter */
646
Ifstack[0].i_state = Ifstate =
647
IFTRUE; /* Currently TRUE #ifxxx assumed */
650
Orig_user = bdos1(BDOS_USER,0xFF); /* Save user and disk */
651
Orig_disk = bdos1(BDOS_GETDISK,0);
652
#endif /* HOST == H_CPM */
655
* Initialize the Time and Date variables to hold the ANSI strings that
656
* each will print out in response to __TIME__ and __DATE__ respectively.
658
#if (HOST == H_CPM) OR (HOST == H_MPW)
659
strcpy(_Time,"HH:MM:SS"); /* Fake a time */
660
strcpy(Date,"Mmm DD YYYY");
661
#else /* !((HOST == H_CPM) OR (HOST == H_MPW)) */
662
(void) time(&long_time); /* Seconds since whenever... */
663
strncpy(str,asctime(localtime(&long_time)),26); /* Get time/date */
665
strncpy(_Time,&str[11],8); /* Pull time portion out of string */
668
strncpy(Date,&str[4],7); /* Pull month and day out of string */
669
strncpy(&Date[7],&str[20],4); /* Pull year out of string */
671
#endif /* (HOST == H_CPM) OR (HOST == H_MPW) */
673
/************************************/
674
/* Define the automatic definitions */
675
/************************************/
678
sbind("unix",one_string,NO_PARAMS); /* #define unix 1 */
679
#endif /* TARGET == T_UNIX */
682
sbind("unix",one_string,NO_PARAMS); /* #define unix 1 */
683
sbind("BSD",one_string,NO_PARAMS); /* #define BSD 1 */
684
#endif /* TARGET == T_BSD */
687
sbind("VMS",one_string,NO_PARAMS); /* #define VMS 1 */
688
#endif /* TARGET == T_VMS */
690
#if (TARGET == T_QC) OR (TARGET == T_QCX)
691
sbind("CPM",one_string,NO_PARAMS); /* #define CPM 1 */
692
sbind("QC",one_string,NO_PARAMS); /* #define QC 1 */
693
/* Generate asm() macro */
694
sbind("asm",";\n#pragma asm\n_PARAM_\n#pragma endasm\n",
695
makeparam("_PARAM_",PF_RQUOTES));
696
#endif /* (TARGET == T_QC) OR (TARGET == T_QCX) */
699
sbind("TC",one_string,NO_PARAMS); /* #define TC 1 */
700
/* Generate asm() macro */
701
sbind("asm",";\n#pragma asm\n_PARAM_\n#pragma endasm\n",
702
makeparam("_PARAM_",PF_RQUOTES));
703
#endif /* TARGET == T_TCX */
705
/* Now bind the automatic line/file generators, etc. */
708
str[0] = (char) LINE_TOKEN; sbind("__LINE__",str,NO_PARAMS);
709
str[0] = (char) FILE_TOKEN; sbind("__FILE__",str,NO_PARAMS);
710
str[0] = (char) TIME_TOKEN; sbind("__TIME__",str,NO_PARAMS);
711
str[0] = (char) DATE_TOKEN; sbind("__DATE__",str,NO_PARAMS);
712
str[0] = (char) NOW_TOKEN; sbind("__NOW__",str,NO_PARAMS);
713
str[0] = (char) NEXT_TOKEN; sbind("__NEXT__",str,NO_PARAMS);
714
str[0] = (char) PREV_TOKEN; sbind("__PREV__",str,NO_PARAMS);
716
/* Bind macro symbols for the configuration setting variables */
718
for(i = 0; pragtab[i].pp_name != NULL; i++)
720
if(pragtab[i].pp_func == pragopt)
723
for(toptr = &str[2], fromptr = pragtab[i].pp_name;
726
*toptr++ = (islower(*fromptr) ?
727
toupper(*fromptr) : *fromptr);
731
sbind(str,"0",NO_PARAMS);
736
/************************************************************************/
740
/* Print usage information and exit with argument passed. */
742
/************************************************************************/
750
"Usage: pp <input> -[cdeilostuvz?]\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
752
"Usage: pp <input> -[cdeilotuv?]\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
754
" -c 0|1|2|3|4|5|6 Enable the desired configuration option:\n",
755
" 0 -> Replace macro arguments in strings.\n",
756
" 1 -> Expand macros inside #pragma asm.\n",
757
" 2 -> Allow recursive comments.\n",
758
" 3 -> Rescan macro expansions for directives.\n",
759
" 4 -> Allow macro defs and undefs to stack/unstack.\n",
760
" 5 -> Perform Trigraph input character translation.\n",
761
" 6 -> Permit C++ style \"//\" eol comments.\n",
762
" -d s[=v] Define symbol <s> to have value <v> (default 1).\n",
763
" -e Don't abort on error.\n",
764
" -i Set path for #include files.\n",
765
" -l a|l|n Specify #line output mode (abbrev/long/none).\n",
766
" -o file Specify output file name.\n",
768
" -s Generate statistics summary at end.\n",
770
" -t str Add/remove LETTER chars (Accc or Rccc).\n",
771
" -u s Undefine an initial symbol.\n",
772
" -v Verbose mode toggle.\n",
774
" -z Output debug messages.\n",
776
" -? Output this message.\n",
777
" Output file, if not specified, is <input>.pp\n");