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 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 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