/************************************************************************* * * $RCSfile: cpp2.c,v $ * * $Revision: 1.1 $ * * last change: $Author: nf $ $Date: 2001-04-18 10:31:56 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses * * - GNU Lesser General Public License Version 2.1 * - Sun Industry Standards Source License Version 1.1 * * Sun Microsystems Inc., October, 2000 * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2000 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * Sun Industry Standards Source License Version 1.1 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.1 (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.openoffice.org/license.html. * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2000 by Sun Microsystems, Inc. * * All Rights Reserved. * * Contributor(s): _______________________________________ * * ************************************************************************/ #include #include #include "cppdef.h" #include "cpp.h" #if HOST == SYS_VMS /* * Include the rms stuff. (We can't just include rms.h as it uses the * VaxC-specific library include syntax that Decus CPP doesn't support. * By including things by hand, we can CPP ourself.) */ #include #include #include #include #endif /* * Generate (by hand-inspection) a set of unique values for each control * operator. Note that this is not guaranteed to work for non-Ascii * machines. CPP won't compile if there are hash conflicts. */ #define L_assert ('a' + ('s' << 1)) #define L_define ('d' + ('f' << 1)) #define L_elif ('e' + ('i' << 1)) #define L_else ('e' + ('s' << 1)) #define L_endif ('e' + ('d' << 1)) #define L_if ('i' + (EOS << 1)) #define L_ifdef ('i' + ('d' << 1)) #define L_ifndef ('i' + ('n' << 1)) #define L_include ('i' + ('c' << 1)) #define L_line ('l' + ('n' << 1)) #define L_nogood (EOS + (EOS << 1)) /* To catch #i */ #define L_pragma ('p' + ('a' << 1)) #define L_undef ('u' + ('d' << 1)) #define L_error ('e' + ('r' << 1)) /* BP 5.3.92, #error */ #define MAXLINE 80 /* BP 5.3.92, #error */ #ifdef DEBUG #define L_debug ('d' + ('b' << 1)) /* #debug */ #define L_nodebug ('n' + ('d' << 1)) /* #nodebug */ #endif void InitCpp2() { } int control(counter) int counter; /* Pending newline counter */ /* * Process #control lines. Simple commands are processed inline, * while complex commands have their own subroutines. * * The counter is used to force out a newline before #line, and * #pragma commands. This prevents these commands from ending up at * the end of the previous line if cpp is invoked with the -C option. */ { register int c; register char *tp; register int hash; char *ep; c = skipws(); if (c == '\n' || c == EOF_CHAR) return (counter + 1); if (!isdigit(c)) scanid(c); /* Get #word to token[] */ else { unget(); /* Hack -- allow #123 as a */ strcpy(token, "line"); /* synonym for #line 123 */ } hash = (token[1] == EOS) ? L_nogood : (token[0] + (token[2] << 1)); switch (hash) { case L_assert: tp = "assert"; break; case L_define: tp = "define"; break; case L_elif: tp = "elif"; break; case L_else: tp = "else"; break; case L_endif: tp = "endif"; break; case L_if: tp = "if"; break; case L_ifdef: tp = "ifdef"; break; case L_ifndef: tp = "ifndef"; break; case L_include: tp = "include"; break; case L_line: tp = "line"; break; case L_pragma: tp = "pragma"; break; case L_undef: tp = "undef"; break; case L_error: tp = "error"; break; #ifdef DEBUG case L_debug: tp = "debug"; break; case L_nodebug: tp = "nodebug"; break; #endif default: hash = L_nogood; case L_nogood: tp = ""; break; } if (!streq(tp, token)) hash = L_nogood; /* * hash is set to a unique value corresponding to the * control keyword (or L_nogood if we think it's nonsense). */ if (infile->fp == NULL) cwarn("Control line \"%s\" within macro expansion", token); if (!compiling) { /* Not compiling now */ switch (hash) { case L_if: /* These can't turn */ case L_ifdef: /* compilation on, but */ case L_ifndef: /* we must nest #if's */ if (++ifptr >= &ifstack[BLK_NEST]) goto if_nest_err; *ifptr = 0; /* !WAS_COMPILING */ case L_line: /* Many */ /* * Are pragma's always processed? */ case L_pragma: /* options */ case L_include: /* are uninteresting */ case L_define: /* if we */ case L_undef: /* aren't */ case L_assert: /* compiling. */ case L_error: /* BP 5.3.92, #error */ dump_line: skipnl(); /* Ignore rest of line */ return (counter + 1); } } /* * Make sure that #line and #pragma are output on a fresh line. */ if (counter > 0 && (hash == L_line || hash == L_pragma)) { PUTCHAR('\n'); counter--; } switch (hash) { case L_line: /* * Parse the line to update the line number and "progname" * field and line number for the next input line. * Set wrongline to force it out later. */ c = skipws(); workp = work; /* Save name in work */ while (c != '\n' && c != EOF_CHAR) { save(c); c = get(); } unget(); save(EOS); /* * Split #line argument into and * We subtract 1 as we want the number of the next line. */ line = atoi(work) - 1; /* Reset line number */ for (tp = work; isdigit(*tp) || type[*tp] == SPA; tp++) ; /* Skip over digits */ if (*tp != EOS) { /* Got a filename, so: */ if (*tp == '"' && (ep = strrchr(tp + 1, '"')) != NULL) { tp++; /* Skip over left quote */ *ep = EOS; /* And ignore right one */ } if (infile->progname != NULL) /* Give up the old name */ free(infile->progname); /* if it's allocated. */ infile->progname = savestring(tp); } wrongline = TRUE; /* Force output later */ break; case L_include: doinclude(); break; case L_define: dodefine(); break; case L_undef: doundef(); break; case L_else: if (ifptr == &ifstack[0]) goto nest_err; else if ((*ifptr & ELSE_SEEN) != 0) goto else_seen_err; *ifptr |= ELSE_SEEN; if ((*ifptr & WAS_COMPILING) != 0) { if (compiling || (*ifptr & TRUE_SEEN) != 0) compiling = FALSE; else { compiling = TRUE; } } break; case L_elif: if (ifptr == &ifstack[0]) goto nest_err; else if ((*ifptr & ELSE_SEEN) != 0) { else_seen_err: cerror("#%s may not follow #else", token); goto dump_line; } if ((*ifptr & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING) { compiling = FALSE; /* Done compiling stuff */ goto dump_line; /* Skip this clause */ } doif(L_if); break; case L_if: case L_ifdef: case L_ifndef: if (++ifptr >= &ifstack[BLK_NEST]) if_nest_err: cfatal("Too many nested #%s statements", token); *ifptr = WAS_COMPILING; doif(hash); break; case L_endif: if (ifptr == &ifstack[0]) { nest_err: cerror("#%s must be in an #if", token); goto dump_line; } if (!compiling && (*ifptr & WAS_COMPILING) != 0) wrongline = TRUE; compiling = ((*ifptr & WAS_COMPILING) != 0); --ifptr; break; case L_assert: if (eval() == 0) cerror("Preprocessor assertion failure", NULLST); break; case L_pragma: /* * #pragma is provided to pass "options" to later * passes of the compiler. cpp doesn't have any yet. */ fprintf( pCppOut, "#pragma "); while ((c = get()) != '\n' && c != EOF_CHAR) cput(c); unget(); break; #ifdef DEBUG case L_debug: if (debug == 0) dumpdef("debug set on"); debug++; break; case L_nodebug: debug--; break; #endif case L_error: /* BP 5.3.92, #error */ { char sTxt[MAXLINE]; int i = 0; fprintf( pCppOut, "cpp: line %u, Error directive: ", line ); while ((c = get()) != '\n' && c != EOF_CHAR) cput(c); fprintf( pCppOut, "\n" ); exit( 1 ); break; } default: /* * Undefined #control keyword. * Note: the correct behavior may be to warn and * pass the line to a subsequent compiler pass. * This would allow #asm or similar extensions. */ cerror("Illegal # command \"%s\"", token); break; } if (hash != L_include) { #if OLD_PREPROCESSOR /* * Ignore the rest of the #control line so you can write * #if foo * #endif foo */ goto dump_line; /* Take common exit */ #else if (skipws() != '\n') { cwarn("Unexpected text in #control line ignored", NULLST); skipnl(); } #endif } return (counter + 1); } FILE_LOCAL doif(hash) int hash; /* * Process an #if, #ifdef, or #ifndef. The latter two are straightforward, * while #if needs a subroutine of its own to evaluate the expression. * * doif() is called only if compiling is TRUE. If false, compilation * is always supressed, so we don't need to evaluate anything. This * supresses unnecessary warnings. */ { register int c; register int found; if ((c = skipws()) == '\n' || c == EOF_CHAR) { unget(); goto badif; } if (hash == L_if) { unget(); found = (eval() != 0); /* Evaluate expr, != 0 is TRUE */ hash = L_ifdef; /* #if is now like #ifdef */ } else { if (type[c] != LET) /* Next non-blank isn't letter */ goto badif; /* ... is an error */ found = (lookid(c) != NULL); /* Look for it in symbol table */ } if (found == (hash == L_ifdef)) { compiling = TRUE; *ifptr |= TRUE_SEEN; } else { compiling = FALSE; } return; badif: cerror("#if, #ifdef, or #ifndef without an argument", NULLST); #if !OLD_PREPROCESSOR skipnl(); /* Prevent an extra */ unget(); /* Error message */ #endif return; } FILE_LOCAL doinclude() /* * Process the #include control line. * There are three variations: * #include "file" search somewhere relative to the * current source file, if not found, * treat as #include . * #include Search in an implementation-dependent * list of places. * #include token Expand the token, it must be one of * "file" or , process as such. * * Note: the November 12 draft forbids '>' in the #include format. * This restriction is unnecessary and not implemented. */ { register int c; register int delim; #if HOST == SYS_VMS char def_filename[NAM$C_MAXRSS + 1]; #endif delim = macroid(skipws()); if (delim != '<' && delim != '"') goto incerr; if (delim == '<') delim = '>'; workp = work; instring = TRUE; /* Accept all characters */ #ifdef CONTROL_COMMENTS_NOT_ALLOWED while ((c = get()) != '\n' && c != EOF_CHAR) save(c); /* Put it away. */ unget(); /* Force nl after includee */ /* * The draft is unclear if the following should be done. */ while (--workp >= work && *workp == ' ') ; /* Trim blanks from filename */ if (*workp != delim) goto incerr; #else while ((c = get()) != delim && c != EOF_CHAR) save(c); #endif *workp = EOS; /* Terminate filename */ instring = FALSE; #if HOST == SYS_VMS /* * Assume the default .h filetype. */ if (!vmsparse(work, ".H", def_filename)) { perror(work); /* Oops. */ goto incerr; } else if (openinclude(def_filename, (delim == '"'))) return; #else if (openinclude(work, (delim == '"'))) return; #endif /* * No sense continuing if #include file isn't there. */ cfatal("Cannot open include file \"%s\"", work); incerr: cerror("#include syntax error", NULLST); return; } FILE_LOCAL int openinclude(filename, searchlocal) char *filename; /* Input file name */ int searchlocal; /* TRUE if #include "file" */ /* * Actually open an include file. This routine is only called from * doinclude() above, but was written as a separate subroutine for * programmer convenience. It searches the list of directories * and actually opens the file, linking it into the list of * active files. Returns TRUE if the file was opened, FALSE * if openinclude() fails. No error message is printed. */ { register char **incptr; #if HOST == SYS_VMS #if NFWORK < (NAM$C_MAXRSS + 1) << error, NFWORK is not greater than NAM$C_MAXRSS >> #endif #endif char tmpname[NFWORK]; /* Filename work area */ if (searchlocal) { /* * Look in local directory first */ #if HOST == SYS_UNIX /* * Try to open filename relative to the directory of the current * source file (as opposed to the current directory). (ARF, SCK). */ if (filename[0] != '/' && hasdirectory(infile->filename, tmpname)) strcat(tmpname, filename); else { strcpy(tmpname, filename); } #else if (!hasdirectory(filename, tmpname) && hasdirectory(infile->filename, tmpname)) strcat(tmpname, filename); else { strcpy(tmpname, filename); } #endif if (openfile(tmpname)) return (TRUE); } /* * Look in any directories specified by -I command line * arguments, then in the builtin search list. */ for (incptr = incdir; incptr < incend; incptr++) { if (strlen(*incptr) + strlen(filename) >= (NFWORK - 1)) cfatal("Filename work buffer overflow", NULLST); else { #if HOST == SYS_UNIX if (filename[0] == '/') strcpy(tmpname, filename); else { sprintf(tmpname, "%s/%s", *incptr, filename); } #elif HOST == SYS_UNKNOWN if (filename[0] == '\\') strcpy(tmpname, filename); else { sprintf(tmpname, "%s\\%s", *incptr, filename); } #else if (!hasdirectory(filename, tmpname)) sprintf(tmpname, "%s%s", *incptr, filename); #endif if (openfile(tmpname)) return (TRUE); } } return (FALSE); } FILE_LOCAL int hasdirectory(source, result) char *source; /* Directory to examine */ char *result; /* Put directory stuff here */ /* * If a device or directory is found in the source filename string, the * node/device/directory part of the string is copied to result and * hasdirectory returns TRUE. Else, nothing is copied and it returns FALSE. */ { #if HOST == SYS_UNIX register char *tp; if ((tp = strrchr(source, '/')) == NULL) return (FALSE); else { strncpy(result, source, tp - source + 1); result[tp - source + 1] = EOS; return (TRUE); } #else #if HOST == SYS_VMS if (vmsparse(source, NULLST, result) && result[0] != EOS) return (TRUE); else { return (FALSE); } #else /* * Random DEC operating system (RSX, RT11, RSTS/E) */ register char *tp; if ((tp = strrchr(source, ']')) == NULL && (tp = strrchr(source, ':')) == NULL) return (FALSE); else { strncpy(result, source, tp - source + 1); result[tp - source + 1] = EOS; return (TRUE); } #endif #endif } #if HOST == SYS_VMS /* * EXP_DEV is set if a device was specified, EXP_DIR if a directory * is specified. (Both set indicate a file-logical, but EXP_DEV * would be set by itself if you are reading, say, SYS$INPUT:) */ #define DEVDIR (NAM$M_EXP_DEV | NAM$M_EXP_DIR) FILE_LOCAL int vmsparse(source, defstring, result) char *source; char *defstring; /* non-NULL -> default string. */ char *result; /* Size is at least NAM$C_MAXRSS + 1 */ /* * Parse the source string, applying the default (properly, using * the system parse routine), storing it in result. * TRUE if it parsed, FALSE on error. * * If defstring is NULL, there are no defaults and result gets * (just) the node::[directory] part of the string (possibly "") */ { struct FAB fab = cc$rms_fab; /* File access block */ struct NAM nam = cc$rms_nam; /* File name block */ char fullname[NAM$C_MAXRSS + 1]; register char *rp; /* Result pointer */ fab.fab$l_nam = &nam; /* fab -> nam */ fab.fab$l_fna = source; /* Source filename */ fab.fab$b_fns = strlen(source); /* Size of source */ fab.fab$l_dna = defstring; /* Default string */ if (defstring != NULLST) fab.fab$b_dns = strlen(defstring); /* Size of default */ nam.nam$l_esa = fullname; /* Expanded filename */ nam.nam$b_ess = NAM$C_MAXRSS; /* Expanded name size */ if (sys$parse(&fab) == RMS$_NORMAL) { /* Parse away */ fullname[nam.nam$b_esl] = EOS; /* Terminate string */ result[0] = EOS; /* Just in case */ rp = &result[0]; /* * Remove stuff added implicitly, accepting node names and * dev:[directory] strings (but not process-permanent files). */ if ((nam.nam$l_fnb & NAM$M_PPF) == 0) { if ((nam.nam$l_fnb & NAM$M_NODE) != 0) { strncpy(result, nam.nam$l_node, nam.nam$b_node); rp += nam.nam$b_node; *rp = EOS; } if ((nam.nam$l_fnb & DEVDIR) == DEVDIR) { strncpy(rp, nam.nam$l_dev, nam.nam$b_dev + nam.nam$b_dir); rp += nam.nam$b_dev + nam.nam$b_dir; *rp = EOS; } } if (defstring != NULLST) { strncpy(rp, nam.nam$l_name, nam.nam$b_name + nam.nam$b_type); rp += nam.nam$b_name + nam.nam$b_type; *rp = EOS; if ((nam.nam$l_fnb & NAM$M_EXP_VER) != 0) { strncpy(rp, nam.nam$l_ver, nam.nam$b_ver); rp[nam.nam$b_ver] = EOS; } } return (TRUE); } return (FALSE); } #endif