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