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