xref: /trunk/main/rsc/source/rscpp/cpp1.c (revision cf09c872)
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