1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 #include <stdio.h>
25 #include <ctype.h>
26 #include "cppdef.h"
27 #include "cpp.h"
28
29 FILE *pCppOut = NULL;
30 FILE *pCppIn = NULL;
31
32 #if OSL_DEBUG_LEVEL > 1
33 FILE *pDefOut = NULL; /* ER evtl. #define's dump */
34 #endif
35
36 #ifdef B200
37 /* BP, 25.07.91, einzige Moeglichkeit unter BC Stack und Heap festzusetzen */
38 extern unsigned _stklen = 24000;
39 extern unsigned _heaplen = 30000;
40 #endif
41
42
43
44 /*
45 * Commonly used global variables:
46 * line is the current input line number.
47 * wrongline is set in many places when the actual output
48 * line is out of sync with the numbering, e.g,
49 * when expanding a macro with an embedded newline.
50 *
51 * token holds the last identifier scanned (which might
52 * be a candidate for macro expansion).
53 * errors is the running cpp error counter.
54 * infile is the head of a linked list of input files (extended by
55 * #include and macros being expanded). infile always points
56 * to the current file/macro. infile->parent to the includer,
57 * etc. infile->fd is NULL if this input stream is a macro.
58 */
59 int line; /* Current line number */
60 int wrongline; /* Force #line to compiler */
61 char token[IDMAX + 1]; /* Current input token */
62 int errors; /* cpp error counter */
63 FILEINFO *infile = NULL; /* Current input file */
64 #if OSL_DEBUG_LEVEL > 1
65 int debug; /* TRUE if debugging now */
66 int bDumpDefs; /* TRUE if #define's dump req. */
67 #ifdef EVALDEFS
68 int bIsInEval; /* TRUE if #define eval now */
69 char EvalBuf[NEVALBUF + 1]; /* evaluation buffer */
70 int nEvalOff = 0; /* offset to free buffer pos */
71 #endif
72 #endif
73 /*
74 * This counter is incremented when a macro expansion is initiated.
75 * If it exceeds a built-in value, the expansion stops -- this tests
76 * for a runaway condition:
77 * #define X Y
78 * #define Y X
79 * X
80 * This can be disabled by falsifying rec_recover. (Nothing does this
81 * currently: it is a hook for an eventual invocation flag.)
82 */
83 int recursion; /* Infinite recursion counter */
84 int rec_recover = TRUE; /* Unwind recursive macros */
85
86 /*
87 * instring is set TRUE when a string is scanned. It modifies the
88 * behavior of the "get next character" routine, causing all characters
89 * to be passed to the caller (except <DEF_MAGIC>). Note especially that
90 * comments and \<newline> are not removed from the source. (This
91 * prevents cpp output lines from being arbitrarily long).
92 *
93 * inmacro is set by #define -- it absorbs comments and converts
94 * form-feed and vertical-tab to space, but returns \<newline>
95 * to the caller. Strictly speaking, this is a bug as \<newline>
96 * shouldn't delimit tokens, but we'll worry about that some other
97 * time -- it is more important to prevent infinitly long output lines.
98 *
99 * instring and inmarcor are parameters to the get() routine which
100 * were made global for speed.
101 */
102 int instring = FALSE; /* TRUE if scanning string */
103 int inmacro = FALSE; /* TRUE if #defining a macro */
104
105 /*
106 * work[] and workp are used to store one piece of text in a temporay
107 * buffer. To initialize storage, set workp = work. To store one
108 * character, call save(c); (This will fatally exit if there isn't
109 * room.) To terminate the string, call save(EOS). Note that
110 * the work buffer is used by several subroutines -- be sure your
111 * data won't be overwritten. The extra byte in the allocation is
112 * needed for string formal replacement.
113 */
114 char work[NWORK + 1]; /* Work buffer */
115 char *workp; /* Work buffer pointer */
116
117 /*
118 * keepcomments is set TRUE by the -C option. If TRUE, comments
119 * are written directly to the output stream. This is needed if
120 * the output from cpp is to be passed to lint (which uses commands
121 * embedded in comments). cflag contains the permanent state of the
122 * -C flag. keepcomments is always falsified when processing #control
123 * commands and when compilation is suppressed by a false #if
124 *
125 * If eflag is set, CPP returns "success" even if non-fatal errors
126 * were detected.
127 *
128 * If nflag is non-zero, no symbols are predefined except __LINE__.
129 * __FILE__, and __DATE__. If nflag > 1, absolutely no symbols
130 * are predefined.
131 */
132 int keepcomments = FALSE; /* Write out comments flag */
133 int cflag = FALSE; /* -C option (keep comments) */
134 int eflag = FALSE; /* -E option (never fail) */
135 int nflag = 0; /* -N option (no predefines) */
136
137 /*
138 * ifstack[] holds information about nested #if's. It is always
139 * accessed via *ifptr. The information is as follows:
140 * WAS_COMPILING state of compiling flag at outer level.
141 * ELSE_SEEN set TRUE when #else seen to prevent 2nd #else.
142 * TRUE_SEEN set TRUE when #if or #elif succeeds
143 * ifstack[0] holds the compiling flag. It is TRUE if compilation
144 * is currently enabled. Note that this must be initialized TRUE.
145 */
146 char ifstack[BLK_NEST] = { TRUE }; /* #if information */
147 char *ifptr = ifstack; /* -> current ifstack[] */
148
149 /*
150 * incdir[] stores the -i directories (and the system-specific
151 * #include <...> directories.
152 */
153 char *incdir[NINCLUDE]; /* -i directories */
154 char **incend = incdir; /* -> free space in incdir[] */
155
156 /*
157 * This is the table used to predefine target machine and operating
158 * system designators. It may need hacking for specific circumstances.
159 * Note: it is not clear that this is part of the Ansi Standard.
160 * The -N option suppresses preset definitions.
161 */
162 char *preset[] = { /* names defined at cpp start */
163 #ifdef MACHINE
164 MACHINE,
165 #endif
166 #ifdef SYSTEM
167 SYSTEM,
168 #endif
169 #ifdef COMPILER
170 COMPILER,
171 #endif
172 #if OSL_DEBUG_LEVEL > 1
173 "decus_cpp", /* Ourselves! */
174 #endif
175 NULL /* Must be last */
176 };
177
178 /*
179 * The value of these predefined symbols must be recomputed whenever
180 * they are evaluated. The order must not be changed.
181 */
182 char *magic[] = { /* Note: order is important */
183 "__LINE__",
184 "__FILE__",
185 NULL /* Must be last */
186 };
187
188 static char *sharpfilename = NULL;
189
190 int nRunde = 0;
191
InitCpp1()192 void InitCpp1()
193 {
194 int i;
195 /* BP */
196 /* in der LIB-Version muessen alle Variablen initialisiert werden */
197
198 line = wrongline = errors = recursion = 0;
199 for( i = 0; i < IDMAX; i++ )
200 token[ i ] = 0;
201
202 for( i = 0; i < NWORK; i++ )
203 work[ i ] = 0;
204
205 for( i = 0; i < NINCLUDE; i++ )
206 incdir[ i ] = NULL;
207
208 workp = NULL;
209 for( i = 0; i < BLK_NEST; i++ )
210 ifstack[ i ] = TRUE;
211 ifptr = ifstack;
212
213 pCppOut = stdout;
214 pCppIn = stdin;
215 #if OSL_DEBUG_LEVEL > 1
216 debug = 0;
217 bDumpDefs = 0;
218 pDefOut = stdout;
219 #ifdef EVALDEFS
220 bIsInEval = 0;
221 for( i = 0; i < NEVALBUF; i++ )
222 EvalBuf[ i ] = 0;
223 nEvalOff = 0;
224 #endif
225 #endif
226 rec_recover = TRUE;
227 infile = NULL;
228 instring = inmacro = keepcomments = cflag = eflag = FALSE;
229 nflag = 0;
230 incend = incdir;
231 sharpfilename = NULL;
232 /* BP */
233 }
234
MAIN(int argc,char ** argv)235 int MAIN(int argc, char** argv)
236 {
237 register int i;
238 char **useargv, **pfargv;
239
240
241 if( nRunde == 0 )
242 {
243 pCppIn = stdin;
244 pCppOut = stdout;
245 }
246
247 nRunde++;
248 InitCpp1();
249 InitCpp2();
250 InitCpp3();
251 InitCpp4();
252 InitCpp5();
253 InitCpp6();
254
255 #if HOST == SYS_VMS
256 argc = getredirection(argc, argv); /* vms >file and <file */
257 #endif
258 initdefines(); /* O.S. specific def's */
259 if ( argv[argc-1][0] == '@' )
260 {
261 i = readoptions( argv[1], &pfargv ); /* Command file */
262 useargv=pfargv;
263 }
264 else
265 {
266 i = dooptions(argc, argv); /* Command line -flags */
267 useargv=argv;
268 }
269 switch (i) {
270 #if OSL_DEBUG_LEVEL > 1
271 case 4:
272 if ( bDumpDefs )
273 {
274 /*
275 * Get defBase file, "-" means use stdout.
276 */
277 if (!streq(useargv[3], "-")) {
278 #if HOST == SYS_VMS
279 /*
280 * On vms, reopen stdout with "vanilla rms" attributes.
281 */
282 if ((i = creat(useargv[3], 0, "rat=cr", "rfm=var")) == -1
283 || dup2(i, fileno(stdout)) == -1) {
284 #else
285 /* alt if (freopen(useargv[3], "w", stdout) == NULL) { */
286
287 pDefOut = fopen( useargv[3], "w" );
288 if( pDefOut == NULL ) {
289 #endif
290 perror(useargv[3]);
291 cerror("Can't open output file \"%s\"", useargv[3]);
292 exit(IO_ERROR);
293 }
294 } /* Continue by opening output */
295 }
296 /* OSL_DEBUG_LEVEL > 1 */
297 #endif
298 case 3:
299 /*
300 * Get output file, "-" means use stdout.
301 */
302 if (!streq(useargv[2], "-")) {
303 #if HOST == SYS_VMS
304 /*
305 * On vms, reopen stdout with "vanilla rms" attributes.
306 */
307 if ((i = creat(useargv[2], 0, "rat=cr", "rfm=var")) == -1
308 || dup2(i, fileno(stdout)) == -1) {
309 #else
310 /* alt if (freopen(useargv[2], "w", stdout) == NULL) { */
311
312 pCppOut = fopen( useargv[2], "w" );
313 if( pCppOut == NULL ) {
314 #endif
315 perror(useargv[2]);
316 cerror("Can't open output file \"%s\"", useargv[2]);
317 exit(IO_ERROR);
318 }
319 } /* Continue by opening input */
320 case 2: /* One file -> stdin */
321 /*
322 * Open input file, "-" means use stdin.
323 */
324 if (!streq(useargv[1], "-")) {
325 /* alt: if (freopen(useargv[1], "r", stdin) == NULL) { */
326 pCppIn = fopen( useargv[1], "r" );
327 if( pCppIn == NULL) {
328 perror(useargv[1]);
329 cerror("Can't open input file \"%s\"", useargv[1]);
330 exit(IO_ERROR);
331 }
332 strncpy(work, useargv[1], NWORK+1); /* Remember input filename */
333 work[NWORK] = '\0';
334 break;
335 } /* Else, just get stdin */
336 case 0: /* No args? */
337 case 1: /* No files, stdin -> stdout */
338 #if (HOST == SYS_UNIX) || (HOST == SYS_UNKNOWN)
339 work[0] = EOS; /* Unix can't find stdin name */
340 #else
341 fgetname(stdin, work); /* Vax-11C, Decus C know name */
342 #endif
343 break;
344
345 default:
346 exit(IO_ERROR); /* Can't happen */
347 }
348 /* if ( pfargv )
349 {
350 for ( j=0;j++;j < PARALIMIT )
351 {
352 if (pfargv[j]!=0)
353 free(pfargv[j]);
354 }
355 free(pfargv);
356 }
357 */
358
359 setincdirs(); /* Setup -I include directories */
360 addfile( pCppIn, work); /* "open" main input file */
361 #if OSL_DEBUG_LEVEL > 1
362 if (debug > 0 || bDumpDefs)
363 dumpdef("preset #define symbols");
364 #endif
365 if( pCppIn != stdin )
366 rewind( pCppIn );
367
368 cppmain(); /* Process main file */
369
370 if ((i = (ifptr - &ifstack[0])) != 0) {
371 #if OLD_PREPROCESSOR
372 ciwarn("Inside #ifdef block at end of input, depth = %d", i);
373 #else
374 cierror("Inside #ifdef block at end of input, depth = %d", i);
375 #endif
376 }
377 #if OSL_DEBUG_LEVEL > 1
378 if( pDefOut != stdout && pDefOut != stderr )
379 fclose( pDefOut );
380 #endif
381 if( pCppOut != stdout && pCppOut != stderr )
382 fclose( pCppOut );
383
384 if (errors > 0) {
385 fprintf(stderr, (errors == 1)
386 ? "%d error in preprocessor\n"
387 : "%d errors in preprocessor\n", errors);
388 if (!eflag)
389 exit(IO_ERROR);
390 }
391 #ifdef NOMAIN /* BP */ /* kein exit im der LIB-Version */
392 return( IO_NORMAL );
393 #else
394 exit(IO_NORMAL); /* No errors or -E option set */
395 #endif
396
397 }
398
399 FILE_LOCAL
400 void cppmain()
401 /*
402 * Main process for cpp -- copies tokens from the current input
403 * stream (main file, include file, or a macro) to the output
404 * file.
405 */
406 {
407 register int c; /* Current character */
408 register int counter; /* newlines and spaces */
409
410 /*
411 * Explicitly output a #line at the start of cpp output so
412 * that lint (etc.) knows the name of the original source
413 * file. If we don't do this explicitly, we may get
414 * the name of the first #include file instead.
415 * We also seem to need a blank line following that first #line.
416 */
417 #ifdef EVALDEFS
418 if ( !bIsInEval )
419 #endif
420 {
421 sharp();
422 PUTCHAR('\n');
423 }
424 /*
425 * This loop is started "from the top" at the beginning of each line
426 * wrongline is set TRUE in many places if it is necessary to write
427 * a #line record. (But we don't write them when expanding macros.)
428 *
429 * The counter variable has two different uses: at
430 * the start of a line, it counts the number of blank lines that
431 * have been skipped over. These are then either output via
432 * #line records or by outputting explicit blank lines.
433 * When expanding tokens within a line, the counter remembers
434 * whether a blank/tab has been output. These are dropped
435 * at the end of the line, and replaced by a single blank
436 * within lines.
437 */
438 for (;;) {
439 counter = 0; /* Count empty lines */
440 for (;;) { /* For each line, ... */
441 while (type[(c = get())] == SPA) /* Skip leading blanks */
442 ; /* in this line. */
443 if (c == '\n') /* If line's all blank, */
444 ++counter; /* Do nothing now */
445 else if (c == '#') { /* Is 1st non-space '#' */
446 keepcomments = FALSE; /* Don't pass comments */
447 counter = control(counter); /* Yes, do a #command */
448 keepcomments = (cflag && compiling);
449 }
450 else if (c == EOF_CHAR) /* At end of file? */
451 {
452 break;
453 }
454 else if (!compiling) { /* #ifdef false? */
455 skipnl(); /* Skip to newline */
456 counter++; /* Count it, too. */
457 }
458 else {
459 break; /* Actual token */
460 }
461 }
462 if (c == EOF_CHAR) /* Exit process at */
463 break; /* End of file */
464 /*
465 * If the loop didn't terminate because of end of file, we
466 * know there is a token to compile. First, clean up after
467 * absorbing newlines. counter has the number we skipped.
468 */
469 if ((wrongline && infile->fp != NULL) || counter > 4)
470 sharp(); /* Output # line number */
471 else { /* If just a few, stuff */
472 while (--counter >= 0) /* them out ourselves */
473 PUTCHAR('\n');
474 }
475 /*
476 * Process each token on this line.
477 */
478 unget(); /* Reread the char. */
479 for (;;) { /* For the whole line, */
480 do { /* Token concat. loop */
481 for (counter = 0; (type[(c = get())] == SPA);) {
482 #if COMMENT_INVISIBLE
483 if (c != COM_SEP)
484 counter++;
485 #else
486 counter++; /* Skip over blanks */
487 #endif
488 }
489 if (c == EOF_CHAR || c == '\n')
490 goto end_line; /* Exit line loop */
491 else if (counter > 0) /* If we got any spaces */
492 PUTCHAR(' '); /* Output one space */
493 c = macroid(c); /* Grab the token */
494 } while (type[c] == LET && catenate());
495 if (c == EOF_CHAR || c == '\n') /* From macro exp error */
496 goto end_line; /* Exit line loop */
497 switch (type[c]) {
498 case LET:
499 fputs(token, pCppOut); /* Quite ordinary token */
500 #ifdef EVALDEFS
501 {
502 int len;
503 if ( bIsInEval
504 && nEvalOff + (len=strlen(token)) < NEVALBUF )
505 {
506 strcpy( &EvalBuf[nEvalOff], token );
507 nEvalOff += len;
508 }
509 }
510 #endif
511 break;
512
513
514 case DIG: /* Output a number */
515 case DOT: /* Dot may begin floats */
516 #ifdef EVALDEFS
517 if ( bIsInEval )
518 scannumber(c, outputEval);
519 else
520 scannumber(c, output);
521 #else
522 scannumber(c, output);
523 #endif
524 break;
525
526 case QUO: /* char or string const */
527 scanstring(c, output); /* Copy it to output */
528 break;
529
530 default: /* Some other character */
531 cput(c); /* Just output it */
532 #ifdef EVALDEFS
533 if ( bIsInEval && nEvalOff < NEVALBUF )
534 EvalBuf[nEvalOff++] = c;
535 #endif
536 break;
537 } /* Switch ends */
538 } /* Line for loop */
539 end_line: if (c == '\n') { /* Compiling at EOL? */
540 PUTCHAR('\n'); /* Output newline, if */
541 if (infile->fp == NULL) /* Expanding a macro, */
542 wrongline = TRUE; /* Output # line later */
543 }
544 } /* Continue until EOF */
545 #ifdef EVALDEFS
546 if ( bIsInEval )
547 EvalBuf[nEvalOff++] = '\0';
548 #endif
549 }
550
551 void output(int c)
552 /*
553 * Output one character to stdout -- output() is passed as an
554 * argument to scanstring()
555 */
556 {
557 #if COMMENT_INVISIBLE
558 if (c != TOK_SEP && c != COM_SEP)
559 #else
560 if (c != TOK_SEP)
561 #endif
562 /* alt: PUTCHAR(c); */
563 PUTCHAR(c);
564 }
565
566 #ifdef EVALDEFS
567 outputEval(c)
568 int c;
569 /*
570 * Output one character to stdout -- output() is passed as an
571 * argument to scanstring()
572 */
573 {
574 #if COMMENT_INVISIBLE
575 if (c != TOK_SEP && c != COM_SEP)
576 #else
577 if (c != TOK_SEP)
578 #endif
579 /* alt: PUTCHAR(c); */
580 {
581 PUTCHAR(c);
582 if ( bIsInEval && nEvalOff < NEVALBUF )
583 EvalBuf[nEvalOff++] = c;
584 }
585 }
586 #endif
587
588
589 FILE_LOCAL
590 void sharp()
591 /*
592 * Output a line number line.
593 */
594 {
595 register char *name;
596
597 if (keepcomments) /* Make sure # comes on */
598 PUTCHAR('\n'); /* a fresh, new line. */
599 fprintf( pCppOut, "#%s %d", LINE_PREFIX, line);
600 if (infile->fp != NULL) {
601 name = (infile->progname != NULL)
602 ? infile->progname : infile->filename;
603 if (sharpfilename == NULL
604 || (sharpfilename != NULL && !streq(name, sharpfilename)) ) {
605 if (sharpfilename != NULL)
606 free(sharpfilename);
607 sharpfilename = savestring(name);
608 fprintf( pCppOut, " \"%s\"", name);
609 }
610 }
611 PUTCHAR('\n');
612 wrongline = FALSE;
613 }
614