xref: /aoo4110/main/rsc/source/rscpp/cpp1.c (revision b1cdbd2c)
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 supressed 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 supresses 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                 strcpy(work, useargv[1]);  /* Remember input filename      */
333                 break;
334             }                           /* Else, just get stdin         */
335         case 0:                         /* No args?                     */
336         case 1:                         /* No files, stdin -> stdout    */
337 #if (HOST == SYS_UNIX) || (HOST == SYS_UNKNOWN)
338             work[0] = EOS;              /* Unix can't find stdin name   */
339 #else
340             fgetname(stdin, work);      /* Vax-11C, Decus C know name   */
341 #endif
342             break;
343 
344         default:
345             exit(IO_ERROR);             /* Can't happen                 */
346         }
347 /*		if ( pfargv )
348 		{
349 			for ( j=0;j++;j < PARALIMIT )
350 			{
351 				if (pfargv[j]!=0)
352 					free(pfargv[j]);
353 			}
354 			free(pfargv);
355 		}
356 */
357 
358 		setincdirs();                   /* Setup -I include directories */
359         addfile( pCppIn, work);           /* "open" main input file       */
360 #if OSL_DEBUG_LEVEL > 1
361         if (debug > 0 || bDumpDefs)
362             dumpdef("preset #define symbols");
363 #endif
364         if( pCppIn != stdin )
365             rewind( pCppIn );
366 
367         cppmain();                      /* Process main file            */
368 
369         if ((i = (ifptr - &ifstack[0])) != 0) {
370 #if OLD_PREPROCESSOR
371             ciwarn("Inside #ifdef block at end of input, depth = %d", i);
372 #else
373             cierror("Inside #ifdef block at end of input, depth = %d", i);
374 #endif
375         }
376 #if OSL_DEBUG_LEVEL > 1
377         if( pDefOut != stdout && pDefOut != stderr )
378             fclose( pDefOut );
379 #endif
380         if( pCppOut != stdout && pCppOut != stderr )
381             fclose( pCppOut );
382 
383         if (errors > 0) {
384             fprintf(stderr, (errors == 1)
385                 ? "%d error in preprocessor\n"
386                 : "%d errors in preprocessor\n", errors);
387             if (!eflag)
388                 exit(IO_ERROR);
389         }
390 #ifdef NOMAIN                  /* BP */ /* kein exit im der LIB-Version */
391         return( IO_NORMAL );
392 #else
393         exit(IO_NORMAL);                /* No errors or -E option set   */
394 #endif
395 
396 }
397 
398 FILE_LOCAL
399 void cppmain()
400 /*
401  * Main process for cpp -- copies tokens from the current input
402  * stream (main file, include file, or a macro) to the output
403  * file.
404  */
405 {
406         register int            c;              /* Current character    */
407         register int            counter;        /* newlines and spaces  */
408 
409         /*
410          * Explicitly output a #line at the start of cpp output so
411          * that lint (etc.) knows the name of the original source
412          * file.  If we don't do this explicitly, we may get
413          * the name of the first #include file instead.
414          * We also seem to need a blank line following that first #line.
415          */
416 #ifdef EVALDEFS
417 		if ( !bIsInEval )
418 #endif
419 		{
420 	        sharp();
421 	        PUTCHAR('\n');
422 		}
423         /*
424          * This loop is started "from the top" at the beginning of each line
425          * wrongline is set TRUE in many places if it is necessary to write
426          * a #line record.  (But we don't write them when expanding macros.)
427          *
428          * The counter variable has two different uses:  at
429          * the start of a line, it counts the number of blank lines that
430          * have been skipped over.  These are then either output via
431          * #line records or by outputting explicit blank lines.
432          * When expanding tokens within a line, the counter remembers
433          * whether a blank/tab has been output.  These are dropped
434          * at the end of the line, and replaced by a single blank
435          * within lines.
436          */
437         for (;;) {
438             counter = 0;                        /* Count empty lines    */
439             for (;;) {                          /* For each line, ...   */
440                 while (type[(c = get())] == SPA) /* Skip leading blanks */
441                     ;                           /* in this line.        */
442                 if (c == '\n')                  /* If line's all blank, */
443                     ++counter;                  /* Do nothing now       */
444                 else if (c == '#') {            /* Is 1st non-space '#' */
445                     keepcomments = FALSE;       /* Don't pass comments  */
446                     counter = control(counter); /* Yes, do a #command   */
447                     keepcomments = (cflag && compiling);
448                 }
449                 else if (c == EOF_CHAR)         /* At end of file?      */
450 				{
451 					break;
452 				}
453 				else if (!compiling) {          /* #ifdef false?        */
454                     skipnl();                   /* Skip to newline      */
455                     counter++;                  /* Count it, too.       */
456                 }
457                 else {
458                     break;                      /* Actual token         */
459                 }
460             }
461             if (c == EOF_CHAR)                  /* Exit process at      */
462                 break;                          /* End of file          */
463             /*
464              * If the loop didn't terminate because of end of file, we
465              * know there is a token to compile.  First, clean up after
466              * absorbing newlines.  counter has the number we skipped.
467              */
468             if ((wrongline && infile->fp != NULL) || counter > 4)
469                 sharp();                        /* Output # line number */
470             else {                              /* If just a few, stuff */
471                 while (--counter >= 0)          /* them out ourselves   */
472                     PUTCHAR('\n');
473             }
474             /*
475              * Process each token on this line.
476              */
477             unget();                            /* Reread the char.     */
478             for (;;) {                          /* For the whole line,  */
479                 do {                            /* Token concat. loop   */
480                     for (counter = 0; (type[(c = get())] == SPA);) {
481 #if COMMENT_INVISIBLE
482                         if (c != COM_SEP)
483                             counter++;
484 #else
485                         counter++;              /* Skip over blanks     */
486 #endif
487                     }
488                     if (c == EOF_CHAR || c == '\n')
489                         goto end_line;          /* Exit line loop       */
490                     else if (counter > 0)       /* If we got any spaces */
491                         PUTCHAR(' ');           /* Output one space     */
492                     c = macroid(c);             /* Grab the token       */
493                 } while (type[c] == LET && catenate());
494                 if (c == EOF_CHAR || c == '\n') /* From macro exp error */
495                     goto end_line;              /* Exit line loop       */
496                 switch (type[c]) {
497                 case LET:
498                     fputs(token, pCppOut);       /* Quite ordinary token */
499 #ifdef EVALDEFS
500 					{
501 						int len;
502 						if ( bIsInEval
503 								&& nEvalOff + (len=strlen(token)) < NEVALBUF )
504 						{
505 							strcpy( &EvalBuf[nEvalOff], token );
506 							nEvalOff += len;
507 						}
508 					}
509 #endif
510                     break;
511 
512 
513                 case DIG:                       /* Output a number      */
514                 case DOT:                       /* Dot may begin floats */
515 #ifdef EVALDEFS
516 					if ( bIsInEval )
517 	                    scannumber(c, outputEval);
518 					else
519 	                    scannumber(c, output);
520 #else
521                     scannumber(c, output);
522 #endif
523                     break;
524 
525                 case QUO:                       /* char or string const */
526                     scanstring(c, output);      /* Copy it to output    */
527                     break;
528 
529                 default:                        /* Some other character */
530                     cput(c);                    /* Just output it       */
531 #ifdef EVALDEFS
532 					if ( bIsInEval && nEvalOff < NEVALBUF )
533 						EvalBuf[nEvalOff++] = c;
534 #endif
535                     break;
536                 }                               /* Switch ends          */
537             }                                   /* Line for loop        */
538 end_line:   if (c == '\n') {                    /* Compiling at EOL?    */
539                 PUTCHAR('\n');                  /* Output newline, if   */
540                 if (infile->fp == NULL)         /* Expanding a macro,   */
541                     wrongline = TRUE;           /* Output # line later  */
542             }
543         }                                       /* Continue until EOF   */
544 #ifdef EVALDEFS
545 		if ( bIsInEval )
546 			EvalBuf[nEvalOff++] = '\0';
547 #endif
548 }
549 
550 void output(int c)
551 /*
552  * Output one character to stdout -- output() is passed as an
553  * argument to scanstring()
554  */
555 {
556 #if COMMENT_INVISIBLE
557         if (c != TOK_SEP && c != COM_SEP)
558 #else
559         if (c != TOK_SEP)
560 #endif
561 /* alt:            PUTCHAR(c); */
562             PUTCHAR(c);
563 }
564 
565 #ifdef EVALDEFS
566 outputEval(c)
567 int             c;
568 /*
569  * Output one character to stdout -- output() is passed as an
570  * argument to scanstring()
571  */
572 {
573 #if COMMENT_INVISIBLE
574         if (c != TOK_SEP && c != COM_SEP)
575 #else
576         if (c != TOK_SEP)
577 #endif
578 /* alt:            PUTCHAR(c); */
579 		{
580             PUTCHAR(c);
581 			if ( bIsInEval && nEvalOff < NEVALBUF )
582 				EvalBuf[nEvalOff++] = c;
583 		}
584 }
585 #endif
586 
587 
588 FILE_LOCAL
589 void sharp()
590 /*
591  * Output a line number line.
592  */
593 {
594         register char           *name;
595 
596         if (keepcomments)                       /* Make sure # comes on */
597             PUTCHAR('\n');                      /* a fresh, new line.   */
598         fprintf( pCppOut, "#%s %d", LINE_PREFIX, line);
599         if (infile->fp != NULL) {
600             name = (infile->progname != NULL)
601                 ? infile->progname : infile->filename;
602             if (sharpfilename == NULL
603                 || (sharpfilename != NULL && !streq(name, sharpfilename)) ) {
604                 if (sharpfilename != NULL)
605                     free(sharpfilename);
606                 sharpfilename = savestring(name);
607                 fprintf( pCppOut, " \"%s\"", name);
608              }
609         }
610         PUTCHAR('\n');
611         wrongline = FALSE;
612 }
613