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 #include <stdio.h> 24 #ifdef UNX 25 #include <stdlib.h> 26 #endif 27 #include <ctype.h> 28 #include "cppdef.h" 29 #include "cpp.h" 30 31 #include "time.h" /* BP */ 32 33 #ifndef _STRING_H 34 #include <string.h> 35 #endif 36 37 #ifndef _NO_PROTO 38 int AddInclude( char *pIncStr ); /* BP, 11.09.91, Forward-Deklaration */ 39 #endif 40 41 #if (OSL_DEBUG_LEVEL > 1) && (HOST == SYS_VMS || HOST == SYS_UNIX) 42 #include <signal.h> 43 #endif 44 45 void InitCpp3() 46 { 47 } 48 49 50 int 51 openfile(char* filename) 52 /* 53 * Open a file, add it to the linked list of open files. 54 * This is called only from openfile() above. 55 */ 56 { 57 register FILE *fp; 58 59 if ((fp = fopen(filename, "r")) == NULL) { 60 #if OSL_DEBUG_LEVEL > 1 61 if ( debug || !bDumpDefs ) 62 perror(filename); 63 #endif 64 return (FALSE); 65 } 66 #if OSL_DEBUG_LEVEL > 1 67 if (debug) 68 fprintf(stderr, "Reading from \"%s\"\n", filename); 69 #endif 70 addfile(fp, filename); 71 return (TRUE); 72 } 73 74 void addfile(FILE* fp, char* filename) 75 /* 76 * Initialize tables for this open file. This is called from openfile() 77 * above (for #include files), and from the entry to cpp to open the main 78 * input file. It calls a common routine, getfile() to build the FILEINFO 79 * structure which is used to read characters. (getfile() is also called 80 * to setup a macro replacement.) 81 */ 82 { 83 register FILEINFO *file; 84 /* #ifndef _NO_PROTO */ 85 extern FILEINFO *getfile( int bufsize, char *filename ); /* BP */ 86 /* #endif */ 87 file = getfile(NBUFF, filename); 88 file->fp = fp; /* Better remember FILE * */ 89 file->buffer[0] = EOS; /* Initialize for first read */ 90 line = 1; /* Working on line 1 now */ 91 wrongline = TRUE; /* Force out initial #line */ 92 } 93 94 void setincdirs() 95 /* 96 * Append system-specific directories to the include directory list. 97 * Called only when cpp is started. 98 */ 99 { 100 101 #ifdef CPP_INCLUDE 102 *incend++ = CPP_INCLUDE; 103 #define IS_INCLUDE 1 104 #else 105 #define IS_INCLUDE 0 106 #endif 107 108 #if HOST == SYS_UNIX 109 *incend++ = "/usr/include"; 110 #define MAXINCLUDE (NINCLUDE - 1 - IS_INCLUDE) 111 #endif 112 113 #if HOST == SYS_VMS 114 extern char *getenv(); 115 116 if (getenv("C$LIBRARY") != NULL) 117 *incend++ = "C$LIBRARY:"; 118 *incend++ = "SYS$LIBRARY:"; 119 #define MAXINCLUDE (NINCLUDE - 2 - IS_INCLUDE) 120 #endif 121 122 #if HOST == SYS_RSX 123 extern int $$rsts; /* TRUE on RSTS/E */ 124 extern int $$pos; /* TRUE on PRO-350 P/OS */ 125 extern int $$vms; /* TRUE on VMS compat. */ 126 127 if ($$pos) { /* P/OS? */ 128 *incend++ = "SY:[ZZDECUSC]"; /* C #includes */ 129 *incend++ = "LB:[1,5]"; /* RSX library */ 130 } 131 else if ($$rsts) { /* RSTS/E? */ 132 *incend++ = "SY:@"; /* User-defined account */ 133 *incend++ = "C:"; /* Decus-C library */ 134 *incend++ = "LB:[1,1]"; /* RSX library */ 135 } 136 else if ($$vms) { /* VMS compatibility? */ 137 *incend++ = "C:"; 138 } 139 else { /* Plain old RSX/IAS */ 140 *incend++ = "LB:[1,1]"; 141 } 142 #define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) 143 #endif 144 145 #if HOST == SYS_RT11 146 extern int $$rsts; /* RSTS/E emulation? */ 147 148 if ($$rsts) 149 *incend++ = "SY:@"; /* User-defined account */ 150 *incend++ = "C:"; /* Decus-C library disk */ 151 *incend++ = "SY:"; /* System (boot) disk */ 152 #define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) 153 #endif 154 155 #if HOST == SYS_UNKNOWN 156 /* 157 * BP: 25.07.91, Kontext: GenMake 158 * Unter DOS wird nun auch die Environment-Variable INCLUDE ausgewetet. 159 * Es kommt erschwerend hinzu, dass alle Eintraege, die mit ';' getrennt 160 * sind, mit in die Liste aufenommen werden muessen. 161 * Dies wird mit der Funktion strtok() realisiert. 162 * Vorsicht bei der Benutzung von malloc !!! 163 * In savestring wird naemlich getmem() verwendet. Vermutlich kommen sich 164 * die beiden Funktion in die Quere. Als ich malloc statt savestring 165 * verwendete knallte es in strcpy() ! 166 */ 167 168 #if !defined( ZTC ) && !defined( WNT ) && !defined(BLC) && ! defined UNX && ! defined OS2 169 extern char *getenv( char *pStr ); /* BP */ 170 #endif 171 char *pIncGetEnv = NULL; /* Pointer auf INCLUDE */ 172 173 if ( ( pIncGetEnv = getenv("INCLUDE") ) != NULL ) 174 AddInclude( pIncGetEnv ); 175 176 #define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) 177 #endif 178 179 180 } 181 182 /* BP: 11.09.91, Kontext: Erweiterung des INCLUDE-Services 183 * Bislang konnte der cpp keine Include-Angaben in der Kommandozeile 184 * vertragen, bei denen die directries mit ';' getrennt wurden. 185 * Dies ist auch verstaendlich, da dieses cpp fuer UNIX-Systeme 186 * massgeschneidert wurde und in UNI die ';' als Zeichen zum Abschluss 187 * von Kommandos gilt. 188 */ 189 190 int AddInclude( char* pIncStr ) 191 { 192 char *pIncEnv = NULL; /* Kopie des INCLUDE */ 193 char *pIncPos; /* wandert zum naechsten */ 194 195 pIncEnv = savestring( pIncStr ); 196 pIncPos = strtok( pIncEnv, ";" ); 197 198 while( pIncPos != NULL ) 199 { 200 if (incend >= &incdir[MAXINCLUDE]) 201 cfatal("Too many include directories", NULLST); 202 *incend++ = pIncPos; 203 pIncPos = strtok( NULL, ";" ); 204 } 205 return( 1 ); 206 } 207 208 209 210 211 int 212 dooptions(int argc, char** argv) 213 /* 214 * dooptions is called to process command line arguments (-Detc). 215 * It is called only at cpp startup. 216 */ 217 { 218 register char *ap; 219 register DEFBUF *dp; 220 register int c; 221 int i, j; 222 char *arg; 223 SIZES *sizp; /* For -S */ 224 int size; /* For -S */ 225 int isdatum; /* FALSE for -S* */ 226 int endtest; /* For -S */ 227 228 for (i = j = 1; i < argc; i++) { 229 arg = ap = argv[i]; 230 231 if (*ap++ != '-' || *ap == EOS) 232 { 233 argv[j++] = argv[i]; 234 } 235 else { 236 c = *ap++; /* Option byte */ 237 if (islower(c)) /* Normalize case */ 238 c = toupper(c); 239 switch (c) { /* Command character */ 240 case 'C': /* Keep comments */ 241 cflag = TRUE; 242 keepcomments = TRUE; 243 break; 244 245 case 'D': /* Define symbol */ 246 #if HOST != SYS_UNIX 247 /* zap_uc(ap); */ /* Force define to U.C. */ 248 #endif 249 /* 250 * If the option is just "-Dfoo", make it -Dfoo=1 251 */ 252 while (*ap != EOS && *ap != '=') 253 ap++; 254 if (*ap == EOS) 255 ap = "1"; 256 else 257 *ap++ = EOS; 258 /* 259 * Now, save the word and its definition. 260 */ 261 dp = defendel(argv[i] + 2, FALSE); 262 dp->repl = savestring(ap); 263 dp->nargs = DEF_NOARGS; 264 break; 265 266 case 'E': /* Ignore non-fatal */ 267 eflag = TRUE; /* errors. */ 268 break; 269 270 case 'I': /* Include directory */ 271 AddInclude( ap ); /* BP, 11.09.91 */ 272 break; 273 274 case 'N': /* No predefineds */ 275 nflag++; /* Repeat to undefine */ 276 break; /* __LINE__, etc. */ 277 278 case 'S': 279 sizp = size_table; 280 if (0 != (isdatum = (*ap != '*'))) /* If it's just -S, */ 281 endtest = T_FPTR; /* Stop here */ 282 else { /* But if it's -S* */ 283 ap++; /* Step over '*' */ 284 endtest = 0; /* Stop at end marker */ 285 } 286 while (sizp->bits != endtest && *ap != EOS) { 287 if (!isdigit(*ap)) { /* Skip to next digit */ 288 ap++; 289 continue; 290 } 291 size = 0; /* Compile the value */ 292 while (isdigit(*ap)) { 293 size *= 10; 294 size += (*ap++ - '0'); 295 } 296 if (isdatum) 297 sizp->size = size; /* Datum size */ 298 else 299 sizp->psize = size; /* Pointer size */ 300 sizp++; 301 } 302 if (sizp->bits != endtest) 303 cwarn("-S, too few values specified in %s", argv[i]); 304 else if (*ap != EOS) 305 cwarn("-S, too many values, \"%s\" unused", ap); 306 break; 307 308 case 'U': /* Undefine symbol */ 309 #if HOST != SYS_UNIX 310 /* zap_uc(ap);*/ 311 #endif 312 if (defendel(ap, TRUE) == NULL) 313 cwarn("\"%s\" wasn't defined", ap); 314 break; 315 316 #if OSL_DEBUG_LEVEL > 1 317 case 'X': /* Debug */ 318 debug = (isdigit(*ap)) ? atoi(ap) : 1; 319 #if (HOST == SYS_VMS || HOST == SYS_UNIX) 320 signal(SIGINT, (void (*)(int)) abort); /* Trap "interrupt" */ 321 #endif 322 fprintf(stderr, "Debug set to %d\n", debug); 323 break; 324 #endif 325 326 #if OSL_DEBUG_LEVEL > 1 327 case 'P': /* #define's dump */ 328 bDumpDefs = 1; 329 fprintf(stderr, "Dump #define's is on\n"); 330 break; 331 #endif 332 333 default: /* What is this one? */ 334 cwarn("Unknown option \"%s\"", arg); 335 fprintf(stderr, "The following options are valid:\n\ 336 -C\t\t\tWrite source file comments to output\n\ 337 -Dsymbol=value\tDefine a symbol with the given (optional) value\n\ 338 -Idirectory\t\tAdd a directory to the #include search list\n\ 339 -N\t\t\tDon't predefine target-specific names\n\ 340 -Stext\t\tSpecify sizes for #if sizeof\n\ 341 -Usymbol\t\tUndefine symbol\n"); 342 #if OSL_DEBUG_LEVEL > 1 343 fprintf(stderr, " -Xvalue\t\tSet internal debug flag\n"); 344 fprintf(stderr, " -P\t\t\tdump #define's\n"); 345 #endif 346 break; 347 } /* Switch on all options */ 348 } /* If it's a -option */ 349 } /* For all arguments */ 350 #if OSL_DEBUG_LEVEL > 1 351 if ( (bDumpDefs ? j > 4 : j > 3) ) { 352 #else 353 if (j > 3) { 354 #endif 355 cerror( 356 "Too many file arguments. Usage: cpp [input [output]]", 357 NULLST); 358 } 359 return (j); /* Return new argc */ 360 } 361 362 int 363 readoptions(char* filename, char*** pfargv) 364 { 365 FILE *fp; 366 int c; 367 int bInQuotes = 0; 368 char optbuff[1024], *poptbuff; 369 int fargc=0, back; 370 char *fargv[PARALIMIT], **pfa; 371 372 pfa=*pfargv=malloc(sizeof(fargv)); 373 374 poptbuff=&optbuff[0]; 375 filename++; 376 if ((fp = fopen(filename, "r")) == NULL) { 377 #if OSL_DEBUG_LEVEL > 1 378 if ( debug || !bDumpDefs ) 379 perror(filename); 380 #endif 381 return (FALSE); 382 } 383 do 384 { 385 /* 386 * #i27914# double ticks '"' now have a duplicate function: 387 * 1. they define a string ( e.g. -DFOO="baz" ) 388 * 2. a string can contain spaces, so -DFOO="baz zum" defines one 389 * argument no two ! 390 */ 391 c=fgetc(fp); 392 if ( c != ' ' && c != CR && c != NL && c != HT && c != EOF) 393 { 394 *poptbuff++=(char)c; 395 if( c == '"' ) 396 bInQuotes = ~bInQuotes; 397 } 398 else 399 { 400 if( c != EOF && bInQuotes ) 401 *poptbuff++=(char)c; 402 else 403 { 404 *poptbuff=EOS; 405 if (strlen(optbuff)>0) 406 { 407 pfa[fargc+1]=malloc(strlen(optbuff)+1); 408 strcpy(pfa[fargc+1],optbuff); 409 fargc++; 410 pfa[fargc+1]=NULL; 411 poptbuff=&optbuff[0]; 412 } 413 } 414 } 415 } 416 while ( c != EOF ); 417 418 fclose(fp); 419 back=dooptions(fargc+1,pfa); 420 421 return (back); 422 } 423 424 425 426 #if HOST != SYS_UNIX 427 FILE_LOCAL void 428 zap_uc(char* ap) 429 /* 430 * Dec operating systems mangle upper-lower case in command lines. 431 * This routine forces the -D and -U arguments to uppercase. 432 * It is called only on cpp startup by dooptions(). 433 */ 434 { 435 while (*ap != EOS) { 436 /* 437 * Don't use islower() here so it works with Multinational 438 */ 439 if (*ap >= 'a' && *ap <= 'z') 440 *ap = (char)toupper(*ap); 441 ap++; 442 } 443 } 444 #endif 445 446 void initdefines() 447 /* 448 * Initialize the built-in #define's. There are two flavors: 449 * #define decus 1 (static definitions) 450 * #define __FILE__ ?? (dynamic, evaluated by magic) 451 * Called only on cpp startup. 452 * 453 * Note: the built-in static definitions are suppressed by the -N option. 454 * __LINE__, __FILE__, and __DATE__ are always present. 455 */ 456 { 457 register char **pp; 458 register char *tp; 459 register DEFBUF *dp; 460 int i; 461 time_t tvec; 462 463 #if !defined( ZTC ) && !defined( WNT ) && !defined(BLC) && !defined(G3) 464 extern char *ctime(); 465 #endif 466 467 /* 468 * Predefine the built-in symbols. Allow the 469 * implementor to pre-define a symbol as "" to 470 * eliminate it. 471 */ 472 if (nflag == 0) { 473 for (pp = preset; *pp != NULL; pp++) { 474 if (*pp[0] != EOS) { 475 dp = defendel(*pp, FALSE); 476 dp->repl = savestring("1"); 477 dp->nargs = DEF_NOARGS; 478 } 479 } 480 } 481 /* 482 * The magic pre-defines (__FILE__ and __LINE__ are 483 * initialized with negative argument counts. expand() 484 * notices this and calls the appropriate routine. 485 * DEF_NOARGS is one greater than the first "magic" definition. 486 */ 487 if (nflag < 2) { 488 for (pp = magic, i = DEF_NOARGS; *pp != NULL; pp++) { 489 dp = defendel(*pp, FALSE); 490 dp->nargs = --i; 491 } 492 #if OK_DATE 493 /* 494 * Define __DATE__ as today's date. 495 */ 496 dp = defendel("__DATE__", FALSE); 497 dp->repl = tp = getmem(27); 498 dp->nargs = DEF_NOARGS; 499 time(&tvec); 500 *tp++ = '"'; 501 strcpy(tp, ctime(&tvec)); 502 tp[24] = '"'; /* Overwrite newline */ 503 #endif 504 } 505 } 506 507 #if HOST == SYS_VMS 508 /* 509 * getredirection() is intended to aid in porting C programs 510 * to VMS (Vax-11 C) which does not support '>' and '<' 511 * I/O redirection. With suitable modification, it may 512 * useful for other portability problems as well. 513 */ 514 515 int 516 getredirection(argc, argv) 517 int argc; 518 char **argv; 519 /* 520 * Process vms redirection arg's. Exit if any error is seen. 521 * If getredirection() processes an argument, it is erased 522 * from the vector. getredirection() returns a new argc value. 523 * 524 * Warning: do not try to simplify the code for vms. The code 525 * presupposes that getredirection() is called before any data is 526 * read from stdin or written to stdout. 527 * 528 * Normal usage is as follows: 529 * 530 * main(argc, argv) 531 * int argc; 532 * char *argv[]; 533 * { 534 * argc = getredirection(argc, argv); 535 * } 536 */ 537 { 538 register char *ap; /* Argument pointer */ 539 int i; /* argv[] index */ 540 int j; /* Output index */ 541 int file; /* File_descriptor */ 542 extern int errno; /* Last vms i/o error */ 543 544 for (j = i = 1; i < argc; i++) { /* Do all arguments */ 545 switch (*(ap = argv[i])) { 546 case '<': /* <file */ 547 if (freopen(++ap, "r", stdin) == NULL) { 548 perror(ap); /* Can't find file */ 549 exit(errno); /* Is a fatal error */ 550 } 551 break; 552 553 case '>': /* >file or >>file */ 554 if (*++ap == '>') { /* >>file */ 555 /* 556 * If the file exists, and is writable by us, 557 * call freopen to append to the file (using the 558 * file's current attributes). Otherwise, create 559 * a new file with "vanilla" attributes as if the 560 * argument was given as ">filename". 561 * access(name, 2) returns zero if we can write on 562 * the specified file. 563 */ 564 if (access(++ap, 2) == 0) { 565 if (freopen(ap, "a", stdout) != NULL) 566 break; /* Exit case statement */ 567 perror(ap); /* Error, can't append */ 568 exit(errno); /* After access test */ 569 } /* If file accessible */ 570 } 571 /* 572 * On vms, we want to create the file using "standard" 573 * record attributes. creat(...) creates the file 574 * using the caller's default protection mask and 575 * "variable length, implied carriage return" 576 * attributes. dup2() associates the file with stdout. 577 */ 578 if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1 579 || dup2(file, fileno(stdout)) == -1) { 580 perror(ap); /* Can't create file */ 581 exit(errno); /* is a fatal error */ 582 } /* If '>' creation */ 583 break; /* Exit case test */ 584 585 default: 586 argv[j++] = ap; /* Not a redirector */ 587 break; /* Exit case test */ 588 } 589 } /* For all arguments */ 590 argv[j] = NULL; /* Terminate argv[] */ 591 return (j); /* Return new argc */ 592 } 593 #endif 594