| /************************************************************** |
| * |
| * 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. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include <stdio.h> |
| #include <ctype.h> |
| #include "cppdef.h" |
| #include "cpp.h" |
| |
| FILE *pCppOut = NULL; |
| FILE *pCppIn = NULL; |
| |
| #if OSL_DEBUG_LEVEL > 1 |
| FILE *pDefOut = NULL; /* ER evtl. #define's dump */ |
| #endif |
| |
| #ifdef B200 |
| /* BP, 25.07.91, einzige Moeglichkeit unter BC Stack und Heap festzusetzen */ |
| extern unsigned _stklen = 24000; |
| extern unsigned _heaplen = 30000; |
| #endif |
| |
| |
| |
| /* |
| * Commonly used global variables: |
| * line is the current input line number. |
| * wrongline is set in many places when the actual output |
| * line is out of sync with the numbering, e.g, |
| * when expanding a macro with an embedded newline. |
| * |
| * token holds the last identifier scanned (which might |
| * be a candidate for macro expansion). |
| * errors is the running cpp error counter. |
| * infile is the head of a linked list of input files (extended by |
| * #include and macros being expanded). infile always points |
| * to the current file/macro. infile->parent to the includer, |
| * etc. infile->fd is NULL if this input stream is a macro. |
| */ |
| int line; /* Current line number */ |
| int wrongline; /* Force #line to compiler */ |
| char token[IDMAX + 1]; /* Current input token */ |
| int errors; /* cpp error counter */ |
| FILEINFO *infile = NULL; /* Current input file */ |
| #if OSL_DEBUG_LEVEL > 1 |
| int debug; /* TRUE if debugging now */ |
| int bDumpDefs; /* TRUE if #define's dump req. */ |
| #ifdef EVALDEFS |
| int bIsInEval; /* TRUE if #define eval now */ |
| char EvalBuf[NEVALBUF + 1]; /* evaluation buffer */ |
| int nEvalOff = 0; /* offset to free buffer pos */ |
| #endif |
| #endif |
| /* |
| * This counter is incremented when a macro expansion is initiated. |
| * If it exceeds a built-in value, the expansion stops -- this tests |
| * for a runaway condition: |
| * #define X Y |
| * #define Y X |
| * X |
| * This can be disabled by falsifying rec_recover. (Nothing does this |
| * currently: it is a hook for an eventual invocation flag.) |
| */ |
| int recursion; /* Infinite recursion counter */ |
| int rec_recover = TRUE; /* Unwind recursive macros */ |
| |
| /* |
| * instring is set TRUE when a string is scanned. It modifies the |
| * behavior of the "get next character" routine, causing all characters |
| * to be passed to the caller (except <DEF_MAGIC>). Note especially that |
| * comments and \<newline> are not removed from the source. (This |
| * prevents cpp output lines from being arbitrarily long). |
| * |
| * inmacro is set by #define -- it absorbs comments and converts |
| * form-feed and vertical-tab to space, but returns \<newline> |
| * to the caller. Strictly speaking, this is a bug as \<newline> |
| * shouldn't delimit tokens, but we'll worry about that some other |
| * time -- it is more important to prevent infinitly long output lines. |
| * |
| * instring and inmarcor are parameters to the get() routine which |
| * were made global for speed. |
| */ |
| int instring = FALSE; /* TRUE if scanning string */ |
| int inmacro = FALSE; /* TRUE if #defining a macro */ |
| |
| /* |
| * work[] and workp are used to store one piece of text in a temporay |
| * buffer. To initialize storage, set workp = work. To store one |
| * character, call save(c); (This will fatally exit if there isn't |
| * room.) To terminate the string, call save(EOS). Note that |
| * the work buffer is used by several subroutines -- be sure your |
| * data won't be overwritten. The extra byte in the allocation is |
| * needed for string formal replacement. |
| */ |
| char work[NWORK + 1]; /* Work buffer */ |
| char *workp; /* Work buffer pointer */ |
| |
| /* |
| * keepcomments is set TRUE by the -C option. If TRUE, comments |
| * are written directly to the output stream. This is needed if |
| * the output from cpp is to be passed to lint (which uses commands |
| * embedded in comments). cflag contains the permanent state of the |
| * -C flag. keepcomments is always falsified when processing #control |
| * commands and when compilation is supressed by a false #if |
| * |
| * If eflag is set, CPP returns "success" even if non-fatal errors |
| * were detected. |
| * |
| * If nflag is non-zero, no symbols are predefined except __LINE__. |
| * __FILE__, and __DATE__. If nflag > 1, absolutely no symbols |
| * are predefined. |
| */ |
| int keepcomments = FALSE; /* Write out comments flag */ |
| int cflag = FALSE; /* -C option (keep comments) */ |
| int eflag = FALSE; /* -E option (never fail) */ |
| int nflag = 0; /* -N option (no predefines) */ |
| |
| /* |
| * ifstack[] holds information about nested #if's. It is always |
| * accessed via *ifptr. The information is as follows: |
| * WAS_COMPILING state of compiling flag at outer level. |
| * ELSE_SEEN set TRUE when #else seen to prevent 2nd #else. |
| * TRUE_SEEN set TRUE when #if or #elif succeeds |
| * ifstack[0] holds the compiling flag. It is TRUE if compilation |
| * is currently enabled. Note that this must be initialized TRUE. |
| */ |
| char ifstack[BLK_NEST] = { TRUE }; /* #if information */ |
| char *ifptr = ifstack; /* -> current ifstack[] */ |
| |
| /* |
| * incdir[] stores the -i directories (and the system-specific |
| * #include <...> directories. |
| */ |
| char *incdir[NINCLUDE]; /* -i directories */ |
| char **incend = incdir; /* -> free space in incdir[] */ |
| |
| /* |
| * This is the table used to predefine target machine and operating |
| * system designators. It may need hacking for specific circumstances. |
| * Note: it is not clear that this is part of the Ansi Standard. |
| * The -N option supresses preset definitions. |
| */ |
| char *preset[] = { /* names defined at cpp start */ |
| #ifdef MACHINE |
| MACHINE, |
| #endif |
| #ifdef SYSTEM |
| SYSTEM, |
| #endif |
| #ifdef COMPILER |
| COMPILER, |
| #endif |
| #if OSL_DEBUG_LEVEL > 1 |
| "decus_cpp", /* Ourselves! */ |
| #endif |
| NULL /* Must be last */ |
| }; |
| |
| /* |
| * The value of these predefined symbols must be recomputed whenever |
| * they are evaluated. The order must not be changed. |
| */ |
| char *magic[] = { /* Note: order is important */ |
| "__LINE__", |
| "__FILE__", |
| NULL /* Must be last */ |
| }; |
| |
| static char *sharpfilename = NULL; |
| |
| int nRunde = 0; |
| |
| void InitCpp1() |
| { |
| int i; |
| /* BP */ |
| /* in der LIB-Version muessen alle Variablen initialisiert werden */ |
| |
| line = wrongline = errors = recursion = 0; |
| for( i = 0; i < IDMAX; i++ ) |
| token[ i ] = 0; |
| |
| for( i = 0; i < NWORK; i++ ) |
| work[ i ] = 0; |
| |
| for( i = 0; i < NINCLUDE; i++ ) |
| incdir[ i ] = NULL; |
| |
| workp = NULL; |
| for( i = 0; i < BLK_NEST; i++ ) |
| ifstack[ i ] = TRUE; |
| ifptr = ifstack; |
| |
| pCppOut = stdout; |
| pCppIn = stdin; |
| #if OSL_DEBUG_LEVEL > 1 |
| debug = 0; |
| bDumpDefs = 0; |
| pDefOut = stdout; |
| #ifdef EVALDEFS |
| bIsInEval = 0; |
| for( i = 0; i < NEVALBUF; i++ ) |
| EvalBuf[ i ] = 0; |
| nEvalOff = 0; |
| #endif |
| #endif |
| rec_recover = TRUE; |
| infile = NULL; |
| instring = inmacro = keepcomments = cflag = eflag = FALSE; |
| nflag = 0; |
| incend = incdir; |
| sharpfilename = NULL; |
| /* BP */ |
| } |
| |
| int MAIN(int argc, char** argv) |
| { |
| register int i; |
| char **useargv, **pfargv; |
| |
| |
| if( nRunde == 0 ) |
| { |
| pCppIn = stdin; |
| pCppOut = stdout; |
| } |
| |
| nRunde++; |
| InitCpp1(); |
| InitCpp2(); |
| InitCpp3(); |
| InitCpp4(); |
| InitCpp5(); |
| InitCpp6(); |
| |
| #if HOST == SYS_VMS |
| argc = getredirection(argc, argv); /* vms >file and <file */ |
| #endif |
| initdefines(); /* O.S. specific def's */ |
| if ( argv[argc-1][0] == '@' ) |
| { |
| i = readoptions( argv[1], &pfargv ); /* Command file */ |
| useargv=pfargv; |
| } |
| else |
| { |
| i = dooptions(argc, argv); /* Command line -flags */ |
| useargv=argv; |
| } |
| switch (i) { |
| #if OSL_DEBUG_LEVEL > 1 |
| case 4: |
| if ( bDumpDefs ) |
| { |
| /* |
| * Get defBase file, "-" means use stdout. |
| */ |
| if (!streq(useargv[3], "-")) { |
| #if HOST == SYS_VMS |
| /* |
| * On vms, reopen stdout with "vanilla rms" attributes. |
| */ |
| if ((i = creat(useargv[3], 0, "rat=cr", "rfm=var")) == -1 |
| || dup2(i, fileno(stdout)) == -1) { |
| #else |
| /* alt if (freopen(useargv[3], "w", stdout) == NULL) { */ |
| |
| pDefOut = fopen( useargv[3], "w" ); |
| if( pDefOut == NULL ) { |
| #endif |
| perror(useargv[3]); |
| cerror("Can't open output file \"%s\"", useargv[3]); |
| exit(IO_ERROR); |
| } |
| } /* Continue by opening output */ |
| } |
| /* OSL_DEBUG_LEVEL > 1 */ |
| #endif |
| case 3: |
| /* |
| * Get output file, "-" means use stdout. |
| */ |
| if (!streq(useargv[2], "-")) { |
| #if HOST == SYS_VMS |
| /* |
| * On vms, reopen stdout with "vanilla rms" attributes. |
| */ |
| if ((i = creat(useargv[2], 0, "rat=cr", "rfm=var")) == -1 |
| || dup2(i, fileno(stdout)) == -1) { |
| #else |
| /* alt if (freopen(useargv[2], "w", stdout) == NULL) { */ |
| |
| pCppOut = fopen( useargv[2], "w" ); |
| if( pCppOut == NULL ) { |
| #endif |
| perror(useargv[2]); |
| cerror("Can't open output file \"%s\"", useargv[2]); |
| exit(IO_ERROR); |
| } |
| } /* Continue by opening input */ |
| case 2: /* One file -> stdin */ |
| /* |
| * Open input file, "-" means use stdin. |
| */ |
| if (!streq(useargv[1], "-")) { |
| /* alt: if (freopen(useargv[1], "r", stdin) == NULL) { */ |
| pCppIn = fopen( useargv[1], "r" ); |
| if( pCppIn == NULL) { |
| perror(useargv[1]); |
| cerror("Can't open input file \"%s\"", useargv[1]); |
| exit(IO_ERROR); |
| } |
| strcpy(work, useargv[1]); /* Remember input filename */ |
| break; |
| } /* Else, just get stdin */ |
| case 0: /* No args? */ |
| case 1: /* No files, stdin -> stdout */ |
| #if (HOST == SYS_UNIX) || (HOST == SYS_UNKNOWN) |
| work[0] = EOS; /* Unix can't find stdin name */ |
| #else |
| fgetname(stdin, work); /* Vax-11C, Decus C know name */ |
| #endif |
| break; |
| |
| default: |
| exit(IO_ERROR); /* Can't happen */ |
| } |
| /* if ( pfargv ) |
| { |
| for ( j=0;j++;j < PARALIMIT ) |
| { |
| if (pfargv[j]!=0) |
| free(pfargv[j]); |
| } |
| free(pfargv); |
| } |
| */ |
| |
| setincdirs(); /* Setup -I include directories */ |
| addfile( pCppIn, work); /* "open" main input file */ |
| #if OSL_DEBUG_LEVEL > 1 |
| if (debug > 0 || bDumpDefs) |
| dumpdef("preset #define symbols"); |
| #endif |
| if( pCppIn != stdin ) |
| rewind( pCppIn ); |
| |
| cppmain(); /* Process main file */ |
| |
| if ((i = (ifptr - &ifstack[0])) != 0) { |
| #if OLD_PREPROCESSOR |
| ciwarn("Inside #ifdef block at end of input, depth = %d", i); |
| #else |
| cierror("Inside #ifdef block at end of input, depth = %d", i); |
| #endif |
| } |
| #if OSL_DEBUG_LEVEL > 1 |
| if( pDefOut != stdout && pDefOut != stderr ) |
| fclose( pDefOut ); |
| #endif |
| if( pCppOut != stdout && pCppOut != stderr ) |
| fclose( pCppOut ); |
| |
| if (errors > 0) { |
| fprintf(stderr, (errors == 1) |
| ? "%d error in preprocessor\n" |
| : "%d errors in preprocessor\n", errors); |
| if (!eflag) |
| exit(IO_ERROR); |
| } |
| #ifdef NOMAIN /* BP */ /* kein exit im der LIB-Version */ |
| return( IO_NORMAL ); |
| #else |
| exit(IO_NORMAL); /* No errors or -E option set */ |
| #endif |
| |
| } |
| |
| FILE_LOCAL |
| void cppmain() |
| /* |
| * Main process for cpp -- copies tokens from the current input |
| * stream (main file, include file, or a macro) to the output |
| * file. |
| */ |
| { |
| register int c; /* Current character */ |
| register int counter; /* newlines and spaces */ |
| |
| /* |
| * Explicitly output a #line at the start of cpp output so |
| * that lint (etc.) knows the name of the original source |
| * file. If we don't do this explicitly, we may get |
| * the name of the first #include file instead. |
| * We also seem to need a blank line following that first #line. |
| */ |
| #ifdef EVALDEFS |
| if ( !bIsInEval ) |
| #endif |
| { |
| sharp(); |
| PUTCHAR('\n'); |
| } |
| /* |
| * This loop is started "from the top" at the beginning of each line |
| * wrongline is set TRUE in many places if it is necessary to write |
| * a #line record. (But we don't write them when expanding macros.) |
| * |
| * The counter variable has two different uses: at |
| * the start of a line, it counts the number of blank lines that |
| * have been skipped over. These are then either output via |
| * #line records or by outputting explicit blank lines. |
| * When expanding tokens within a line, the counter remembers |
| * whether a blank/tab has been output. These are dropped |
| * at the end of the line, and replaced by a single blank |
| * within lines. |
| */ |
| for (;;) { |
| counter = 0; /* Count empty lines */ |
| for (;;) { /* For each line, ... */ |
| while (type[(c = get())] == SPA) /* Skip leading blanks */ |
| ; /* in this line. */ |
| if (c == '\n') /* If line's all blank, */ |
| ++counter; /* Do nothing now */ |
| else if (c == '#') { /* Is 1st non-space '#' */ |
| keepcomments = FALSE; /* Don't pass comments */ |
| counter = control(counter); /* Yes, do a #command */ |
| keepcomments = (cflag && compiling); |
| } |
| else if (c == EOF_CHAR) /* At end of file? */ |
| { |
| break; |
| } |
| else if (!compiling) { /* #ifdef false? */ |
| skipnl(); /* Skip to newline */ |
| counter++; /* Count it, too. */ |
| } |
| else { |
| break; /* Actual token */ |
| } |
| } |
| if (c == EOF_CHAR) /* Exit process at */ |
| break; /* End of file */ |
| /* |
| * If the loop didn't terminate because of end of file, we |
| * know there is a token to compile. First, clean up after |
| * absorbing newlines. counter has the number we skipped. |
| */ |
| if ((wrongline && infile->fp != NULL) || counter > 4) |
| sharp(); /* Output # line number */ |
| else { /* If just a few, stuff */ |
| while (--counter >= 0) /* them out ourselves */ |
| PUTCHAR('\n'); |
| } |
| /* |
| * Process each token on this line. |
| */ |
| unget(); /* Reread the char. */ |
| for (;;) { /* For the whole line, */ |
| do { /* Token concat. loop */ |
| for (counter = 0; (type[(c = get())] == SPA);) { |
| #if COMMENT_INVISIBLE |
| if (c != COM_SEP) |
| counter++; |
| #else |
| counter++; /* Skip over blanks */ |
| #endif |
| } |
| if (c == EOF_CHAR || c == '\n') |
| goto end_line; /* Exit line loop */ |
| else if (counter > 0) /* If we got any spaces */ |
| PUTCHAR(' '); /* Output one space */ |
| c = macroid(c); /* Grab the token */ |
| } while (type[c] == LET && catenate()); |
| if (c == EOF_CHAR || c == '\n') /* From macro exp error */ |
| goto end_line; /* Exit line loop */ |
| switch (type[c]) { |
| case LET: |
| fputs(token, pCppOut); /* Quite ordinary token */ |
| #ifdef EVALDEFS |
| { |
| int len; |
| if ( bIsInEval |
| && nEvalOff + (len=strlen(token)) < NEVALBUF ) |
| { |
| strcpy( &EvalBuf[nEvalOff], token ); |
| nEvalOff += len; |
| } |
| } |
| #endif |
| break; |
| |
| |
| case DIG: /* Output a number */ |
| case DOT: /* Dot may begin floats */ |
| #ifdef EVALDEFS |
| if ( bIsInEval ) |
| scannumber(c, outputEval); |
| else |
| scannumber(c, output); |
| #else |
| scannumber(c, output); |
| #endif |
| break; |
| |
| case QUO: /* char or string const */ |
| scanstring(c, output); /* Copy it to output */ |
| break; |
| |
| default: /* Some other character */ |
| cput(c); /* Just output it */ |
| #ifdef EVALDEFS |
| if ( bIsInEval && nEvalOff < NEVALBUF ) |
| EvalBuf[nEvalOff++] = c; |
| #endif |
| break; |
| } /* Switch ends */ |
| } /* Line for loop */ |
| end_line: if (c == '\n') { /* Compiling at EOL? */ |
| PUTCHAR('\n'); /* Output newline, if */ |
| if (infile->fp == NULL) /* Expanding a macro, */ |
| wrongline = TRUE; /* Output # line later */ |
| } |
| } /* Continue until EOF */ |
| #ifdef EVALDEFS |
| if ( bIsInEval ) |
| EvalBuf[nEvalOff++] = '\0'; |
| #endif |
| } |
| |
| void output(int c) |
| /* |
| * Output one character to stdout -- output() is passed as an |
| * argument to scanstring() |
| */ |
| { |
| #if COMMENT_INVISIBLE |
| if (c != TOK_SEP && c != COM_SEP) |
| #else |
| if (c != TOK_SEP) |
| #endif |
| /* alt: PUTCHAR(c); */ |
| PUTCHAR(c); |
| } |
| |
| #ifdef EVALDEFS |
| outputEval(c) |
| int c; |
| /* |
| * Output one character to stdout -- output() is passed as an |
| * argument to scanstring() |
| */ |
| { |
| #if COMMENT_INVISIBLE |
| if (c != TOK_SEP && c != COM_SEP) |
| #else |
| if (c != TOK_SEP) |
| #endif |
| /* alt: PUTCHAR(c); */ |
| { |
| PUTCHAR(c); |
| if ( bIsInEval && nEvalOff < NEVALBUF ) |
| EvalBuf[nEvalOff++] = c; |
| } |
| } |
| #endif |
| |
| |
| FILE_LOCAL |
| void sharp() |
| /* |
| * Output a line number line. |
| */ |
| { |
| register char *name; |
| |
| if (keepcomments) /* Make sure # comes on */ |
| PUTCHAR('\n'); /* a fresh, new line. */ |
| fprintf( pCppOut, "#%s %d", LINE_PREFIX, line); |
| if (infile->fp != NULL) { |
| name = (infile->progname != NULL) |
| ? infile->progname : infile->filename; |
| if (sharpfilename == NULL |
| || (sharpfilename != NULL && !streq(name, sharpfilename)) ) { |
| if (sharpfilename != NULL) |
| free(sharpfilename); |
| sharpfilename = savestring(name); |
| fprintf( pCppOut, " \"%s\"", name); |
| } |
| } |
| PUTCHAR('\n'); |
| wrongline = FALSE; |
| } |