diff options
Diffstat (limited to 'usr/src/lib/libpp/common/ppargs.c')
-rw-r--r-- | usr/src/lib/libpp/common/ppargs.c | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/usr/src/lib/libpp/common/ppargs.c b/usr/src/lib/libpp/common/ppargs.c new file mode 100644 index 0000000000..0aaff3905e --- /dev/null +++ b/usr/src/lib/libpp/common/ppargs.c @@ -0,0 +1,603 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1986-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * common preprocessor command line argument parse + * called by optjoin() + */ + +static const char usage[] = +"[-?\n@(#)$Id: cpp (AT&T Research) 2006-01-11 $\n]" +USAGE_LICENSE +"[+NAME?cpp - C language preprocessor]" +"[+DESCRIPTION?\bcpp\b is the preprocessor for all C language dialects. It is" +" a standalone version of the \blibpp\b(3) preprocessor library. The" +" C dialect implemented by \bcpp\b is determined by probing \bcc\b(1)" +" using \bprobe\b(1). The path of the emulated compiler can be changed" +" by the \b-D-X\b command line option.]" +"[+?If \aoutput\a is omitted then the standard output is written; if \ainput\a" +" is also omitted then the standard input is read. NOTE: this is an" +" ancient, non-standard, non-intuitiive file operand syntax that is" +" required by \bcc\b(1); use shell file name expansion at your peril.]" +"[+?\bcpp\b specific options are set by the \b-D-\b and \b-I-\b options.]" + +"[C:comments?Pass comments to the output. By default comments are omitted.]" +"[D:define?Define the macro \aname\a to have \avalue\a; \b1\b is assumed if" +" \b=\b\avalue\a is omitted. If \aname\a begins with \b:\b then it is" +" interpreted as a \blibpp\b(3) \b#pragma pp:\b statement; if \aname\a" +" begins with \b%\b then it is interpreted as a \blibpp\b(3) \b#\b" +" directive statement; if \aname\a begins with \b-\b or \b+\b then it is" +" interpreted as a \blibpp\b(3) option; \b-\b turns the option on," +" \b+\b turns it off. Most options have a \b#pragma\b counterpart that" +" is listed with the option definition. Right, this is ugly, but its the" +" only portable way to pass options through \bcc\b(1) to" +" \bcpp\b:]:[name[=value]]]{" +" [+-D-C, pp::compatibility?Preprocess for K&R compatibility.]" +" [+-D-D\alevel\a, \bpp::debug\b \alevel\a?Set the debug trace level." +" Higher levels produce more output. Levels higher than 3" +" enabled only in \b-g\b compiled versions.]" +" [+-D-F\aname\a?Set the main input file name to \aname\a. This only" +" affects error message and line sync output.]" +" [+-D-H, pp::hosted?All directories are hosted; compatibility" +" warning messages from hosted directory headers are suppressed.]" +" [+-D-I, pp::cdir?All directories contain C headers; used only with" +" \b-D-+\b.]" +" [+-D-K, pp::keyargs?Enable the non-standard \aname=value\a macro" +" argument mode.]" +" [+-D-L\b[\aid\a]], \bpp::lineid\b [\aid\a]]?Set the line sync directive" +" id to \aid\a or null if omitted.]" +" [+-D-M, pp::nomultiple?Disable multiple include detection.]" +" [+-D-P, pp::passthrough?Enable the non-standard passthrough mode; may" +" be useful for processing non-C input.]" +" [+-D-Q, pp::dump?Dump macro definitions to the output so that the" +" output may be passed through \bcpp\b again. Used for" +" generating precompiled headers.]" +" [+-D-R, pp::transition?Enable the transition preprocessing mode. Used" +" for compilers that can't make up their semantics between" +" K&R and ISO.]" +" [+-D-S, pp::strict?Enable strict preprocessing semantics and warnings." +" Works with any mode (compatibiliy, transition," +" or the default ISO).]" +" [+-D-T\atest\a, \bpp::test\b \atest\a?Enable implementation specific" +" test code according to \atest\a.]" +" [+-D-W, pp::warn?Enable pedantic warnings in non-hosted files.]" +" [+-D-X\b[\acc\a]]?Preprocess for the compiler \acc\a which must be" +" an executable path or an executable on \b$PATH\b.]" +" [+-D-Z, pp::pool?Enable pool mode. See \blibpp\b(3).]" +" [+-D-d?List canonicalized \b#define\b statements for non-predefined" +" macros in the output. ]" +" [+-D-m?List canonicalized \b#define\b statements for all macros. All" +" other output is disabled.]" +" [+-D-+, pp::plusplus?Preprocess for the C++ dialect.]" +"}" +"[I:include?Append \adirectory\a to the list of directories searched for" +" \b#include\b files. If \adirectory\a is \b-\b then: (1) \b-I\b" +" directories before \b-I-\b are searched only for \"...\" include" +" files; (2) \b-I\b directories after \b-I-\b are searched for" +" \"...\" and <...> include files; (3) the directory \b.\b is searched" +" only if it is explicitly specified by a \b-I\b option.]:?[directory]{" +" [+-I-C\adirectory\a, \bpp::cdir\b \adirectory\a?Mark \adirectory\a" +" as a C header directory. Used with \bpp:plusplus\b.]" +" [+-I-D[\afile\a]]?Read the default \bprobe\b(1) definitions from" +" \afile\a, or ignore the default definitions if \afile\a" +" is omitted.]" +" [+-I-H\adirectory\a, \bpp::hostdir\b \adirectory\a?Mark \adirectory\a" +" as a hosted directory. Headers from hosted directories have" +" compatibility warnings disabled.]" +" [+-I-I\aheader\a, \bpp::ignore\b \aheader\a?Add \aheader\a to the" +" list of ignored headers.]" +" [+-I-M\afile\a?\afile\a contains a sequence of \aheader\a" +" [= \"\amap\a\" ]] lines, where \aheader\a is either <\aname\a>" +" or \"\aname\a\", and \"\amap\a\" is an explicit binding" +" for \aheader\a. \aheader\a is ignored if = \"\amap\a\" is" +" omitted.]" +" [+-I-R\afile\a?Include \afile\a but do not emit text or line syncs.]" +" [+-I-S\adirectory\a?Add \adirectory\a to the default standard include" +" directory list.]" +" [+-I-T\afile\a?Include \afile\a and emit text to the output file.]" +"}" +"[M:dependencies?Generate \bmake\b(1) dependencies. Not needed with" +" \bnmake\b(1). \b-M\b may be followed by optional \aflags\a to change" +" dependency output styles:]{" +" [+D?Generate dependencies in a separate \b.d\b file. Preprocessed" +" output is still written to \aoutput\a, or the standard output" +" if \aoutput\a is omitted.]" +" [+G?Generate missing dependencies too.]" +" [+M?Only generate local header dependencies; \ahosted\a headers are" +" omitted. Note that \ahosted\a headers are determined by" +" \b-I-H\b and the \bpp:hosted\b and \bpp:hostdir\b pragmas;" +" no special distiction is made between \"\" and <> \binclude\b" +" styles.]" +"}" +"[P!:sync?Emit line syncs.]" +"[U:undefine?Remove the definition for the macro \aname\a.]:[name]" + +"[A:assert?Enter the assertion via \b#assert\b for system V" +" compatibility.]:[assertion]" +"[E:preprocess?Ignored for compatibility with ancient compilers.]" +"[H:include-reference?Emit \b#include\b file paths on the standard error," +" one per line, indented to show nesting.]" +"[T?If not \bgcc\b(1) then truncate identifiers to \alength\a" +" characters for compatibility with old AT&T (I guess only Lucent needs" +" them now) compilers.]#?[length]" +"[V:version?Emit the \blibpp\b(3) version.]" +"[X:argmode?Enable \aname\a=\avalue\a macro arguments for \beasel\b(1)" +" compatibility.]" +"[Y:standard?Add \adirectory\a to the list searched for" +" \b#include\b \b<...>\b files.]:[directory]" + +"\n" +"\n[ input [ output ] ]\n" +"\n" + +"[+SEE ALSO?\bcc\b(1), \bgcc\b(1), \blibpp\b(3)]" +; + +#include "pplib.h" + +#include <ctype.h> + +/* + * convert lint comments to pragmas + */ + +static void +pplint(char* head, char* comment, char* tail, int line) +{ + NoP(line); + if (strmatch(comment, "(ARGSUSED|PRINTFLIKE|PROTOLIB|SCANFLIKE|VARARGS)*([0-9])|CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY|FALLTHRU|FALLTHROUGH|LINTLIBRARY|LINTED*|NOTREACHED")) + { + strncopy(pp.token, comment, MAXTOKEN); + ppprintf("\n#%s %s:%s\n", dirname(PRAGMA), pp.pass, pp.token); + ppline(error_info.line, NiL); + } +} + +/* + * if last!=0 then argv[opt_info.index]==0 with return(0) + * else if argv[opt_info.index]==0 then return(0) + * otherwise argv[opt_info.index] is the first unrecognized + * option with return(1) + * + * use last=0 if the preprocessor is combined with other passes + * so that unknown options may be interpreted for those passes + */ + +int +ppargs(char** argv, int last) +{ + register char* s; + register int c; + register int n; + char* p; + + /* + * check the args and initialize + */ + + if (!error_info.id) + error_info.id = "cpp"; + for (;;) + { + for (; c = optget(argv, usage); last = 0) switch (c) + { + case 'C': + ppop(PP_COMMENT, ppcomment); + break; + case 'D': + /* + * this allows single arg pp option extensions + * without touching cc + * (not all cc wrappers have -W...) + */ + + switch (*(s = opt_info.arg)) + { + case '-': + case '+': + n = (*s++ == '-'); + while (c = *s++) switch (c) + { + case 'C': + ppop(PP_COMPATIBILITY, n); + break; + case 'D': + if (n && ((c = strtol(s, &p, 0)) || p != s)) + { + s = p; + n = c; + } + ppop(PP_DEBUG, -n); + break; + case 'F': + ppop(PP_FILENAME, n ? s : NiL); + goto hasarg; + case 'H': + ppop(PP_HOSTDIR, "-", n); + break; + case 'I': + ppop(PP_CDIR, "-", n); + break; + case 'K': + ppop(PP_KEYARGS, n); + break; + case 'L': + ppop(PP_LINEID, n && *s ? s : "line"); + goto hasarg; + case 'M': + ppop(PP_MULTIPLE, !n); + break; + case 'P': + ppop(PP_PASSTHROUGH, n); + break; + case 'Q': + ppop(PP_DUMP, n); + break; + case 'R': + ppop(PP_TRANSITION, n); + break; + case 'S': + ppop(PP_STRICT, n); + break; + case 'T': + ppop(PP_TEST, s); + goto hasarg; + case 'V': + ppop(PP_VENDOR, "-", n); + break; + case 'W': + ppop(PP_WARN, n); + break; + case 'X': + ppop(PP_PROBE, n && *s ? s : 0); + goto hasarg; + case 'Z': + ppop(PP_POOL, n); + break; + case 'd': + pp.option |= DEFINITIONS; + break; + case 'm': + pp.state |= NOTEXT; + pp.option |= KEEPNOTEXT|DEFINITIONS|PREDEFINITIONS; + pp.linesync = 0; + break; + case '+': + ppop(PP_PLUSPLUS, n); + break; + default: + if (pp.optarg) + { + if ((c = (*pp.optarg)(n, c, s)) > 0) goto hasarg; + else if (!c) break; + } + error(1, "%c%s: unknown -D option overload", n ? '-' : '+', s - 1); + goto hasarg; + } + hasarg: + break; + case ':': + ppop(PP_OPTION, s + 1); + break; + case '%': + ppop(PP_DIRECTIVE, s + 1); + break; + case '_': + if (strmatch(s, "__GNUC__*")) + pp.arg_style |= STYLE_gnu; + else if (strmatch(s, "__(ANSI|STDC|STRICT)__*") || !(pp.arg_style & STYLE_gnu) && strmatch(s, "__STRICT_ANSI__*")) + ppop(PP_STRICT, 1); + else if (strmatch(s, "__cplusplus*")) + ppop(PP_PLUSPLUS, 1); + /*FALLTHROUGH*/ + default: + ppop(PP_DEFINE, s); + break; + } + break; + case 'E': + /* historically ignored */ + break; + case 'I': + if (!(s = opt_info.arg)) + { + /* + * some compilers interpret `-I ...' as + * `-I-S' and arg ... while others interpret + * it as `-I...' + */ + + p = "-S"; + if ((s = argv[opt_info.index]) && ((n = *s++) == '-' || n == '+') && *s++ == 'D') + { + if (isalpha(*s) || *s == '_') + while (isalnum(*++s) || *s == '_'); + if (*s && *s != '=' && *s != '-' && *s != '+') + p = argv[opt_info.index++]; + } + s = p; + } + switch (*s) + { + case '-': + case '+': + n = *(p = s++) == '-'; + c = *s++; + if (!n && !*s) s = 0; + switch (c) + { + case 0: + ppop(PP_LOCAL); + break; + case 'C': + ppop(PP_CDIR, s, n); + break; + case 'D': + ppop(PP_DEFAULT, s); + break; + case 'H': + ppop(PP_HOSTDIR, s, n); + break; + case 'I': + ppop(PP_IGNORE, s); + break; + case 'M': + ppop(PP_IGNORELIST, s); + break; + case 'R': + ppop(PP_READ, s); + break; + case 'S': + ppop(PP_STANDARD, s); + break; + case 'T': + ppop(PP_TEXT, s); + break; + case 'V': + ppop(PP_VENDOR, s, n); + break; + default: + error(1, "%s: unknown -I option overload", p); + break; + } + break; + default: + ppop(PP_INCLUDE, s); + break; + } + break; + case 'M': + for (n = PP_deps; argv[opt_info.index]; opt_info.offset++) + { + switch (argv[opt_info.index][opt_info.offset]) + { + case 'D': + n |= PP_deps_file; + continue; + case 'G': + n |= PP_deps_generated; + continue; + case 'M': + n |= PP_deps_local; + continue; + } + break; + } + ppop(PP_FILEDEPS, n); + break; + case 'P': + ppop(PP_LINE, (PPLINESYNC)0); + break; + case 'U': + ppop(PP_UNDEF, opt_info.arg); + break; + + /* + * System V CCS compatibility + */ + + case 'A': + if (isalpha(opt_info.arg[0]) || opt_info.arg[0] == '_' || opt_info.arg[0] == '$') + ppop(PP_ASSERT, opt_info.arg); + break; + case 'H': + ppop(PP_INCREF, ppincref); + break; + case 'T': + if (!(pp.arg_style & STYLE_gnu)) + ppop(PP_TRUNCATE, TRUNCLENGTH); + /* else enable ANSI trigraphs -- default */ + break; + case 'V': + error(0, "%s", pp.version); + break; + case 'X': + pp.arg_mode = (*(opt_info.arg + 1) || pp.arg_mode && pp.arg_mode != *opt_info.arg) ? '-' : *opt_info.arg; + break; + case 'Y': + if (*(s = opt_info.arg) && *(s + 1) == ',') + { + if (*s != 'I') break; + s += 2; + } + ppop(PP_STANDARD, s); + break; + + /* + * errors + */ + + case '?': + error(ERROR_USAGE|4, "%s", opt_info.arg); + break; + case ':': + if (!last) + { + opt_info.again = 1; + return(1); + } + + /* + * cross your fingers + */ + + if (!(s = argv[opt_info.index])) + error(3, "%s", opt_info.arg); + if (opt_info.offset == 2 && (pp.arg_style & STYLE_gnu)) + { + p = argv[opt_info.index + 1]; + if (streq(s, "-$")) + { + ppop(PP_OPTION, "noid \"$\""); + goto ignore; + } + else if (streq(s, "-dD")) + { + pp.option |= DEFINITIONS; + goto ignore; + } + else if (streq(s, "-dM")) + { + pp.state |= NOTEXT; + pp.option |= KEEPNOTEXT|DEFINITIONS|PREDEFINITIONS; + pp.linesync = 0; + goto ignore; + } + else if (streq(s, "-imacros")) + { + if (p) + { + ppop(PP_READ, p); + opt_info.index++; + opt_info.offset = 0; + } + goto ignore; + } + else if (streq(s, "-include")) + { + if (p) + { + ppop(PP_TEXT, p); + opt_info.index++; + opt_info.offset = 0; + } + opt_info.offset = 0; + goto ignore; + } + else if (strneq(s, "-lang-", 6)) + { + s += 6; + if (streq(s, "c")) + c = 0; + else if (streq(s, "c++")) + c = 1; + else if (streq(s, "objc")) + c = 2; + else if (streq(s, "objc++")) + c = 3; + ppop(PP_PLUSPLUS, c & 1); + if (c & 2) + ppop(PP_DIRECTIVE, "pragma pp:map \"/#(pragma )?import>/\" \"/#(pragma )?import(.*)/__STDPP__IMPORT__(\\2)/\"\n\ +#macdef __STDPP__IMPORT__(x)\n\ +#pragma pp:noallmultiple\n\ +#include x\n\ +#pragma pp:allmultiple\n\ +#endmac"); + goto ignore; + } + else if (streq(s, "-lint")) + { + ppop(PP_COMMENT, pplint); + goto ignore; + } + } + s += opt_info.offset - 1; + if (strmatch(s, "i*.h")) + ppop((pp.arg_style & STYLE_gnu) || s[1] == '/' ? PP_READ : PP_TEXT, s + 1); + else if (strmatch(s, "*@(nostandard|nostdinc)*")) + ppop(PP_STANDARD, ""); + else if (strmatch(s, "*@(exten|xansi)*|std")) + { + ppop(PP_COMPATIBILITY, 0); + ppop(PP_TRANSITION, 1); + } + else if (strmatch(s, "*@(ansi|conform|pedantic|stand|std1|strict[!-])*")) + { + ppop(PP_COMPATIBILITY, 0); + ppop(PP_STRICT, 1); + if (strmatch(s, "*pedantic*")) + ppop(PP_PEDANTIC, 1); + } + else if (strmatch(s, "*@(trans)*")) + { + ppop(PP_COMPATIBILITY, 1); + ppop(PP_TRANSITION, 1); + } + else if (strmatch(s, "*@(classic|compat|std0|tradition|[kK][n&+][rR])*")) + { + ppop(PP_COMPATIBILITY, 1); + ppop(PP_TRANSITION, 0); + } + else if (strmatch(s, "*@(plusplus|++)*")) + ppop(PP_PLUSPLUS, 1); + else if (strmatch(s, "*@(warn)*")) + ppop(PP_WARN, 1); + + /* + * ignore unknown options + * the probe info takes care of these + * fails if an option value is in the next arg + * and this is the last option + */ + + if (argv[opt_info.index + 1] && argv[opt_info.index + 1][0] != '-' && argv[opt_info.index + 2] && argv[opt_info.index + 2][0] == '-') + { + opt_info.index++; + opt_info.offset = 0; + } + ignore: + while (argv[opt_info.index][opt_info.offset]) opt_info.offset++; + break; + } + if (!(s = argv[opt_info.index])) return(0); + switch (pp.arg_file) + { + case 0: + if (*s != '-' || *(s + 1)) ppop(PP_INPUT, s); + break; + case 1: + if (*s != '-' || *(s + 1)) ppop(PP_OUTPUT, s); + break; + default: + if (!last) return(1); + error(1, "%s: extraneous argument ignored", s); + break; + } + pp.arg_file++; + if (!argv[++opt_info.index]) return(0); + + /* + * old versions allow options after file args + */ + } +} |