| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| #if defined(_MSC_VER) && (_MSC_VER > 1310) |
| #define _USE_32BIT_TIME_T |
| #endif |
| |
| #include <stdio.h> |
| #ifdef UNX |
| #include <stdlib.h> |
| #endif |
| #include <ctype.h> |
| #include "cppdef.h" |
| #include "cpp.h" |
| |
| #include "time.h" /* BP */ |
| |
| #ifndef _STRING_H |
| #include <string.h> |
| #endif |
| |
| #ifndef _NO_PROTO |
| int AddInclude( char *pIncStr ); /* BP, 11.09.91, Forward-Deklaration */ |
| #endif |
| |
| #if (OSL_DEBUG_LEVEL > 1) && (HOST == SYS_VMS || HOST == SYS_UNIX) |
| #include <signal.h> |
| #endif |
| |
| void InitCpp3() |
| { |
| } |
| |
| |
| int |
| openfile(char* filename) |
| /* |
| * Open a file, add it to the linked list of open files. |
| * This is called only from openfile() above. |
| */ |
| { |
| register FILE *fp; |
| |
| if ((fp = fopen(filename, "r")) == NULL) { |
| #if OSL_DEBUG_LEVEL > 1 |
| if ( debug || !bDumpDefs ) |
| perror(filename); |
| #endif |
| return (FALSE); |
| } |
| #if OSL_DEBUG_LEVEL > 1 |
| if (debug) |
| fprintf(stderr, "Reading from \"%s\"\n", filename); |
| #endif |
| addfile(fp, filename); |
| return (TRUE); |
| } |
| |
| void addfile(FILE* fp, char* filename) |
| /* |
| * Initialize tables for this open file. This is called from openfile() |
| * above (for #include files), and from the entry to cpp to open the main |
| * input file. It calls a common routine, getfile() to build the FILEINFO |
| * structure which is used to read characters. (getfile() is also called |
| * to setup a macro replacement.) |
| */ |
| { |
| register FILEINFO *file; |
| /* #ifndef _NO_PROTO */ |
| extern FILEINFO *getfile( int bufsize, char *filename ); /* BP */ |
| /* #endif */ |
| file = getfile(NBUFF, filename); |
| file->fp = fp; /* Better remember FILE * */ |
| file->buffer[0] = EOS; /* Initialize for first read */ |
| line = 1; /* Working on line 1 now */ |
| wrongline = TRUE; /* Force out initial #line */ |
| } |
| |
| void setincdirs() |
| /* |
| * Append system-specific directories to the include directory list. |
| * Called only when cpp is started. |
| */ |
| { |
| |
| #ifdef CPP_INCLUDE |
| *incend++ = CPP_INCLUDE; |
| #define IS_INCLUDE 1 |
| #else |
| #define IS_INCLUDE 0 |
| #endif |
| |
| #if HOST == SYS_UNIX |
| *incend++ = "/usr/include"; |
| #define MAXINCLUDE (NINCLUDE - 1 - IS_INCLUDE) |
| #endif |
| |
| #if HOST == SYS_VMS |
| extern char *getenv(); |
| |
| if (getenv("C$LIBRARY") != NULL) |
| *incend++ = "C$LIBRARY:"; |
| *incend++ = "SYS$LIBRARY:"; |
| #define MAXINCLUDE (NINCLUDE - 2 - IS_INCLUDE) |
| #endif |
| |
| #if HOST == SYS_RSX |
| extern int $$rsts; /* TRUE on RSTS/E */ |
| extern int $$pos; /* TRUE on PRO-350 P/OS */ |
| extern int $$vms; /* TRUE on VMS compat. */ |
| |
| if ($$pos) { /* P/OS? */ |
| *incend++ = "SY:[ZZDECUSC]"; /* C #includes */ |
| *incend++ = "LB:[1,5]"; /* RSX library */ |
| } |
| else if ($$rsts) { /* RSTS/E? */ |
| *incend++ = "SY:@"; /* User-defined account */ |
| *incend++ = "C:"; /* Decus-C library */ |
| *incend++ = "LB:[1,1]"; /* RSX library */ |
| } |
| else if ($$vms) { /* VMS compatibility? */ |
| *incend++ = "C:"; |
| } |
| else { /* Plain old RSX/IAS */ |
| *incend++ = "LB:[1,1]"; |
| } |
| #define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) |
| #endif |
| |
| #if HOST == SYS_RT11 |
| extern int $$rsts; /* RSTS/E emulation? */ |
| |
| if ($$rsts) |
| *incend++ = "SY:@"; /* User-defined account */ |
| *incend++ = "C:"; /* Decus-C library disk */ |
| *incend++ = "SY:"; /* System (boot) disk */ |
| #define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) |
| #endif |
| |
| #if HOST == SYS_UNKNOWN |
| /* |
| * BP: 25.07.91, Kontext: GenMake |
| * Unter DOS wird nun auch die Environment-Variable INCLUDE ausgewetet. |
| * Es kommt erschwerend hinzu, dass alle Eintraege, die mit ';' getrennt |
| * sind, mit in die Liste aufenommen werden muessen. |
| * Dies wird mit der Funktion strtok() realisiert. |
| * Vorsicht bei der Benutzung von malloc !!! |
| * In savestring wird naemlich getmem() verwendet. Vermutlich kommen sich |
| * die beiden Funktion in die Quere. Als ich malloc statt savestring |
| * verwendete knallte es in strcpy() ! |
| */ |
| |
| #if !defined( ZTC ) && !defined( WNT ) && !defined(BLC) && ! defined UNX && ! defined OS2 |
| extern char *getenv( char *pStr ); /* BP */ |
| #endif |
| char *pIncGetEnv = NULL; /* Pointer auf INCLUDE */ |
| |
| if ( ( pIncGetEnv = getenv("INCLUDE") ) != NULL ) |
| AddInclude( pIncGetEnv ); |
| |
| #define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) |
| #endif |
| |
| |
| } |
| |
| /* BP: 11.09.91, Kontext: Erweiterung des INCLUDE-Services |
| * Bislang konnte der cpp keine Include-Angaben in der Kommandozeile |
| * vertragen, bei denen die directries mit ';' getrennt wurden. |
| * Dies ist auch verstaendlich, da dieses cpp fuer UNIX-Systeme |
| * massgeschneidert wurde und in UNI die ';' als Zeichen zum Abschluss |
| * von Kommandos gilt. |
| */ |
| |
| int AddInclude( char* pIncStr ) |
| { |
| char *pIncEnv = NULL; /* Kopie des INCLUDE */ |
| char *pIncPos; /* wandert zum naechsten */ |
| |
| pIncEnv = savestring( pIncStr ); |
| pIncPos = strtok( pIncEnv, ";" ); |
| |
| while( pIncPos != NULL ) |
| { |
| if (incend >= &incdir[MAXINCLUDE]) |
| cfatal("Too many include directories", NULLST); |
| *incend++ = pIncPos; |
| pIncPos = strtok( NULL, ";" ); |
| } |
| return( 1 ); |
| } |
| |
| |
| |
| |
| int |
| dooptions(int argc, char** argv) |
| /* |
| * dooptions is called to process command line arguments (-Detc). |
| * It is called only at cpp startup. |
| */ |
| { |
| register char *ap; |
| register DEFBUF *dp; |
| register int c; |
| int i, j; |
| char *arg; |
| SIZES *sizp; /* For -S */ |
| int size; /* For -S */ |
| int isdatum; /* FALSE for -S* */ |
| int endtest; /* For -S */ |
| |
| for (i = j = 1; i < argc; i++) { |
| arg = ap = argv[i]; |
| |
| if (*ap++ != '-' || *ap == EOS) |
| { |
| argv[j++] = argv[i]; |
| } |
| else { |
| c = *ap++; /* Option byte */ |
| if (islower(c)) /* Normalize case */ |
| c = toupper(c); |
| switch (c) { /* Command character */ |
| case 'C': /* Keep comments */ |
| cflag = TRUE; |
| keepcomments = TRUE; |
| break; |
| |
| case 'D': /* Define symbol */ |
| #if HOST != SYS_UNIX |
| /* zap_uc(ap); */ /* Force define to U.C. */ |
| #endif |
| /* |
| * If the option is just "-Dfoo", make it -Dfoo=1 |
| */ |
| while (*ap != EOS && *ap != '=') |
| ap++; |
| if (*ap == EOS) |
| ap = "1"; |
| else |
| *ap++ = EOS; |
| /* |
| * Now, save the word and its definition. |
| */ |
| dp = defendel(argv[i] + 2, FALSE); |
| dp->repl = savestring(ap); |
| dp->nargs = DEF_NOARGS; |
| break; |
| |
| case 'E': /* Ignore non-fatal */ |
| eflag = TRUE; /* errors. */ |
| break; |
| |
| case 'I': /* Include directory */ |
| AddInclude( ap ); /* BP, 11.09.91 */ |
| break; |
| |
| case 'N': /* No predefineds */ |
| nflag++; /* Repeat to undefine */ |
| break; /* __LINE__, etc. */ |
| |
| case 'S': |
| sizp = size_table; |
| if (0 != (isdatum = (*ap != '*'))) /* If it's just -S, */ |
| endtest = T_FPTR; /* Stop here */ |
| else { /* But if it's -S* */ |
| ap++; /* Step over '*' */ |
| endtest = 0; /* Stop at end marker */ |
| } |
| while (sizp->bits != endtest && *ap != EOS) { |
| if (!isdigit(*ap)) { /* Skip to next digit */ |
| ap++; |
| continue; |
| } |
| size = 0; /* Compile the value */ |
| while (isdigit(*ap)) { |
| size *= 10; |
| size += (*ap++ - '0'); |
| } |
| if (isdatum) |
| sizp->size = size; /* Datum size */ |
| else |
| sizp->psize = size; /* Pointer size */ |
| sizp++; |
| } |
| if (sizp->bits != endtest) |
| cwarn("-S, too few values specified in %s", argv[i]); |
| else if (*ap != EOS) |
| cwarn("-S, too many values, \"%s\" unused", ap); |
| break; |
| |
| case 'U': /* Undefine symbol */ |
| #if HOST != SYS_UNIX |
| /* zap_uc(ap);*/ |
| #endif |
| if (defendel(ap, TRUE) == NULL) |
| cwarn("\"%s\" wasn't defined", ap); |
| break; |
| |
| #if OSL_DEBUG_LEVEL > 1 |
| case 'X': /* Debug */ |
| debug = (isdigit(*ap)) ? atoi(ap) : 1; |
| #if (HOST == SYS_VMS || HOST == SYS_UNIX) |
| signal(SIGINT, (void (*)(int)) abort); /* Trap "interrupt" */ |
| #endif |
| fprintf(stderr, "Debug set to %d\n", debug); |
| break; |
| #endif |
| |
| #if OSL_DEBUG_LEVEL > 1 |
| case 'P': /* #define's dump */ |
| bDumpDefs = 1; |
| fprintf(stderr, "Dump #define's is on\n"); |
| break; |
| #endif |
| |
| default: /* What is this one? */ |
| cwarn("Unknown option \"%s\"", arg); |
| fprintf(stderr, "The following options are valid:\n\ |
| -C\t\t\tWrite source file comments to output\n\ |
| -Dsymbol=value\tDefine a symbol with the given (optional) value\n\ |
| -Idirectory\t\tAdd a directory to the #include search list\n\ |
| -N\t\t\tDon't predefine target-specific names\n\ |
| -Stext\t\tSpecify sizes for #if sizeof\n\ |
| -Usymbol\t\tUndefine symbol\n"); |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf(stderr, " -Xvalue\t\tSet internal debug flag\n"); |
| fprintf(stderr, " -P\t\t\tdump #define's\n"); |
| #endif |
| break; |
| } /* Switch on all options */ |
| } /* If it's a -option */ |
| } /* For all arguments */ |
| #if OSL_DEBUG_LEVEL > 1 |
| if ( (bDumpDefs ? j > 4 : j > 3) ) { |
| #else |
| if (j > 3) { |
| #endif |
| cerror( |
| "Too many file arguments. Usage: cpp [input [output]]", |
| NULLST); |
| } |
| return (j); /* Return new argc */ |
| } |
| |
| int |
| readoptions(char* filename, char*** pfargv) |
| { |
| FILE *fp; |
| int c; |
| int bInQuotes = 0; |
| char optbuff[1024], *poptbuff; |
| int fargc=0, back; |
| char *fargv[PARALIMIT], **pfa; |
| |
| pfa=*pfargv=malloc(sizeof(fargv)); |
| |
| poptbuff=&optbuff[0]; |
| filename++; |
| if ((fp = fopen(filename, "r")) == NULL) { |
| #if OSL_DEBUG_LEVEL > 1 |
| if ( debug || !bDumpDefs ) |
| perror(filename); |
| #endif |
| return (FALSE); |
| } |
| do |
| { |
| /* |
| * #i27914# double ticks '"' now have a duplicate function: |
| * 1. they define a string ( e.g. -DFOO="baz" ) |
| * 2. a string can contain spaces, so -DFOO="baz zum" defines one |
| * argument no two ! |
| */ |
| c=fgetc(fp); |
| if ( c != ' ' && c != CR && c != NL && c != HT && c != EOF) |
| { |
| *poptbuff++=(char)c; |
| if( c == '"' ) |
| bInQuotes = ~bInQuotes; |
| } |
| else |
| { |
| if( c != EOF && bInQuotes ) |
| *poptbuff++=(char)c; |
| else |
| { |
| *poptbuff=EOS; |
| if (strlen(optbuff)>0) |
| { |
| pfa[fargc+1]=malloc(strlen(optbuff)+1); |
| strcpy(pfa[fargc+1],optbuff); |
| fargc++; |
| pfa[fargc+1]=0; |
| poptbuff=&optbuff[0]; |
| } |
| } |
| } |
| } |
| while ( c != EOF ); |
| |
| fclose(fp); |
| back=dooptions(fargc+1,pfa); |
| |
| return (back); |
| } |
| |
| |
| |
| #if HOST != SYS_UNIX |
| FILE_LOCAL void |
| zap_uc(char* ap) |
| /* |
| * Dec operating systems mangle upper-lower case in command lines. |
| * This routine forces the -D and -U arguments to uppercase. |
| * It is called only on cpp startup by dooptions(). |
| */ |
| { |
| while (*ap != EOS) { |
| /* |
| * Don't use islower() here so it works with Multinational |
| */ |
| if (*ap >= 'a' && *ap <= 'z') |
| *ap = (char)toupper(*ap); |
| ap++; |
| } |
| } |
| #endif |
| |
| void initdefines() |
| /* |
| * Initialize the built-in #define's. There are two flavors: |
| * #define decus 1 (static definitions) |
| * #define __FILE__ ?? (dynamic, evaluated by magic) |
| * Called only on cpp startup. |
| * |
| * Note: the built-in static definitions are suppressed by the -N option. |
| * __LINE__, __FILE__, and __DATE__ are always present. |
| */ |
| { |
| register char **pp; |
| register char *tp; |
| register DEFBUF *dp; |
| int i; |
| long tvec; |
| |
| #if !defined( ZTC ) && !defined( WNT ) && !defined(BLC) && !defined(G3) |
| extern char *ctime(); |
| #endif |
| |
| /* |
| * Predefine the built-in symbols. Allow the |
| * implementor to pre-define a symbol as "" to |
| * eliminate it. |
| */ |
| if (nflag == 0) { |
| for (pp = preset; *pp != NULL; pp++) { |
| if (*pp[0] != EOS) { |
| dp = defendel(*pp, FALSE); |
| dp->repl = savestring("1"); |
| dp->nargs = DEF_NOARGS; |
| } |
| } |
| } |
| /* |
| * The magic pre-defines (__FILE__ and __LINE__ are |
| * initialized with negative argument counts. expand() |
| * notices this and calls the appropriate routine. |
| * DEF_NOARGS is one greater than the first "magic" definition. |
| */ |
| if (nflag < 2) { |
| for (pp = magic, i = DEF_NOARGS; *pp != NULL; pp++) { |
| dp = defendel(*pp, FALSE); |
| dp->nargs = --i; |
| } |
| #if OK_DATE |
| /* |
| * Define __DATE__ as today's date. |
| */ |
| dp = defendel("__DATE__", FALSE); |
| dp->repl = tp = getmem(27); |
| dp->nargs = DEF_NOARGS; |
| time( (time_t*)&tvec); |
| *tp++ = '"'; |
| strcpy(tp, ctime((const time_t*)&tvec)); |
| tp[24] = '"'; /* Overwrite newline */ |
| #endif |
| } |
| } |
| |
| #if HOST == SYS_VMS |
| /* |
| * getredirection() is intended to aid in porting C programs |
| * to VMS (Vax-11 C) which does not support '>' and '<' |
| * I/O redirection. With suitable modification, it may |
| * useful for other portability problems as well. |
| */ |
| |
| int |
| getredirection(argc, argv) |
| int argc; |
| char **argv; |
| /* |
| * Process vms redirection arg's. Exit if any error is seen. |
| * If getredirection() processes an argument, it is erased |
| * from the vector. getredirection() returns a new argc value. |
| * |
| * Warning: do not try to simplify the code for vms. The code |
| * presupposes that getredirection() is called before any data is |
| * read from stdin or written to stdout. |
| * |
| * Normal usage is as follows: |
| * |
| * main(argc, argv) |
| * int argc; |
| * char *argv[]; |
| * { |
| * argc = getredirection(argc, argv); |
| * } |
| */ |
| { |
| register char *ap; /* Argument pointer */ |
| int i; /* argv[] index */ |
| int j; /* Output index */ |
| int file; /* File_descriptor */ |
| extern int errno; /* Last vms i/o error */ |
| |
| for (j = i = 1; i < argc; i++) { /* Do all arguments */ |
| switch (*(ap = argv[i])) { |
| case '<': /* <file */ |
| if (freopen(++ap, "r", stdin) == NULL) { |
| perror(ap); /* Can't find file */ |
| exit(errno); /* Is a fatal error */ |
| } |
| break; |
| |
| case '>': /* >file or >>file */ |
| if (*++ap == '>') { /* >>file */ |
| /* |
| * If the file exists, and is writable by us, |
| * call freopen to append to the file (using the |
| * file's current attributes). Otherwise, create |
| * a new file with "vanilla" attributes as if the |
| * argument was given as ">filename". |
| * access(name, 2) returns zero if we can write on |
| * the specified file. |
| */ |
| if (access(++ap, 2) == 0) { |
| if (freopen(ap, "a", stdout) != NULL) |
| break; /* Exit case statement */ |
| perror(ap); /* Error, can't append */ |
| exit(errno); /* After access test */ |
| } /* If file accessible */ |
| } |
| /* |
| * On vms, we want to create the file using "standard" |
| * record attributes. creat(...) creates the file |
| * using the caller's default protection mask and |
| * "variable length, implied carriage return" |
| * attributes. dup2() associates the file with stdout. |
| */ |
| if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1 |
| || dup2(file, fileno(stdout)) == -1) { |
| perror(ap); /* Can't create file */ |
| exit(errno); /* is a fatal error */ |
| } /* If '>' creation */ |
| break; /* Exit case test */ |
| |
| default: |
| argv[j++] = ap; /* Not a redirector */ |
| break; /* Exit case test */ |
| } |
| } /* For all arguments */ |
| argv[j] = NULL; /* Terminate argv[] */ |
| return (j); /* Return new argc */ |
| } |
| #endif |