Files
aboba/gpp/gpp.c
2025-06-09 14:42:51 +02:00

3268 lines
101 KiB
C

/* File: gpp.c -- generic preprocessor
** Author: Denis Auroux, Tristan Miller
** Contact: tristan@logological.org
**
** Copyright (C) 1996, 1999, 2001 Denis Auroux
** Copyright (C) 2003-2023 Tristan Miller
**
** This program is free software: you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program 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 program. If not, see <http://www.gnu.org/licenses/>.
*/
/* To compile under MS VC++, one must define WIN_NT */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef WIN_NT /* WIN NT settings */
#define popen _popen
#define pclose _pclose
#define my_strdup _strdup
#define my_strcasecmp _stricmp
#define SLASH '\\'
#define DEFAULT_CRLF 1
#else /* UNIX settings */
#define SLASH '/'
#define DEFAULT_CRLF 0
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#if HAVE_FNMATCH_H
# include <fnmatch.h>
#endif
#include <time.h>
#define STACKDEPTH 50
#define MAXARGS 100
#define MAXINCL 128 /* max # of include dirs */
#define MAX_GPP_NUM_SIZE 15
#define MAX_GPP_DATE_SIZE 1024
typedef struct MODE {
char *mStart; /* before macro name */
char *mEnd; /* end macro without arg */
char *mArgS; /* start 1st argument */
char *mArgSep; /* separate arguments */
char *mArgE; /* end last argument */
char *mArgRef; /* how to refer to arguments in a def */
char quotechar; /* quote next char */
char *stackchar; /* characters to stack */
char *unstackchar; /* characters to unstack */
} MODE;
/* translation for delimiters :
\001 = \b = ' ' = one or more spaces \201 = \!b = non-space
\002 = \w = zero or more spaces
\003 = \B = one or more spaces or \n \203 = \!B = non-space nor \n
\004 = \W = zero or more spaces or \n
\005 = \a = alphabetic (a-z, A-Z) \205 = \!a = non-alphabetic
\006 = \A = alphabetic or space/\n \206 = \!A
\007 = \# = numeric (0-9) \207 = \!#
\010 = \i = identifier (a-zA-Z0-9_) \210 = \!i
\011 = \t, \012 = \n \211 = \!t, \212 = \!n
\013 = \o = operator (+-*\/^<>=`~:.?@#&!%|) \213 = \!o
\014 = \O = operator or ()[]{} \214 = \!O
*/
/* st end args sep arge ref quot stk unstk*/
struct MODE CUser = {"", "", "(", ",", ")", "#", '\\', "(", ")" };
struct MODE CMeta = {"#", "\n", "\001","\001","\n","#", '\\', "(", ")" };
struct MODE KUser = {"", "", "(", ",", ")", "#", 0, "(", ")" };
struct MODE KMeta = {"\n#\002","\n", "\001","\001","\n","#", 0, "", "" };
struct MODE Tex = {"\\", "", "{", "}{", "}", "#", '@', "{", "}" };
struct MODE Html = {"<#", ">", "\003","|", ">", "#", '\\', "<", ">" };
struct MODE XHtml = {"<#", "/>", "\003","|", "/>","#", '\\', "<", ">" };
#define DEFAULT_OP_STRING (unsigned char *)"+-*/\\^<>=`~:.?@#&!%|"
#define PROLOG_OP_STRING (unsigned char *)"+-*/\\^<>=`~:.?@#&"
#define DEFAULT_OP_PLUS (unsigned char *)"()[]{}"
#define DEFAULT_ID_STRING (unsigned char *)"\005\007_" /* or equiv. "A-Za-z0-9_" */
/* here we assume that longs are at least 32 bit... if not, change this ! */
#define LOG_LONG_BITS 5
#define CHARSET_SUBSET_LEN (256 >> LOG_LONG_BITS)
typedef unsigned long *CHARSET_SUBSET;
CHARSET_SUBSET DefaultOp, DefaultExtOp, PrologOp, DefaultId;
typedef struct COMMENT {
char *start; /* how the comment/string starts */
char *end; /* how it ends */
char quote; /* how to prevent it from ending */
char warn; /* a character that shouldn't be in there */
int flags[3]; /* meta, user, text */
struct COMMENT *next;
} COMMENT;
#define OUTPUT_TEXT 0x1 /* what's inside will be output */
#define OUTPUT_DELIM 0x2 /* the delimiters will be output */
#define PARSE_MACROS 0x4 /* macros inside will be parsed */
#define FLAG_IGNORE 0x40
#define FLAG_STRING (OUTPUT_TEXT|OUTPUT_DELIM)
#define FLAG_COMMENT 0
#define FLAG_META 0
#define FLAG_USER 1
#define FLAG_TEXT 2
/* Some stuff I removed because it made for some impossible situations :
#define PARSE_COMMENTS 0x8
comments inside comments will not be parsed because nesting comments is
too complicated (syntax conflicts, esp. to find a comment's end)
-- of course, unless the comment is ignored.
#define MACRO_FRIENDLY 0x20
a comment-end is to be processed even if an unfinished macro call has
started inside the comment, otherwise it's too hard do decide in advance
where a comment ends. In particular foo('bar((((') is valid.
#define PREVENT_DELIM 0x10
all comments will prevent macro delimitation, i.e. foo('bar) is invalid.
-- of course, unless the comment is ignored.
Too bad, #define foo '... terminates only at following "'".
Unless one adds quotechars like in #define foo \' ...
ALSO NOTE : comments are not allowed before the end of the first argument
to a meta-macro. E.g. this is legal : #define foo <* blah *> 3
This is not legal : #define <* blah *> foo 3
If a comment occurs here, the behavior depends on the actual meta-macro :
most will yield an error and stop gpp (#define, #undef, #ifdef/ifndef,
#defeval, #include, #mode) ; #exec, #if and #eval should be ok ;
#ifeq will always fail while #ifneq will always succeed ;
*/
typedef struct SPECS {
struct MODE User, Meta;
struct COMMENT *comments;
struct SPECS *stack_next;
int preservelf;
CHARSET_SUBSET op_set, ext_op_set, id_set;
} SPECS;
struct SPECS *S;
typedef struct MACRO {
char *username, *macrotext, **argnames;
int macrolen, nnamedargs;
struct SPECS *define_specs;
int defined_in_comment;
} MACRO;
struct MACRO *macros;
int nmacros, nalloced;
char *includedir[MAXINCL];
int nincludedirs;
int execallowed;
int dosmode;
int autoswitch;
/* must be a format-like string that has % % % in it.
The first % is replaced with line number, the second with "filename", and
the third with 1, 2 or blank
Can also use ? instead of %.
*/
char *include_directive_marker = NULL;
short WarningLevel = 2;
/* controls if standard dirs, like /usr/include, are to be searched for
#include and whether the current dir is to be searched first or last. */
int NoStdInc = 0;
int NoCurIncFirst = 0;
int CurDirIncLast = 0;
int file_and_stdout = 0;
char *IncludeFile = NULL;
typedef struct OUTPUTCONTEXT {
char *buf;
int len, bufsize;
FILE *f;
} OUTPUTCONTEXT;
typedef struct INPUTCONTEXT {
char *buf;
char *malloced_buf; /* what was actually malloc-ed (buf may have shifted) */
int len, bufsize;
int lineno;
char *filename;
FILE *in;
int argc;
char **argv;
char **namedargs;
struct OUTPUTCONTEXT *out;
int eof;
int in_comment;
int ambience; /* FLAG_TEXT, FLAG_USER or FLAG_META */
int may_have_args;
} INPUTCONTEXT;
struct INPUTCONTEXT *C;
int commented[STACKDEPTH], iflevel;
/* commented = 0: output, 1: not output,
2: not output because we're in a #elif and we've already gone through
the right case (so #else/#elif can't toggle back to output) */
int parselevel;
void ProcessContext(void); /* the main loop */
int findIdent(const char *b, int l);
void delete_macro(int i);
/* various recent additions */
void usage(void);
void display_version(void);
void bug(const char *s);
void warning(const char *s);
static void getDirname(const char *fname, char *dirname);
static FILE *openInCurrentDir(const char *incfile);
char *ArithmEval(int pos1, int pos2);
void replace_definition_with_blank_lines(const char *start, const char *end,
int skip);
void replace_directive_with_blank_line(FILE *file);
void write_include_marker(FILE *f, int lineno, char *filename,
const char *marker);
void construct_include_directive_marker(char **include_directive_marker,
const char *includemarker_input);
void escape_backslashes(const char *instr, char **outstr);
static void DoInclude(char *file_name, int ignore_nonexistent);
/*
** strdup() and my_strcasecmp() are not ANSI C, so here we define our own
** versions in case the compiler does not support them
*/
#if ! HAVE_STRDUP
inline char *my_strdup(const char *s);
inline char *my_strdup(const char *s) {
size_t len = strlen(s) + 1;
char *newstr = malloc(len);
return newstr ? (char *) memcpy(newstr, s, len) : NULL ;
}
#else
# undef my_strdup
# define my_strdup strdup
#endif
#if ! HAVE_STRCASECMP
int my_strcasecmp(const char *s, const char *s2) {
do {
char c1 = tolower(*s);
char c2 = tolower(*s2);
if (c1 > c2)
return 1;
if (c1 < c2)
return -1;
} while (*s++ && *s2++);
return 0;
}
#else
# undef my_strcasecmp
# define my_strcasecmp strcasecmp
#endif
void bug(const char *s) {
fprintf(stderr, "%s:%d: error: %s\n", C->filename, C->lineno, s);
exit(EXIT_FAILURE);
}
void warning(const char *s) {
fprintf(stderr, "%s:%d: warning: %s\n", C->filename, C->lineno, s);
}
struct SPECS *CloneSpecs(const struct SPECS *Q) {
struct SPECS *P;
struct COMMENT *x, *y;
P = malloc(sizeof *P);
if (P == NULL )
bug("Out of memory.");
memcpy(P, Q, sizeof(struct SPECS));
P->stack_next = NULL;
if (Q->comments != NULL )
P->comments = malloc(sizeof *(P->comments));
for (x = Q->comments, y = P->comments; x != NULL ;
x = x->next, y = y->next) {
memcpy(y, x, sizeof(struct COMMENT));
y->start = my_strdup(x->start);
y->end = my_strdup(x->end);
if (x->next != NULL )
y->next = malloc(sizeof *(y->next));
}
return P;
}
void FreeComments(struct SPECS *Q) {
struct COMMENT *p;
while (Q && Q->comments != NULL ) {
p = Q->comments;
Q->comments = p->next;
free(p->start);
free(p->end);
free(p);
}
}
void PushSpecs(const struct SPECS *X) {
struct SPECS *P;
P = CloneSpecs(X);
P->stack_next = S;
S = P;
}
void PopSpecs(void) {
struct SPECS *P;
P = S;
S = P->stack_next;
FreeComments(P);
free(P);
if (S == NULL )
bug("#mode restore without #mode save");
}
void display_version(void) {
printf("gpp\n");
printf("Copyright (C) 1996-2001 Denis Auroux\n");
printf("Copyright (C) 2003-2020 Tristan Miller\n");
printf("This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
}
void usage(void) {
printf("Usage : gpp [-{o|O} outfile] [-I/include/path] [-Dname=val ...] [-z] [-x] [-m]\n");
printf(" [-n] [-C | -T | -H | -X | -P | -U ... [-M ...]] [+c<n> str1 str2]\n");
printf(" [+s<n> str1 str2 c] [long options] [infile]\n\n");
printf(" default: #define x y macro(arg,...)\n");
printf(" -C : maximum cpp compatibility (includes -n, +c, +s, ...)\n");
printf(" -T : TeX-like \\define{x}{y} \\macro{arg}{...}\n");
printf(" -H : HTML-like <#define x|y> <#macro arg|...>\n");
printf(" -X : XHTML-like <#define x|y/> <#macro arg|.../>\n");
printf(" -P : prolog compatible cpp-like mode\n");
printf(" -U : user-defined syntax (specified in 9 following args; see manual)\n");
printf(" -M : user-defined syntax for meta-macros (specified in 7 following args)\n\n");
printf(" -o : output to outfile\n");
printf(" -O : output to outfile and stdout\n");
printf(" -z : line terminator is CR-LF (MS-DOS style)\n");
printf(" -x : enable #exec built-in macro\n");
printf(" -m : enable automatic mode switching upon including .h/.c files\n");
printf(" -n : send LF characters serving as macro terminators to output\n");
printf(" +c : use next 2 args as comment start and comment end sequences\n");
printf(" +s : use next 3 args as string start, end and quote character\n\n");
printf(" Long options:\n");
printf(" --include file : process file before infile\n");
printf(" --nostdinc : don't search standard directories for files to include\n");
printf(" --nocurinc : don't search the current directory for files to include\n");
printf(" --curdirinclast : search the current directory last\n");
printf(" --warninglevel n : set warning level\n");
printf(" --includemarker formatstring : keep track of #include directives in output\n\n");
printf(" --version : display version information and exit\n");
printf(" -h, --help : display this message and exit\n\n");
}
int isDelim(unsigned char c) {
if (c >= 128)
return 0;
if ((c >= '0') && (c <= '9'))
return 0;
if ((c >= 'A') && (c <= 'Z'))
return 0;
if ((c >= 'a') && (c <= 'z'))
return 0;
if (c == '_')
return 0;
return 1;
}
int isWhite(char c) {
if (c == ' ')
return 1;
if (c == '\t')
return 1;
if (c == '\n')
return 1;
return 0;
}
void newmacro(const char *s, int len, int hasspecs) {
if (nmacros == nalloced) {
nalloced = 2 * nalloced + 1;
macros = realloc(macros, nalloced * sizeof *macros);
if (macros == NULL )
bug("Out of memory");
}
macros[nmacros].username = malloc(len + 1);
strncpy(macros[nmacros].username, s, len);
macros[nmacros].username[len] = 0;
macros[nmacros].argnames = NULL;
macros[nmacros].nnamedargs = 0;
macros[nmacros].defined_in_comment = 0;
if (hasspecs)
macros[nmacros].define_specs = CloneSpecs(S);
else
macros[nmacros].define_specs = NULL;
}
void lookupArgRefs(int n) {
int i, l;
char *p;
if (macros[n].argnames != NULL )
return; /* don't mess with those */
macros[n].nnamedargs = -1;
l = strlen(S->User.mArgRef);
for (i = 0, p = macros[n].macrotext; i < macros[n].macrolen; i++, p++) {
if ((*p != 0) && (*p == S->User.quotechar)) {
i++;
p++;
} else if (!strncmp(p, S->User.mArgRef, l))
if ((p[l] >= '1') && (p[l] <= '9')) {
macros[n].nnamedargs = 0;
return;
}
}
}
char *strNl0(const char *s) /* replace "\\n" by "\n" in a cmd-line arg */
{
char *t, *u;
t = malloc(strlen(s) + 1);
u = t;
while (*s != 0) {
if ((*s == '\\') && (s[1] == 'n')) {
*u = '\n';
s++;
} else
*u = *s;
s++;
u++;
}
*u = 0;
return t;
}
char *strNl(const char *s) /* the same but with whitespace specifier handling */
{
char *t, *u;
int neg;
t = malloc(strlen(s) + 1);
u = t;
if (!isDelim(*s))
bug("character not allowed to start a syntax specifier");
while (*s != 0) {
if (((*s & 0x60) == 0) && (*s != '\n') && (*s != '\t'))
bug("character not allowed in syntax specifier");
if (*s == '\\') {
neg = (s[1] == '!');
switch (s[neg + 1]) {
case 'n':
case 'r':
*u = '\n';
break;
case 't':
*u = '\t';
break;
case 'b': /* one or more spaces */
*u = '\001';
break;
case 'w': /* zero or more spaces */
if (neg)
bug("\\w and \\W cannot be negated");
*u = '\002';
break;
case 'B': /* one or more spaces or \n */
*u = '\003';
break;
case 'W': /* zero or more spaces or \n */
if (neg)
bug("\\w and \\W cannot be negated");
*u = '\004';
break;
case 'a': /* alphabetic */
*u = '\005';
break;
case 'A': /* alphabetic + space */
*u = '\006';
break;
case '#': /* numeric */
*u = '\007';
break;
case 'i': /* identifier */
*u = '\010';
break;
case 'o': /* operator */
*u = '\013';
break;
case 'O': /* operator/parenthesis */
*u = '\014';
break;
default:
*u = '\\';
neg = -1;
}
if (neg > 0)
*u += (char) 128;
s += neg + 1;
} else if (*s == ' ')
*u = '\001';
else
*u = *s;
s++;
u++;
}
*u = 0;
return t;
}
/* same as strnl() but for C strings & in-place */
char *strNl2(char *s, int check_delim) {
char *u;
int neg;
u = s;
if (check_delim && !isDelim(*s))
bug("character not allowed to start a syntax specifier");
while (*s != '"') {
if (((*s & 0x60) == 0) && (*s != '\n') && (*s != '\t'))
bug("character not allowed in syntax specifier");
if (*s == '\\') {
neg = (s[1] == '!');
switch (s[neg + 1]) {
case 'n':
case 'r':
*u = '\n';
break;
case 't':
*u = '\t';
break;
case 'b': /* one or more spaces */
*u = '\001';
break;
case 'w': /* zero or more spaces */
if (neg)
bug("\\w and \\W cannot be negated");
*u = '\002';
break;
case 'B': /* one or more spaces or \n */
*u = '\003';
break;
case 'W': /* zero or more spaces or \n */
if (neg)
bug("\\w and \\W cannot be negated");
*u = '\004';
break;
case 'a': /* alphabetic */
*u = '\005';
break;
case 'A': /* alphabetic + space */
*u = '\006';
break;
case '#': /* numeric */
*u = '\007';
break;
case 'i': /* identifier */
*u = '\010';
break;
case 'o': /* operator */
*u = '\013';
break;
case 'O': /* operator/parenthesis */
*u = '\014';
break;
case '"':
case '\\':
if (!neg) {
*u = s[1];
break;
}
default:
bug("unknown escape sequence in syntax specifier");
}
if (neg > 0)
*u += (char) 128;
s += neg + 1;
} else if (*s == ' ')
*u = '\001';
else
*u = *s;
if (*s == 0)
bug("unterminated string in #mode command");
s++;
u++;
}
*u = 0;
return (s + 1);
}
int isWhitesep(const char *s) {
while (isWhite(*s) || (*s == '\001') || (*s == '\002') || (*s == '\003')
|| (*s == '\004'))
s++;
return (*s == 0);
}
int nowhite_strcmp(char *s, char *t) {
char *p;
while (isWhite(*s))
s++;
while (isWhite(*t))
t++;
if ((*s == 0) || (*t == 0))
return strcmp(s, t);
p = s + strlen(s) - 1;
while (isWhite(*p))
*(p--) = 0;
p = t + strlen(t) - 1;
while (isWhite(*p))
*(p--) = 0;
return strcmp(s, t);
}
void parseCmdlineDefine(const char *s) {
int l, i, argc;
for (l = 0; s[l] && (s[l] != '=') && (s[l] != '('); l++)
;
i = findIdent(s, l);
if (i >= 0)
delete_macro(i);
newmacro(s, l, 0);
/* possibly allow named arguments: -Dmacro(arg1,arg2)=... (no spaces) */
if (s[l] == '(') {
argc = 0;
do {
l++;
i = l;
while (!isDelim(s[i]))
i++;
if (s[i] != ',' && s[i] != ')')
bug("invalid syntax in -D declaration");
if (i > l)
argc++;
macros[nmacros].argnames = realloc(macros[nmacros].argnames,
(argc + 1) * sizeof(char *));
if (i > l) {
macros[nmacros].argnames[argc - 1] = malloc(i - l + 1);
memcpy(macros[nmacros].argnames[argc - 1], s + l, i - l);
macros[nmacros].argnames[argc - 1][i - l] = 0;
}
l = i;
} while (s[l] != ')');
l++;
macros[nmacros].nnamedargs = argc;
macros[nmacros].argnames[argc] = NULL;
}
/* the macro definition afterwards ! */
if (s[l] == '=')
l++;
else if (s[l] != 0)
bug("invalid syntax in -D declaration");
macros[nmacros].macrolen = strlen(s + l);
macros[nmacros++].macrotext = my_strdup(s + l);
}
int readModeDescription(char **args, struct MODE *mode, int ismeta) {
if (!(*(++args)))
return 0;
mode->mStart = strNl(*args);
if (!(*(++args)))
return 0;
mode->mEnd = strNl(*args);
if (!(*(++args)))
return 0;
mode->mArgS = strNl(*args);
if (!(*(++args)))
return 0;
mode->mArgSep = strNl(*args);
if (!(*(++args)))
return 0;
mode->mArgE = strNl(*args);
if (!(*(++args)))
return 0;
mode->stackchar = strNl(*args);
if (!(*(++args)))
return 0;
mode->unstackchar = strNl(*args);
if (ismeta)
return 1;
if (!(*(++args)))
return 0;
mode->mArgRef = strNl(*args);
if (!(*(++args)))
return 0;
mode->quotechar = **args;
return 1;
}
int parse_comment_specif(char c) {
switch (c) {
case 'I':
case 'i':
return FLAG_IGNORE;
case 'c':
return FLAG_COMMENT;
case 's':
return FLAG_STRING;
case 'q':
return OUTPUT_TEXT;
case 'S':
return FLAG_STRING | PARSE_MACROS;
case 'Q':
return OUTPUT_TEXT | PARSE_MACROS;
case 'C':
return FLAG_COMMENT | PARSE_MACROS;
default:
bug("Invalid comment/string modifier");
return 0;
}
}
void add_comment(struct SPECS *S, const char *specif, char *start, char *end,
char quote, char warn) {
struct COMMENT *p;
if (*start == 0)
bug("Comment/string start delimiter must be non-empty");
for (p = S->comments; p != NULL ; p = p->next)
if (!strcmp(p->start, start)) {
if (strcmp(p->end, end)) /* already exists with a different end */
bug("Conflicting comment/string delimiter specifications");
free(p->start);
free(p->end);
break;
}
if (p == NULL ) {
p = malloc(sizeof *p);
p->next = S->comments;
S->comments = p;
}
p->start = start;
p->end = end;
p->quote = quote;
p->warn = warn;
if (strlen(specif) != 3)
bug("Invalid comment/string modifier");
p->flags[FLAG_META] = parse_comment_specif(specif[0]);
p->flags[FLAG_USER] = parse_comment_specif(specif[1]);
p->flags[FLAG_TEXT] = parse_comment_specif(specif[2]);
}
void delete_comment(struct SPECS *S, char *start) {
struct COMMENT *p, *q;
q = NULL;
for (p = S->comments; p != NULL ; p = p->next) {
if (!strcmp(p->start, start)) {
if (q == NULL )
S->comments = p->next;
else
q->next = p->next;
free(p->start);
free(p->end);
free(p);
free(start);
return;
} else
q = p;
}
free(start);
}
void outchar(char c) {
if (C->out->bufsize) {
if (C->out->len + 1 == C->out->bufsize) {
C->out->bufsize = C->out->bufsize * 2;
C->out->buf = realloc(C->out->buf, C->out->bufsize);
if (C->out->buf == NULL )
bug("Out of memory");
}
C->out->buf[C->out->len++] = c;
} else {
if (dosmode && (c == 10)) {
fputc(13, C->out->f);
if (file_and_stdout)
fputc(13, stdout);
}
if (c != 13) {
fputc(c, C->out->f);
if (file_and_stdout)
fputc(c, stdout);
}
}
}
void sendout(const char *s, int l, int proc) /* only process the quotechar, that's all */
{
int i;
if (!commented[iflevel])
for (i = 0; i < l; i++) {
if (proc && (s[i] != 0) && (s[i] == S->User.quotechar)) {
i++;
if (i == l)
return;
}
if (s[i] != 0)
outchar(s[i]);
}
else
replace_definition_with_blank_lines(s, s + l - 1, 0);
}
void extendBuf(int pos) {
char *p;
if (C->bufsize <= pos) {
C->bufsize += pos; /* approx double */
p = malloc(C->bufsize);
memcpy(p, C->buf, C->len);
free(C->malloced_buf);
C->malloced_buf = C->buf = p;
if (C->buf == NULL )
bug("Out of memory");
}
}
char getChar(int pos) {
static int lastchar = -666;
int c;
if (lastchar == -666 && !strcmp(S->Meta.mEnd, "\n"))
lastchar = '\n';
if (C->in == NULL ) {
if (pos >= C->len)
return 0;
else
return C->buf[pos];
}
extendBuf(pos);
while (pos >= C->len) {
do {
c = fgetc(C->in);
} while (c == 13);
if (lastchar == '\n')
C->lineno++;
lastchar = c;
if (c == EOF)
c = 0;
C->buf[C->len++] = (char) c;
}
return C->buf[pos];
}
int whiteout(int *pos1, int *pos2) /* remove whitespace on both sides */
{
while ((*pos1 < *pos2) && isWhite(getChar(*pos1)))
(*pos1)++;
while ((*pos1 < *pos2) && isWhite(getChar(*pos2 - 1)))
(*pos2)--;
return (*pos1 < *pos2);
}
int identifierEnd(int start) {
char c;
c = getChar(start);
if (c == 0)
return start;
if (c == S->User.quotechar) {
c = getChar(start + 1);
if (c == 0)
return (start + 1);
if (isDelim(c))
return (start + 2);
start += 2;
c = getChar(start);
}
while (!isDelim(c))
c = getChar(++start);
return start;
}
int iterIdentifierEnd(int start) {
int x;
while (1) {
x = identifierEnd(start);
if (x == start)
return x;
start = x;
}
}
int IsInCharset(CHARSET_SUBSET x, int c) {
return (x[c >> LOG_LONG_BITS] & 1L << (c & ((1 << LOG_LONG_BITS) - 1))) != 0;
}
int matchSequence(const char *s, int *pos) {
int i = *pos;
int match;
char c;
while (*s != 0) {
if (!((*s) & 0x60)) { /* special sequences */
match = 1;
switch ((*s) & 0x1f) {
case '\001':
c = getChar(i++);
if ((c != ' ') && (c != '\t')) {
match = 0;
break;
}
case '\002':
i--;
do {
c = getChar(++i);
} while ((c == ' ') || (c == '\t'));
break;
case '\003':
c = getChar(i++);
if ((c != ' ') && (c != '\t') && (c != '\n')) {
match = 0;
break;
}
case '\004':
i--;
do {
c = getChar(++i);
} while ((c == ' ') || (c == '\t') || (c == '\n'));
break;
case '\006':
c = getChar(i++);
match = ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))
|| (c == ' ') || (c == '\t') || (c == '\n');
break;
case '\005':
c = getChar(i++);
match = ((c >= 'a') && (c <= 'z'))
|| ((c >= 'A') && (c <= 'Z'));
break;
case '\007':
c = getChar(i++);
match = ((c >= '0') && (c <= '9'));
break;
case '\010':
c = getChar(i++);
match = IsInCharset(S->id_set, c);
break;
case '\011':
c = getChar(i++);
match = (c == '\t');
break;
case '\012':
c = getChar(i++);
match = (c == '\n');
break;
case '\013':
c = getChar(i++);
match = IsInCharset(S->op_set, c);
break;
case '\014':
c = getChar(i++);
match = IsInCharset(S->ext_op_set, c)
|| IsInCharset(S->op_set, c);
break;
}
if ((*s) & 0x80)
match = !match;
if (!match)
return 0;
} else if (getChar(i++) != *s)
return 0;
s++;
}
*pos = i;
return 1;
}
int matchEndSequence(const char *s, int *pos) {
if (*s == 0)
return 1;
/* if terminator is \n and we're at end of input, let it be... */
if (getChar(*pos) == 0 && s[0] == '\n' && s[1] == 0)
return 1;
if (!matchSequence(s, pos))
return 0;
if (S->preservelf && isWhite(getChar(*pos - 1)))
(*pos)--;
return 1;
}
int matchStartSequence(const char *s, int *pos) {
char c;
int match;
if (!((*s) & 0x60)) { /* special sequences from prev. context */
c = getChar(*pos - 1);
match = 1;
if (*s == 0)
return 1;
switch ((*s) & 0x1f) {
case '\001':
if ((c != ' ') && (c != '\t')) {
match = 0;
break;
}
case '\002':
break;
case '\003':
if ((c != ' ') && (c != '\t') && (c != '\n')) {
match = 0;
break;
}
case '\004':
break;
case '\006':
if ((c == ' ') || (c == '\t') || (c == '\n'))
break;
case '\005':
match = ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
break;
case '\007':
match = ((c >= '0') && (c <= '9'));
break;
case '\010':
match = IsInCharset(S->id_set, c);
break;
case '\011':
match = (c == '\t');
break;
case '\012':
match = (c == '\n');
break;
case '\013':
match = IsInCharset(S->op_set, c);
break;
case '\014':
match = IsInCharset(S->ext_op_set, c) || IsInCharset(S->op_set, c);
break;
}
if ((*s) & 0x80)
match = !match;
if (!match)
return 0;
s++;
}
return matchSequence(s, pos);
}
void AddToCharset(CHARSET_SUBSET x, int c) {
x[c >> LOG_LONG_BITS] |= 1L << (c & ((1 << LOG_LONG_BITS) - 1));
}
CHARSET_SUBSET MakeCharsetSubset(unsigned char *s) {
CHARSET_SUBSET x;
int i;
unsigned char c;
x = malloc(CHARSET_SUBSET_LEN * sizeof(unsigned long));
for (i = 0; i < CHARSET_SUBSET_LEN; i++)
x[i] = 0;
while (*s != 0) {
if (!((*s) & 0x60)) { /* special sequences */
if ((*s) & 0x80)
bug(
"negated special sequences not allowed in charset specifications");
switch ((*s) & 0x1f) {
case '\002': /* \w, \W, \i, \o, \O not allowed */
case '\004':
case '\010':
case '\013':
case '\014':
bug("special sequence not allowed in charset specification");
case '\003':
AddToCharset(x, '\n');
case '\001':
AddToCharset(x, ' ');
case '\011':
AddToCharset(x, '\t');
break;
case '\006':
AddToCharset(x, '\n');
AddToCharset(x, ' ');
AddToCharset(x, '\t');
case '\005':
for (c = 'A'; c <= 'Z'; c++)
AddToCharset(x, c);
for (c = 'a'; c <= 'z'; c++)
AddToCharset(x, c);
break;
case '\007':
for (c = '0'; c <= '9'; c++)
AddToCharset(x, c);
break;
case '\012':
AddToCharset(x, '\n');
break;
}
} else if ((s[1] == '-') && ((s[2] & 0x60) != 0) && (s[2] >= *s)) {
for (c = *s; c <= s[2]; c++)
AddToCharset(x, c);
s += 2;
} else
AddToCharset(x, *s);
s++;
}
return x;
}
int idequal(const char *b, int l, const char *s) {
int i;
if ((int) strlen(s) != l)
return 0;
for (i = 0; i < l; i++)
if (b[i] != s[i])
return 0;
return 1;
}
int findIdent(const char *b, int l) {
int i;
for (i = 0; i < nmacros; i++)
if (idequal(b, l, macros[i].username))
return i;
return -1;
}
int findNamedArg(const char *b, int l) {
char *s;
int i;
for (i = 0;; i++) {
s = C->namedargs[i];
if (s == NULL )
return -1;
if (idequal(b, l, s))
return i;
}
}
void shiftIn(int l) {
int i;
if (l <= 1)
return;
l--;
if (l >= C->len)
C->len = 0;
else {
if (C->len - l > 100) { /* we want to shrink that buffer */
C->buf += l;
C->bufsize -= l;
} else
for (i = l; i < C->len; i++)
C->buf[i - l] = C->buf[i];
C->len -= l;
C->eof = (C->buf[0] == 0);
}
if (C->len <= 1) {
if (C->in == NULL )
C->eof = 1;
else
C->eof = feof(C->in);
}
}
void initthings(int argc, char **argv) {
char **arg, *s;
int i, isinput, isoutput, ishelp, ismode, hasmeta, usrmode;
DefaultOp = MakeCharsetSubset(DEFAULT_OP_STRING);
PrologOp = MakeCharsetSubset(PROLOG_OP_STRING);
DefaultExtOp = MakeCharsetSubset(DEFAULT_OP_PLUS);
DefaultId = MakeCharsetSubset(DEFAULT_ID_STRING);
nmacros = 0;
nalloced = 31;
macros = malloc(nalloced * sizeof *macros);
S = malloc(sizeof *S);
S->User = CUser;
S->Meta = CMeta;
S->comments = NULL;
S->stack_next = NULL;
S->preservelf = 0;
S->op_set = DefaultOp;
S->ext_op_set = DefaultExtOp;
S->id_set = DefaultId;
C = malloc(sizeof *C);
C->in = stdin;
C->argc = 0;
C->argv = NULL;
C->filename = my_strdup("stdin");
C->out = malloc(sizeof *(C->out));
C->out->f = stdout;
C->out->bufsize = 0;
C->lineno = 1;
isinput = isoutput = ismode = ishelp = hasmeta = usrmode = 0;
nincludedirs = 0;
C->bufsize = 80;
C->len = 0;
C->buf = C->malloced_buf = malloc(C->bufsize);
C->eof = 0;
C->namedargs = NULL;
C->in_comment = 0;
C->ambience = FLAG_TEXT;
C->may_have_args = 0;
commented[0] = 0;
iflevel = 0;
execallowed = 0;
autoswitch = 0;
dosmode = DEFAULT_CRLF;
for (arg = argv + 1; *arg; arg++) {
if (strcmp(*arg, "--help") == 0 || strcmp(*arg, "-h") == 0) {
usage();
exit(EXIT_SUCCESS);
}
if (strcmp(*arg, "--version") == 0) {
display_version();
exit(EXIT_SUCCESS);
}
#define DEPRECATED_WARNING fprintf(stderr, "gpp: warning: deprecated option `%s'; use `-%s' instead\n", *arg, *arg)
if (strcmp(*arg, "-nostdinc") == 0) {
DEPRECATED_WARNING;
NoStdInc = 1;
continue;
}
if (strcmp(*arg, "-nocurinc") == 0) {
DEPRECATED_WARNING;
NoCurIncFirst = 1;
continue;
}
if (strcmp(*arg, "-curdirinclast") == 0) {
DEPRECATED_WARNING;
CurDirIncLast = 1;
NoCurIncFirst = 1;
continue;
}
if (strcmp(*arg, "-includemarker") == 0) {
DEPRECATED_WARNING;
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
construct_include_directive_marker(&include_directive_marker, *arg);
continue;
}
if (strcmp(*arg, "--include") == 0) {
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
IncludeFile = *arg;
continue;
}
if (strcmp(*arg, "-warninglevel") == 0) {
DEPRECATED_WARNING;
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
WarningLevel = atoi(*arg);
continue;
}
if (strcmp(*arg, "--nostdinc") == 0) {
NoStdInc = 1;
continue;
}
if (strcmp(*arg, "--nocurinc") == 0) {
NoCurIncFirst = 1;
continue;
}
if (strcmp(*arg, "--curdirinclast") == 0) {
CurDirIncLast = 1;
NoCurIncFirst = 1;
continue;
}
if (strcmp(*arg, "--includemarker") == 0) {
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
construct_include_directive_marker(&include_directive_marker, *arg);
continue;
}
if (strcmp(*arg, "--warninglevel") == 0) {
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
WarningLevel = atoi(*arg);
continue;
}
if (**arg == '+') {
switch ((*arg)[1]) {
case 'c':
s = (*arg) + 2;
if (*s == 0)
s = "ccc";
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
add_comment(S, s, strNl(*(arg - 1)), strNl(*arg), 0, 0);
break;
case 's':
s = (*arg) + 2;
if (*s == 0)
s = "sss";
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
add_comment(S, s, strNl(*(arg - 2)), strNl(*(arg - 1)), **arg,
0);
break;
case 'z':
dosmode = 0;
break;
case 'n':
S->preservelf = 0;
break;
default:
ishelp = 1;
}
} else if (**arg != '-') {
ishelp |= isinput;
isinput = 1;
C->in = fopen(*arg, "r");
free(C->filename);
C->filename = my_strdup(*arg);
if (C->in == NULL )
bug("Cannot open input file");
} else
switch ((*arg)[1]) {
case 'I':
if (nincludedirs == MAXINCL)
bug("too many include directories");
if ((*arg)[2] == 0) {
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
includedir[nincludedirs++] = my_strdup(*arg);
} else
includedir[nincludedirs++] = my_strdup((*arg) + 2);
break;
case 'C':
ishelp |= ismode | hasmeta | usrmode;
ismode = 1;
S->User = KUser;
S->Meta = KMeta;
S->preservelf = 1;
add_comment(S, "ccc", my_strdup("/*"), my_strdup("*/"), 0, 0);
add_comment(S, "ccc", my_strdup("//"), my_strdup("\n"), 0, 0);
add_comment(S, "ccc", my_strdup("\\\n"), my_strdup(""), 0, 0);
add_comment(S, "sss", my_strdup("\""), my_strdup("\""), '\\',
'\n');
add_comment(S, "sss", my_strdup("'"), my_strdup("'"), '\\',
'\n');
break;
case 'P':
ishelp |= ismode | hasmeta | usrmode;
ismode = 1;
S->User = KUser;
S->Meta = KMeta;
S->preservelf = 1;
S->op_set = PrologOp;
add_comment(S, "css", my_strdup("\213/*"), my_strdup("*/"), 0,
0); /* \!o */
add_comment(S, "cii", my_strdup("\\\n"), my_strdup(""), 0, 0);
add_comment(S, "css", my_strdup("%"), my_strdup("\n"), 0, 0);
add_comment(S, "sss", my_strdup("\""), my_strdup("\""), 0,
'\n');
add_comment(S, "sss", my_strdup("\207'"), my_strdup("'"), 0,
'\n'); /* \!# */
break;
case 'T':
ishelp |= ismode | hasmeta | usrmode;
ismode = 1;
S->User = S->Meta = Tex;
break;
case 'H':
ishelp |= ismode | hasmeta | usrmode;
ismode = 1;
S->User = S->Meta = Html;
break;
case 'X':
ishelp |= ismode | hasmeta | usrmode;
ismode = 1;
S->User = S->Meta = XHtml;
break;
case 'U':
ishelp |= ismode | usrmode;
usrmode = 1;
if (!readModeDescription(arg, &(S->User), 0)) {
usage();
exit(EXIT_FAILURE);
}
arg += 9;
if (!hasmeta)
S->Meta = S->User;
break;
case 'M':
ishelp |= ismode | hasmeta;
hasmeta = 1;
if (!readModeDescription(arg, &(S->Meta), 1)) {
usage();
exit(EXIT_FAILURE);
}
arg += 7;
break;
case 'O':
file_and_stdout = 1;
case 'o':
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
ishelp |= isoutput;
isoutput = 1;
C->out->f = fopen(*arg, "w");
if (C->out->f == NULL )
bug("Cannot create output file");
break;
case 'D':
if ((*arg)[2] == 0) {
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
s = strNl0(*arg);
} else
s = strNl0((*arg) + 2);
parseCmdlineDefine(s);
free(s);
break;
case 'x':
execallowed = 1;
break;
case 'n':
S->preservelf = 1;
break;
case 'z':
dosmode = 1;
break;
case 'c':
case 's':
if (!(*(++arg))) {
usage();
exit(EXIT_FAILURE);
}
delete_comment(S, strNl(*arg));
break;
case 'm':
autoswitch = 1;
break;
default:
ishelp = 1;
}
if (hasmeta && !usrmode) {
usage();
exit(EXIT_FAILURE);
}
if (ishelp) {
usage();
exit(EXIT_FAILURE);
}
}
#ifndef WIN_NT
if ((nincludedirs == 0) && !NoStdInc) {
includedir[0] = my_strdup("/usr/include");
nincludedirs = 1;
}
#endif
for (i = 0; i < nmacros; i++) {
if (macros[i].define_specs == NULL )
macros[i].define_specs = CloneSpecs(S);
lookupArgRefs(i); /* for macro aliasing */
}
}
int findCommentEnd(const char *endseq, char quote, char warn, int pos,
int flags) {
int i;
char c;
while (1) {
c = getChar(pos);
i = pos;
if (matchEndSequence(endseq, &i))
return pos;
if (c == 0)
bug("Input ended while scanning a comment/string");
if (c == warn) {
warn = 0;
if (WarningLevel > 1)
warning("possible comment/string termination problem");
}
if (c == quote)
pos += 2;
else if ((flags & PARSE_MACROS) && (c == S->User.quotechar))
pos += 2;
else
pos++;
}
}
void SkipPossibleComments(int *pos, int cmtmode, int silentonly) {
int found;
struct COMMENT *c;
if (C->in_comment)
return;
do {
found = 0;
if (getChar(*pos) == 0)
return; /* EOF */
for (c = S->comments; c != NULL ; c = c->next)
if (!(c->flags[cmtmode] & FLAG_IGNORE))
if (!silentonly || (c->flags[cmtmode] == FLAG_COMMENT))
if (matchStartSequence(c->start, pos)) {
*pos = findCommentEnd(c->end, c->quote, c->warn, *pos,
c->flags[cmtmode]);
matchEndSequence(c->end, pos);
found = 1;
break;
}
} while (found);
}
/* look for a possible user macro.
Input : idstart = scan start
idcheck = check id for long macro forms before splicing args ?
cmtmode = comment mode (FLAG_META or FLAG_USER)
Output : idstart/idend = macro name location
sh_end/lg_end = macro form end (-1 if no match)
argb/arge = argument locations for long form
argc = argument count for long form
id = macro id, if idcheck was set at input
*/
int SplicePossibleUser(int *idstart, int *idend, int *sh_end, int *lg_end,
int *argb, int *arge, int *argc, int idcheck, int *id, int cmtmode) {
int match, k, pos;
if (!matchStartSequence(S->User.mStart, idstart))
return 0;
*idend = identifierEnd(*idstart);
if ((*idend) && !getChar(*idend - 1))
return 0;
/* look for args or no args */
*sh_end = *idend;
if (!matchEndSequence(S->User.mEnd, sh_end))
*sh_end = -1;
pos = *idend;
match = matchSequence(S->User.mArgS, &pos);
if (idcheck) {
*id = findIdent(C->buf + *idstart, *idend - *idstart);
if (*id < 0)
match = 0;
}
*lg_end = -1;
if (match) {
*argc = 0;
while (1) {
if (*argc >= MAXARGS)
bug("too many macro parameters");
argb[*argc] = pos;
k = 0;
while (1) { /* look for mArgE, mArgSep, or comment-start */
pos = iterIdentifierEnd(pos);
SkipPossibleComments(&pos, cmtmode, 0);
if (getChar(pos) == 0)
return (*sh_end >= 0); /* EOF */
if (strchr(S->User.stackchar, getChar(pos)))
k++;
if (k) {
if (strchr(S->User.unstackchar, getChar(pos)))
k--;
} else {
arge[*argc] = pos;
if (matchSequence(S->User.mArgSep, &pos)) {
match = 0;
break;
}
if (matchEndSequence(S->User.mArgE, &pos)) {
match = 1;
break;
}
}
pos++; /* nothing matched, go forward */
}
(*argc)++;
if (match) { /* no more args */
*lg_end = pos;
break;
}
}
}
return ((*lg_end >= 0) || (*sh_end >= 0));
}
int findMetaArgs(int start, int *p1b, int *p1e, int *p2b, int *p2e, int *endm,
int *argc, int *argb, int *arge) {
int pos, k;
int hyp_end1, hyp_end2;
/* look for mEnd or mArgS */
pos = start;
if (!matchSequence(S->Meta.mArgS, &pos)) {
if (!matchEndSequence(S->Meta.mEnd, &pos))
return -1;
*endm = pos;
return 0;
}
*p1b = pos;
/* special syntax for #define : 1st arg is a macro call */
if ((*argc)
&& SplicePossibleUser(&pos, p1e, &hyp_end1, &hyp_end2, argb, arge,
argc, 0, NULL, FLAG_META)) {
*p1b = pos;
if (hyp_end2 >= 0)
pos = hyp_end2;
else {
pos = hyp_end1;
*argc = 0;
}
if (!matchSequence(S->Meta.mArgSep, &pos)) {
if (!matchEndSequence(S->Meta.mArgE, &pos))
bug(
"#define/#defeval requires an identifier or a single macro call");
*endm = pos;
return 1;
}
} else {
*argc = 0;
k = 0;
while (1) { /* look for mArgE, mArgSep, or comment-start */
pos = iterIdentifierEnd(pos);
SkipPossibleComments(&pos, FLAG_META, 0);
if (getChar(pos) != 0 && strchr(S->Meta.stackchar, getChar(pos)))
k++;
if (k) {
if (getChar(pos) != 0
&& strchr(S->Meta.unstackchar, getChar(pos)))
k--;
} else {
*p1e = pos;
if (matchSequence(S->Meta.mArgSep, &pos))
break;
if (matchEndSequence(S->Meta.mArgE, &pos)) {
*endm = pos;
return 1;
}
}
if (getChar(pos) == 0)
bug("unfinished macro argument");
pos++; /* nothing matched, go forward */
}
}
*p2b = pos;
k = 0;
while (1) { /* look for mArgE or comment-start */
pos = iterIdentifierEnd(pos);
SkipPossibleComments(&pos, FLAG_META, 0);
if (getChar(pos) != 0 && strchr(S->Meta.stackchar, getChar(pos)))
k++;
if (k) {
if (getChar(pos) != 0 && strchr(S->Meta.unstackchar, getChar(pos)))
k--;
} else {
*p2e = pos;
if (matchEndSequence(S->Meta.mArgE, &pos))
break;
}
if (getChar(pos) == 0)
bug("unfinished macro");
pos++; /* nothing matched, go forward */
}
*endm = pos;
return 2;
}
char *ProcessText(const char *buf, int l, int ambience) {
char *s;
struct INPUTCONTEXT *T;
if (l == 0) {
s = malloc(1);
s[0] = 0;
return s;
}
s = malloc(l + 2);
s[0] = '\n';
memcpy(s + 1, buf, l);
s[l + 1] = 0;
T = C;
C = malloc(sizeof *C);
C->out = malloc(sizeof *(C->out));
C->in = NULL;
C->argc = T->argc;
C->argv = T->argv;
C->filename = T->filename;
C->out->buf = malloc(80);
C->out->len = 0;
C->out->bufsize = 80;
C->out->f = NULL;
C->lineno = T->lineno;
C->bufsize = l + 2;
C->len = l + 1;
C->buf = C->malloced_buf = s;
C->eof = 0;
C->namedargs = T->namedargs;
C->in_comment = T->in_comment;
C->ambience = ambience;
C->may_have_args = T->may_have_args;
ProcessContext();
outchar(0); /* note that outchar works with the half-destroyed context ! */
s = C->out->buf;
free(C->out);
free(C);
C = T;
return s;
}
int SpliceInfix(const char *buf, int pos1, int pos2, char *sep, int *spl1,
int *spl2) {
int pos, numpar, l;
const char *p;
numpar = 0;
l = strlen(sep);
for (pos = pos2 - 1, p = buf + pos; pos >= pos1; pos--, p--) {
if (*p == ')')
numpar++;
if (*p == '(')
numpar--;
if (numpar < 0)
return 0;
if ((numpar == 0) && (pos2 - pos >= l) && !strncmp(p, sep, l)) {
*spl1 = pos;
*spl2 = pos + l;
return 1;
}
}
return 0;
}
int DoArithmEval(char *buf, int pos1, int pos2, int *result) {
int spl1, spl2, result1, result2, l;
char c, *p;
while ((pos1 < pos2) && isWhite(buf[pos1]))
pos1++;
while ((pos1 < pos2) && isWhite(buf[pos2 - 1]))
pos2--;
if (pos1 == pos2)
return 0;
/* look for C operators starting with lowest precedence */
if (SpliceInfix(buf, pos1, pos2, "||", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
*result = result1 || result2;
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "&&", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
*result = result1 && result2;
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "|", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
*result = result1 | result2;
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "^", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
*result = result1 ^ result2;
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "&", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
*result = result1 & result2;
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "!=", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2)) {
/* revert to string comparison */
while ((pos1 < spl1) && isWhite(buf[spl1 - 1]))
spl1--;
while ((pos2 > spl2) && isWhite(buf[spl2]))
spl2++;
if (spl1 - pos1 != pos2 - spl2)
*result = 1;
else
*result = (strncmp(buf + pos1, buf + spl2, spl1 - pos1) != 0);
} else
*result = (result1 != result2);
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "==", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2)) {
/* revert to string comparison */
while ((pos1 < spl1) && isWhite(buf[spl1 - 1]))
spl1--;
while ((pos2 > spl2) && isWhite(buf[spl2]))
spl2++;
if (spl1 - pos1 != pos2 - spl2)
*result = 0;
else
*result = (strncmp(buf + pos1, buf + spl2, spl1 - pos1) == 0);
} else
*result = (result1 == result2);
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "=~", &spl1, &spl2)) {
#if ! HAVE_FNMATCH_H
bug("globbing support has not been compiled in");
#endif
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2)) {
char *str1, *str2;
/* revert to string comparison */
while ((pos1 < spl1) && isWhite(buf[spl1 - 1]))
spl1--;
while ((pos2 > spl2) && isWhite(buf[spl2]))
spl2++;
str1 = strdup(buf + pos1);
str1[spl1 - pos1] = '\0';
str2 = strdup(buf + spl2);
str2[pos2 - spl2] = '\0';
*result = (fnmatch(str2, str1, 0) == 0);
free(str1);
free(str2);
} else
*result = (result1 == result2);
return 1;
}
if (SpliceInfix(buf, pos1, pos2, ">=", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2)) {
/* revert to string comparison */
while ((pos1 < spl1) && isWhite(buf[spl1 - 1]))
spl1--;
while ((pos2 > spl2) && isWhite(buf[spl2]))
spl2++;
l = spl1 - pos1;
if (l > pos2 - spl2)
l = pos2 - spl2;
result1 = strncmp(buf + pos1, buf + spl2, l);
*result = (result1 > 0)
|| ((result1 == 0) && (spl1 - pos1 >= pos2 - spl2));
} else
*result = (result1 >= result2);
return 1;
}
if (SpliceInfix(buf, pos1, pos2, ">", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2)) {
/* revert to string comparison */
while ((pos1 < spl1) && isWhite(buf[spl1 - 1]))
spl1--;
while ((pos2 > spl2) && isWhite(buf[spl2]))
spl2++;
l = spl1 - pos1;
if (l > pos2 - spl2)
l = pos2 - spl2;
result1 = strncmp(buf + pos1, buf + spl2, l);
*result = (result1 > 0)
|| ((result1 == 0) && (spl1 - pos1 > pos2 - spl2));
} else
*result = (result1 > result2);
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "<=", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2)) {
/* revert to string comparison */
while ((pos1 < spl1) && isWhite(buf[spl1 - 1]))
spl1--;
while ((pos2 > spl2) && isWhite(buf[spl2]))
spl2++;
l = spl1 - pos1;
if (l > pos2 - spl2)
l = pos2 - spl2;
result1 = strncmp(buf + pos1, buf + spl2, l);
*result = (result1 < 0)
|| ((result1 == 0) && (spl1 - pos1 <= pos2 - spl2));
} else
*result = (result1 <= result2);
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "<", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2)) {
/* revert to string comparison */
while ((pos1 < spl1) && isWhite(buf[spl1 - 1]))
spl1--;
while ((pos2 > spl2) && isWhite(buf[spl2]))
spl2++;
l = spl1 - pos1;
if (l > pos2 - spl2)
l = pos2 - spl2;
result1 = strncmp(buf + pos1, buf + spl2, l);
*result = (result1 < 0)
|| ((result1 == 0) && (spl1 - pos1 < pos2 - spl2));
} else
*result = (result1 < result2);
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "+", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
*result = result1 + result2;
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "-", &spl1, &spl2))
if (spl1 != pos1) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
*result = result1 - result2;
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "*", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
*result = result1 * result2;
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "/", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
if (result2 == 0)
bug("Division by zero in expression");
*result = result1 / result2;
return 1;
}
if (SpliceInfix(buf, pos1, pos2, "%", &spl1, &spl2)) {
if (!DoArithmEval(buf, pos1, spl1, &result1)
|| !DoArithmEval(buf, spl2, pos2, &result2))
return 0;
if (result2 == 0)
bug("Division by zero in expression");
*result = result1 % result2;
return 1;
}
if (buf[pos1] == '~') {
if (!DoArithmEval(buf, pos1 + 1, pos2, &result1))
return 0;
*result = ~result1;
return 1;
}
if (buf[pos1] == '!') {
if (!DoArithmEval(buf, pos1 + 1, pos2, &result1))
return 0;
*result = !result1;
return 1;
}
if (buf[pos1] == '-') {
if (!DoArithmEval(buf, pos1 + 1, pos2, &result1))
return 0;
*result = -result1;
return 1;
}
/* add the length() builtin to measure the length of the macro expansion */
if (strncmp(buf + pos1, "length(", strlen("length(")) == 0) {
if (buf[pos2 - 1] != ')')
return 0;
*result = pos2 - pos1 - strlen("length()");
return 1;
}
if (buf[pos1] == '(') {
if (buf[pos2 - 1] != ')')
return 0;
return DoArithmEval(buf, pos1 + 1, pos2 - 1, result);
}
c = buf[pos2];
buf[pos2] = 0;
*result = (int) strtol(buf + pos1, &p, 0);
buf[pos2] = c;
return (p == buf + pos2);
}
void delete_macro(int i) {
int j;
nmacros--;
free(macros[i].username);
free(macros[i].macrotext);
if (macros[i].argnames != NULL ) {
for (j = 0; j < macros[i].nnamedargs; j++)
free(macros[i].argnames[j]);
free(macros[i].argnames);
macros[i].argnames = NULL;
}
FreeComments(macros[i].define_specs);
free(macros[i].define_specs);
memcpy(macros + i, macros + nmacros, sizeof(struct MACRO));
}
char *ArithmEval(int pos1, int pos2) {
char *s, *t;
int i;
/* first define the defined(...) operator */
i = findIdent("defined", strlen("defined"));
if (i >= 0)
warning("the defined(...) macro is already defined");
else {
newmacro("defined", strlen("defined"), 1);
macros[nmacros].macrolen = 0;
macros[nmacros].macrotext = malloc(1);
macros[nmacros].macrotext[0] = 0;
macros[nmacros].nnamedargs = -2; /* trademark of the defined(...) macro */
nmacros++;
}
/* process the text in a usual way */
s = ProcessText(C->buf + pos1, pos2 - pos1, FLAG_META);
/* undefine the defined(...) operator */
if (i < 0) {
i = findIdent("defined", strlen("defined"));
if ((i < 0) || (macros[i].nnamedargs != -2))
warning("the defined(...) macro was redefined in expression");
else
delete_macro(i);
}
if (!DoArithmEval(s, 0, strlen(s), &i))
return s; /* couldn't compute */
t = malloc(MAX_GPP_NUM_SIZE);
sprintf(t, "%d", i);
free(s);
return t;
}
int comment_or_white(int start, int end, int cmtmode) {
char c;
while (start < end) {
SkipPossibleComments(&start, cmtmode, 1);
if (start < end) {
c = getChar(start++);
if ((c != ' ') && (c != '\n') && (c != '\t'))
return 0;
}
}
return 1;
}
char *remove_comments(int start, int end, int cmtmode) {
char *s, *t;
t = s = malloc(end - start + 1);
while (start < end) {
SkipPossibleComments(&start, cmtmode, 1);
if (start < end) {
*t = getChar(start++);
if ((*t == S->User.quotechar) && (start < end)) {
*(++t) = getChar(start++);
}
t++;
}
}
*t = 0;
return s;
}
void SetStandardMode(struct SPECS *P, const char *opt) {
P->op_set = DefaultOp;
P->ext_op_set = DefaultExtOp;
P->id_set = DefaultId;
FreeComments(P);
if (!strcmp(opt, "C") || !strcmp(opt, "cpp")) {
P->User = KUser;
P->Meta = KMeta;
P->preservelf = 1;
add_comment(P, "ccc", my_strdup("/*"), my_strdup("*/"), 0, 0);
add_comment(P, "ccc", my_strdup("//"), my_strdup("\n"), 0, 0);
add_comment(P, "ccc", my_strdup("\\\n"), my_strdup(""), 0, 0);
add_comment(P, "sss", my_strdup("\""), my_strdup("\""), '\\', '\n');
add_comment(P, "sss", my_strdup("'"), my_strdup("'"), '\\', '\n');
} else if (!strcmp(opt, "TeX") || !strcmp(opt, "tex")) {
P->User = Tex;
P->Meta = Tex;
P->preservelf = 0;
} else if (!strcmp(opt, "HTML") || !strcmp(opt, "html")) {
P->User = Html;
P->Meta = Html;
P->preservelf = 0;
} else if (!strcmp(opt, "XHTML") || !strcmp(opt, "xhtml")) {
P->User = XHtml;
P->Meta = XHtml;
P->preservelf = 0;
} else if (!strcmp(opt, "default")) {
P->User = CUser;
P->Meta = CMeta;
P->preservelf = 0;
} else if (!strcmp(opt, "Prolog") || !strcmp(opt, "prolog")) {
P->User = KUser;
P->Meta = KMeta;
P->preservelf = 1;
P->op_set = PrologOp;
add_comment(P, "css", my_strdup("\213/*"), my_strdup("*/"), 0, 0); /* \!o */
add_comment(P, "cii", my_strdup("\\\n"), my_strdup(""), 0, 0);
add_comment(P, "css", my_strdup("%"), my_strdup("\n"), 0, 0);
add_comment(P, "sss", my_strdup("\""), my_strdup("\""), 0, '\n');
add_comment(P, "sss", my_strdup("\207'"), my_strdup("'"), 0, '\n'); /* \!# */
} else
bug("unknown standard mode");
}
void ProcessModeCommand(int p1start, int p1end, int p2start, int p2end) {
struct SPECS *P;
char *s, *p, *opt;
int nargs, check_isdelim;
char *args[10]; /* can't have more than 10 arguments */
whiteout(&p1start, &p1end);
if ((p1start == p1end) || (identifierEnd(p1start) != p1end))
bug("invalid #mode syntax");
if (p2start < 0)
s = my_strdup("");
else
s = ProcessText(C->buf + p2start, p2end - p2start, FLAG_META);
/* argument parsing */
p = s;
opt = NULL;
while (isWhite(*p))
p++;
if ((*p != '"') && (*p != 0)) {
opt = p;
while ((*p != 0) && !isWhite(*p))
p++;
if (*p != 0) {
*(p++) = 0;
while (isWhite(*p))
p++;
}
}
nargs = 0;
check_isdelim = !idequal(C->buf + p1start, p1end - p1start, "charset");
while (*p != 0) {
if (nargs == 10)
bug("too many arguments in #mode command");
if (*(p++) != '"')
bug("syntax error in #mode command (missing \" or trailing data)");
args[nargs++] = p;
p = strNl2(p, check_isdelim);
while (isWhite(*p))
p++;
}
if (idequal(C->buf + p1start, p1end - p1start, "quote")) {
if (opt || (nargs > 1))
bug("syntax error in #mode quote command");
if (nargs == 0)
args[0] = "";
S->stack_next->User.quotechar = args[0][0];
} else if (idequal(C->buf + p1start, p1end - p1start, "comment")) {
if ((nargs < 2) || (nargs > 4))
bug("syntax error in #mode comment command");
if (!opt)
opt = "ccc";
if (nargs < 3)
args[2] = "";
if (nargs < 4)
args[3] = "";
add_comment(S->stack_next, opt, my_strdup(args[0]), my_strdup(args[1]),
args[2][0], args[3][0]);
} else if (idequal(C->buf + p1start, p1end - p1start, "string")) {
if ((nargs < 2) || (nargs > 4))
bug("syntax error in #mode string command");
if (!opt)
opt = "sss";
if (nargs < 3)
args[2] = "";
if (nargs < 4)
args[3] = "";
add_comment(S->stack_next, opt, my_strdup(args[0]), my_strdup(args[1]),
args[2][0], args[3][0]);
} else if (idequal(C->buf + p1start, p1end - p1start, "save")
|| idequal(C->buf + p1start, p1end - p1start, "push")) {
if ((opt != NULL )||nargs)
bug("too many arguments to #mode save");
P = CloneSpecs(S->stack_next);
P->stack_next = S->stack_next;
S->stack_next = P;
} else if (idequal(C->buf + p1start, p1end - p1start, "restore")
|| idequal(C->buf + p1start, p1end - p1start, "pop")) {
if ((opt != NULL )||nargs)
bug("too many arguments to #mode restore");
P = S->stack_next->stack_next;
if (P == NULL )
bug("#mode restore without #mode save");
FreeComments(S->stack_next);
free(S->stack_next);
S->stack_next = P;
} else if (idequal(C->buf + p1start, p1end - p1start, "standard")) {
if ((opt == NULL )||nargs)
bug("syntax error in #mode standard");
SetStandardMode(S->stack_next, opt);
} else if (idequal(C->buf + p1start, p1end - p1start, "user")) {
if ((opt != NULL )||(nargs!=9))bug("#mode user requires 9 arguments");
S->stack_next->User.mStart=my_strdup(args[0]);
S->stack_next->User.mEnd=my_strdup(args[1]);
S->stack_next->User.mArgS=my_strdup(args[2]);
S->stack_next->User.mArgSep=my_strdup(args[3]);
S->stack_next->User.mArgE=my_strdup(args[4]);
S->stack_next->User.stackchar=my_strdup(args[5]);
S->stack_next->User.unstackchar=my_strdup(args[6]);
S->stack_next->User.mArgRef=my_strdup(args[7]);
S->stack_next->User.quotechar=args[8][0];
}
else if (idequal(C->buf+p1start,p1end-p1start,"meta")) {
if ((opt!=NULL)&&!nargs&&!strcmp(opt,"user"))
S->stack_next->Meta=S->stack_next->User;
else {
if ((opt!=NULL)||(nargs!=7)) bug("#mode meta requires 7 arguments");
S->stack_next->Meta.mStart=my_strdup(args[0]);
S->stack_next->Meta.mEnd=my_strdup(args[1]);
S->stack_next->Meta.mArgS=my_strdup(args[2]);
S->stack_next->Meta.mArgSep=my_strdup(args[3]);
S->stack_next->Meta.mArgE=my_strdup(args[4]);
S->stack_next->Meta.stackchar=my_strdup(args[5]);
S->stack_next->Meta.unstackchar=my_strdup(args[6]);
}
}
else if (idequal(C->buf+p1start,p1end-p1start,"preservelf")) {
if ((opt==NULL)||nargs) bug("syntax error in #mode preservelf");
if (!strcmp(opt,"1")||!my_strcasecmp(opt,"on")) S->stack_next->preservelf=1;
else if (!strcmp(opt,"0")||!my_strcasecmp(opt,"off")) S->stack_next->preservelf=0;
else bug("#mode preservelf requires on/off argument");
}
else if (idequal(C->buf+p1start,p1end-p1start,"nocomment")
||idequal(C->buf+p1start,p1end-p1start,"nostring")) {
if ((opt!=NULL)||(nargs>1))
bug("syntax error in #mode nocomment/nostring");
if (nargs==0) FreeComments(S->stack_next);
else delete_comment(S->stack_next,my_strdup(args[0]));
}
else if (idequal(C->buf+p1start,p1end-p1start,"charset")) {
if ((opt==NULL)||(nargs!=1)) bug("syntax error in #mode charset");
if (!my_strcasecmp(opt,"op"))
S->stack_next->op_set=MakeCharsetSubset((unsigned char *)args[0]);
else if (!my_strcasecmp(opt,"par"))
S->stack_next->ext_op_set=MakeCharsetSubset((unsigned char *)args[0]);
else if (!my_strcasecmp(opt,"id"))
S->stack_next->id_set=MakeCharsetSubset((unsigned char *)args[0]);
else bug("unknown charset subset name in #mode charset");
}
else bug("unrecognized #mode command");
free(s);
}
static void DoInclude(char *file_name, int ignore_nonexistent) {
struct INPUTCONTEXT *N;
char *incfile_name = NULL;
FILE *f = NULL;
int j;
int len = strlen(file_name);
/* if absolute path name is specified */
if (file_name[0] == SLASH
#ifdef WIN_NT
|| (isalpha(file_name[0]) && file_name[1]==':')
#endif
)
f = fopen(file_name, "r");
else /* search current dir, if this search isn't turned off */
if (!NoCurIncFirst) {
f = openInCurrentDir(file_name);
}
for (j = 0; (f == NULL )&&(j<nincludedirs);j++){
incfile_name =
realloc(incfile_name,len+strlen(includedir[j])+2);
strcpy(incfile_name,includedir[j]);
incfile_name[strlen(includedir[j])]=SLASH;
/* extract the orig include filename */
strcpy(incfile_name+strlen(includedir[j])+1, file_name);
f=fopen(incfile_name,"r");
}
if (incfile_name != NULL )
free(incfile_name);
/* If didn't find the file and "." is said to be searched last */
if (f == NULL && CurDirIncLast) {
f = openInCurrentDir(file_name);
}
if (f == NULL) {
if (ignore_nonexistent)
return;
else
bug("Requested include file not found");
}
N = C;
C = malloc(sizeof *C);
C->in = f;
C->argc = 0;
C->argv = NULL;
C->filename = file_name;
C->out = N->out;
C->lineno = 1;
C->bufsize = 80;
C->len = 0;
C->buf = C->malloced_buf = malloc(C->bufsize);
C->eof = 0;
C->namedargs = NULL;
C->in_comment = 0;
C->ambience = FLAG_TEXT;
C->may_have_args = 0;
PushSpecs(S);
if (autoswitch) {
if (!strcmp(file_name + strlen(file_name) - 2, ".h")
|| !strcmp(file_name + strlen(file_name) - 2, ".c"))
SetStandardMode(S, "C");
}
/* Include marker before the included contents */
write_include_marker(N->out->f, 1, C->filename, "1");
ProcessContext();
/* Include marker after the included contents */
write_include_marker(N->out->f, N->lineno, N->filename, "2");
/* Need to leave the blank line in lieu of #include, like cpp does */
replace_directive_with_blank_line(N->out->f);
free(C);
PopSpecs();
C = N;
}
int ParsePossibleMeta(void) {
int cklen, nameend;
int id, expparams, nparam, i, j;
int p1start, p1end, p2start, p2end, macend;
int argc, argb[MAXARGS], arge[MAXARGS];
char *tmpbuf;
cklen = 1;
if (!matchStartSequence(S->Meta.mStart, &cklen))
return -1;
nameend = identifierEnd(cklen);
if (nameend && !getChar(nameend - 1))
return -1;
argc = 0; /* for #define with named args */
if (idequal(C->buf + cklen, nameend - cklen, "define")) /* check identifier */
{
id = 1;
expparams = 2;
argc = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "undef")) {
id = 2;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "ifdef")) {
id = 3;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "ifndef")) {
id = 4;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "else")) {
id = 5;
expparams = 0;
} else if (idequal(C->buf + cklen, nameend - cklen, "endif")) {
id = 6;
expparams = 0;
} else if (idequal(C->buf + cklen, nameend - cklen, "include")) {
id = 7;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "exec")) {
id = 8;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "defeval")) {
id = 9;
expparams = 2;
argc = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "ifeq")) {
id = 10;
expparams = 2;
} else if (idequal(C->buf + cklen, nameend - cklen, "ifneq")) {
id = 11;
expparams = 2;
} else if (idequal(C->buf + cklen, nameend - cklen, "eval")) {
id = 12;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "if")) {
id = 13;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "mode")) {
id = 14;
expparams = 2;
} else if (idequal(C->buf + cklen, nameend - cklen, "line")) {
id = 15;
expparams = 0;
} else if (idequal(C->buf + cklen, nameend - cklen, "file")) {
id = 16;
expparams = 0;
} else if (idequal(C->buf + cklen, nameend - cklen, "elif")) {
id = 17;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "error")) {
id = 18;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "warning")) {
id = 19;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "date")) {
id = 20;
expparams = 1;
} else if (idequal(C->buf + cklen, nameend - cklen, "sinclude")) {
id = 21;
expparams = 1;
} else
return -1;
/* #MODE magic : define "..." to be C-style strings */
if (id == 14) {
PushSpecs(S);
S->preservelf = 1;
delete_comment(S, my_strdup("\""));
add_comment(S, "sss", my_strdup("\""), my_strdup("\""), '\\', '\n');
}
nparam = findMetaArgs(nameend, &p1start, &p1end, &p2start, &p2end, &macend,
&argc, argb, arge);
if (nparam == -1)
return -1;
if ((nparam == 2) && isWhitesep(S->Meta.mArgSep))
if (comment_or_white(p2start, p2end, FLAG_META))
nparam = 1;
if ((nparam == 1) && isWhitesep(S->Meta.mArgS))
if (comment_or_white(p1start, p1end, FLAG_META))
nparam = 0;
if (expparams && !nparam)
bug("Missing argument in meta-macro");
switch (id) {
case 1: /* DEFINE */
if (!commented[iflevel]) {
whiteout(&p1start, &p1end); /* recall comments are not allowed here */
if ((p1start == p1end) || (identifierEnd(p1start) != p1end))
bug("#define requires an identifier (A-Z,a-z,0-9,_ only)");
/* buf starts 1 char before the macro */
i = findIdent(C->buf + p1start, p1end - p1start);
if (i >= 0)
delete_macro(i);
newmacro(C->buf + p1start, p1end - p1start, 1);
if (nparam == 1) {
p2end = p2start = p1end;
}
replace_definition_with_blank_lines(C->buf + 1, C->buf + p2end,
S->preservelf);
macros[nmacros].macrotext = remove_comments(p2start, p2end,
FLAG_META);
macros[nmacros].macrolen = strlen(macros[nmacros].macrotext);
macros[nmacros].defined_in_comment = C->in_comment;
if (argc) {
for (j = 0; j < argc; j++)
whiteout(argb + j, arge + j);
/* define with one empty argument */
if ((argc == 1) && (arge[0] == argb[0]))
argc = 0;
macros[nmacros].argnames = malloc((argc + 1) * sizeof(char *));
macros[nmacros].argnames[argc] = NULL;
}
macros[nmacros].nnamedargs = argc;
for (j = 0; j < argc; j++) {
if ((argb[j] == arge[j]) || (identifierEnd(argb[j]) != arge[j]))
bug(
"#define with named args needs identifiers as arg names");
macros[nmacros].argnames[j] = malloc(arge[j] - argb[j] + 1);
memcpy(macros[nmacros].argnames[j], C->buf + argb[j],
arge[j] - argb[j]);
macros[nmacros].argnames[j][arge[j] - argb[j]] = 0;
}
lookupArgRefs(nmacros++);
} else
replace_directive_with_blank_line(C->out->f);
break;
case 2: /* UNDEF */
replace_directive_with_blank_line(C->out->f);
if (!commented[iflevel]) {
if (nparam == 2 && WarningLevel > 0)
warning("Extra argument to #undef ignored");
whiteout(&p1start, &p1end);
if ((p1start == p1end) || (identifierEnd(p1start) != p1end))
bug("#undef requires an identifier (A-Z,a-z,0-9,_ only)");
i = findIdent(C->buf + p1start, p1end - p1start);
if (i >= 0)
delete_macro(i);
}
break;
case 3: /* IFDEF */
replace_directive_with_blank_line(C->out->f);
iflevel++;
if (iflevel == STACKDEPTH)
bug("Too many nested #ifdefs");
commented[iflevel] = commented[iflevel - 1];
if (!commented[iflevel]) {
if (nparam == 2 && WarningLevel > 0)
warning("Extra argument to #ifdef ignored");
whiteout(&p1start, &p1end);
if ((p1start == p1end) || (identifierEnd(p1start) != p1end))
bug("#ifdef requires an identifier (A-Z,a-z,0-9,_ only)");
i = findIdent(C->buf + p1start, p1end - p1start);
commented[iflevel] = (i == -1);
}
break;
case 4: /* IFNDEF */
replace_directive_with_blank_line(C->out->f);
iflevel++;
if (iflevel == STACKDEPTH)
bug("Too many nested #ifdefs");
commented[iflevel] = commented[iflevel - 1];
if (!commented[iflevel]) {
if (nparam == 2 && WarningLevel > 0)
warning("Extra argument to #ifndef ignored");
whiteout(&p1start, &p1end);
if ((p1start == p1end) || (identifierEnd(p1start) != p1end))
bug("#ifndef requires an identifier (A-Z,a-z,0-9,_ only)");
i = findIdent(C->buf + p1start, p1end - p1start);
commented[iflevel] = (i != -1);
}
break;
case 5: /* ELSE */
replace_directive_with_blank_line(C->out->f);
if (!commented[iflevel] && (nparam > 0) && WarningLevel > 0)
warning("Extra argument to #else ignored");
if (iflevel == 0)
bug("#else without #if");
if (!commented[iflevel - 1] && commented[iflevel] != 2)
commented[iflevel] = !commented[iflevel];
break;
case 6: /* ENDIF */
replace_directive_with_blank_line(C->out->f);
if (!commented[iflevel] && (nparam > 0) && WarningLevel > 0)
warning("Extra argument to #endif ignored");
if (iflevel == 0)
bug("#endif without #if");
iflevel--;
break;
case 7: /* INCLUDE */
if (!commented[iflevel]) {
char *incfile_name;
if (nparam == 2 && WarningLevel > 0)
warning("Extra argument to #include ignored");
if (!whiteout(&p1start, &p1end))
bug("Missing file name in #include");
/* user may put "" or <> */
if (((getChar(p1start) == '\"') && (getChar(p1end - 1) == '\"'))
|| ((getChar(p1start) == '<') && (getChar(p1end - 1) == '>'))) {
p1start++;
p1end--;
}
if (p1start >= p1end)
bug("Missing file name in #include");
incfile_name = malloc(p1end - p1start + 1);
/* extract the orig include filename */
for (i = 0; i < p1end - p1start; i++)
incfile_name[i] = getChar(p1start + i);
incfile_name[p1end - p1start] = 0;
DoInclude(incfile_name, 0);
} else
replace_directive_with_blank_line(C->out->f);
break;
case 8: /* EXEC */
if (!commented[iflevel]) {
if (!execallowed)
warning(
"Not allowed to #exec. Command output will be left blank");
else {
char *s, *t;
int c;
FILE *f;
s = ProcessText(C->buf + p1start, p1end - p1start, FLAG_META);
if (nparam == 2) {
t = ProcessText(C->buf + p2start, p2end - p2start,
FLAG_META);
i = strlen(s);
s = realloc(s, i + strlen(t) + 2);
s[i] = ' ';
strcpy(s + i + 1, t);
free(t);
}
f = popen(s, "r");
free(s);
if (f == NULL )
warning("Cannot #exec. Command not found(?)");
else {
while ((c = fgetc(f)) != EOF)
outchar((char) c);
pclose(f);
}
}
}
break;
case 9: /* DEFEVAL */
if (!commented[iflevel]) {
whiteout(&p1start, &p1end);
if ((p1start == p1end) || (identifierEnd(p1start) != p1end))
bug("#defeval requires an identifier (A-Z,a-z,0-9,_ only)");
tmpbuf = ProcessText(C->buf + p2start, p2end - p2start, FLAG_META);
i = findIdent(C->buf + p1start, p1end - p1start);
if (i >= 0)
delete_macro(i);
newmacro(C->buf + p1start, p1end - p1start, 1);
if (nparam == 1) {
p2end = p2start = p1end;
}
replace_definition_with_blank_lines(C->buf + 1, C->buf + p2end,
S->preservelf);
macros[nmacros].macrotext = tmpbuf;
macros[nmacros].macrolen = strlen(macros[nmacros].macrotext);
macros[nmacros].defined_in_comment = C->in_comment;
if (argc) {
for (j = 0; j < argc; j++)
whiteout(argb + j, arge + j);
/* define with one empty argument */
if ((argc == 1) && (arge[0] == argb[0]))
argc = 0;
macros[nmacros].argnames = malloc((argc + 1) * sizeof(char *));
macros[nmacros].argnames[argc] = NULL;
}
macros[nmacros].nnamedargs = argc;
for (j = 0; j < argc; j++) {
if ((argb[j] == arge[j]) || (identifierEnd(argb[j]) != arge[j]))
bug(
"#defeval with named args needs identifiers as arg names");
macros[nmacros].argnames[j] = malloc(arge[j] - argb[j] + 1);
memcpy(macros[nmacros].argnames[j], C->buf + argb[j],
arge[j] - argb[j]);
macros[nmacros].argnames[j][arge[j] - argb[j]] = 0;
}
lookupArgRefs(nmacros++);
} else
replace_directive_with_blank_line(C->out->f);
break;
case 10: /* IFEQ */
replace_directive_with_blank_line(C->out->f);
iflevel++;
if (iflevel == STACKDEPTH)
bug("Too many nested #ifeqs");
commented[iflevel] = commented[iflevel - 1];
if (!commented[iflevel]) {
char *s, *t;
if (nparam != 2)
bug("#ifeq requires two arguments");
s = ProcessText(C->buf + p1start, p1end - p1start, FLAG_META);
t = ProcessText(C->buf + p2start, p2end - p2start, FLAG_META);
commented[iflevel] = (nowhite_strcmp(s, t) != 0);
free(s);
free(t);
}
break;
case 11: /* IFNEQ */
replace_directive_with_blank_line(C->out->f);
iflevel++;
if (iflevel == STACKDEPTH)
bug("Too many nested #ifeqs");
commented[iflevel] = commented[iflevel - 1];
if (!commented[iflevel]) {
char *s, *t;
if (nparam != 2)
bug("#ifneq requires two arguments");
s = ProcessText(C->buf + p1start, p1end - p1start, FLAG_META);
t = ProcessText(C->buf + p2start, p2end - p2start, FLAG_META);
commented[iflevel] = (nowhite_strcmp(s, t) == 0);
free(s);
free(t);
}
break;
case 12: /* EVAL */
if (!commented[iflevel]) {
char *s, *t;
if (nparam == 2)
p1end = p2end; /* we really want it all ! */
s = ArithmEval(p1start, p1end);
for (t = s; *t; t++)
outchar(*t);
free(s);
}
break;
case 13: /* IF */
replace_directive_with_blank_line(C->out->f);
iflevel++;
if (iflevel == STACKDEPTH)
bug("Too many nested #ifs");
commented[iflevel] = commented[iflevel - 1];
if (!commented[iflevel]) {
char *s;
if (nparam == 2)
p1end = p2end; /* we really want it all ! */
s = ArithmEval(p1start, p1end);
commented[iflevel] = ((s[0] == '0') && (s[1] == 0));
free(s);
}
break;
case 14: /* MODE */
replace_directive_with_blank_line(C->out->f);
if (nparam == 1)
p2start = -1;
if (!commented[iflevel])
ProcessModeCommand(p1start, p1end, p2start, p2end);
PopSpecs();
break;
case 15: { /* LINE */
char buf[MAX_GPP_NUM_SIZE];
sprintf(buf, "%d", C->lineno);
replace_directive_with_blank_line(C->out->f);
sendout(buf, strlen(buf), 0);
}
break;
case 16: /* FILE */
replace_directive_with_blank_line(C->out->f);
sendout(C->filename, strlen(C->filename), 0);
break;
case 17: /* ELIF */
replace_directive_with_blank_line(C->out->f);
if (iflevel == 0)
bug("#elif without #if");
if (!commented[iflevel - 1]) {
if (commented[iflevel] != 1)
commented[iflevel] = 2;
else {
char *s;
commented[iflevel] = 0;
if (nparam == 2)
p1end = p2end; /* we really want it all ! */
s = ArithmEval(p1start, p1end);
commented[iflevel] = ((s[0] == '0') && (s[1] == 0));
free(s);
}
}
break;
case 18: /* ERROR */
replace_directive_with_blank_line(C->out->f);
if (!commented[iflevel])
bug(
ProcessText(C->buf + p1start,
(nparam == 2 ? p2end : p1end) - p1start,
FLAG_META));
break;
case 19: /* WARNING */
replace_directive_with_blank_line(C->out->f);
if (!commented[iflevel]) {
char *s;
s = ProcessText(C->buf + p1start,
(nparam == 2 ? p2end : p1end) - p1start, FLAG_META);
warning(s);
free(s);
}
break;
case 20: { /* DATE */
char buf[MAX_GPP_DATE_SIZE];
char *fmt;
time_t now = time(NULL );
fmt = ProcessText(C->buf + p1start,
(nparam == 2 ? p2end : p1end) - p1start, FLAG_META);
if (!strftime(buf, MAX_GPP_DATE_SIZE, fmt, localtime(&now)))
bug("date buffer exceeded");
replace_directive_with_blank_line(C->out->f);
sendout(buf, strlen(buf), 0);
free(fmt);
}
break;
case 21: /* SINCLUDE */
if (!commented[iflevel]) {
char *incfile_name;
if (nparam == 2 && WarningLevel > 0)
warning("Extra argument to #sinclude ignored");
if (!whiteout(&p1start, &p1end))
bug("Missing file name in #sinclude");
/* user may put "" or <> */
if (((getChar(p1start) == '\"') && (getChar(p1end - 1) == '\"'))
|| ((getChar(p1start) == '<') && (getChar(p1end - 1) == '>'))) {
p1start++;
p1end--;
}
if (p1start >= p1end)
bug("Missing file name in #sinclude");
incfile_name = malloc(p1end - p1start + 1);
/* extract the orig include filename */
for (i = 0; i < p1end - p1start; i++)
incfile_name[i] = getChar(p1start + i);
incfile_name[p1end - p1start] = 0;
DoInclude(incfile_name, 1);
} else
replace_directive_with_blank_line(C->out->f);
break;
default:
bug("Internal meta-macro identification error");
}
shiftIn(macend);
return 0;
}
int ParsePossibleUser(void) {
int idstart, idend, sh_end, lg_end, macend;
int argc, id, i, l;
char *argv[MAXARGS];
int argb[MAXARGS], arge[MAXARGS];
struct INPUTCONTEXT *T;
idstart = 1;
id = 0;
if (!SplicePossibleUser(&idstart, &idend, &sh_end, &lg_end, argb, arge,
&argc, 1, &id, FLAG_USER))
return -1;
if ((sh_end >= 0) && (C->namedargs != NULL )) {
i = findNamedArg(C->buf + idstart, idend - idstart);
if (i >= 0) {
if (i < C->argc)
sendout(C->argv[i], strlen(C->argv[i]), 0);
shiftIn(sh_end);
return 0;
}
}
if (id < 0)
return -1;
if (lg_end >= 0)
macend = lg_end;
else {
macend = sh_end;
argc = 0;
}
if (macros[id].nnamedargs == -2) { /* defined(...) macro for arithmetic */
char *s, *t;
if (argc != 1)
return -1;
s = remove_comments(argb[0], arge[0], FLAG_USER);
t = s + strlen(s) - 1;
if (*s != 0)
while ((t != s) && isWhite(*t))
*(t--) = 0;
t = s;
while (isWhite(*t))
t++;
if (findIdent(t, strlen(t)) >= 0)
outchar('1');
else
outchar('0');
free(s);
shiftIn(macend);
return 0;
}
if (!macros[id].macrotext[0]) { /* the empty macro */
shiftIn(macend);
return 0;
}
for (i = 0; i < argc; i++)
argv[i] = ProcessText(C->buf + argb[i], arge[i] - argb[i], FLAG_USER);
/* process macro text */
T = C;
C = malloc(sizeof *C);
C->out = T->out;
C->in = NULL;
C->argc = argc;
C->argv = argv;
C->filename = T->filename;
C->lineno = T->lineno;
C->may_have_args = 1;
if ((macros[id].nnamedargs == -1) && (lg_end >= 0)
&& (macros[id].define_specs->User.mEnd[0] == 0)) {
/* build an aliased macro call */
l = strlen(macros[id].macrotext) + 2
+ strlen(macros[id].define_specs->User.mArgS)
+ strlen(macros[id].define_specs->User.mArgE)
+ (argc - 1) * strlen(macros[id].define_specs->User.mArgSep);
for (i = 0; i < argc; i++)
l += strlen(argv[i]);
C->buf = C->malloced_buf = malloc(l);
l = strlen(macros[id].macrotext) + 1;
C->buf[0] = '\n';
strcpy(C->buf + 1, macros[id].macrotext);
while ((l > 1) && isWhite(C->buf[l - 1]))
l--;
strcpy(C->buf + l, macros[id].define_specs->User.mArgS);
for (i = 0; i < argc; i++) {
if (i > 0)
strcat(C->buf, macros[id].define_specs->User.mArgSep);
strcat(C->buf, argv[i]);
}
strcat(C->buf, macros[id].define_specs->User.mArgE);
C->may_have_args = 0;
} else {
C->buf = C->malloced_buf = malloc(strlen(macros[id].macrotext) + 2);
C->buf[0] = '\n';
strcpy(C->buf + 1, macros[id].macrotext);
}
C->len = strlen(C->buf);
C->bufsize = C->len + 1;
C->eof = 0;
C->namedargs = macros[id].argnames;
C->in_comment = macros[id].defined_in_comment;
C->ambience = FLAG_META;
PushSpecs(macros[id].define_specs);
ProcessContext();
PopSpecs();
free(C);
C = T;
for (i = 0; i < argc; i++)
free(argv[i]);
shiftIn(macend);
return 0;
}
void ParseText(void) {
int l, cs, ce;
char c, *s;
struct COMMENT *p;
if (++parselevel == STACKDEPTH)
bug("Stack depth exceeded during parse");
/* look for comments first */
if (!C->in_comment) {
cs = 1;
for (p = S->comments; p != NULL ; p = p->next)
if (!(p->flags[C->ambience] & FLAG_IGNORE))
if (matchStartSequence(p->start, &cs)) {
l = ce = findCommentEnd(p->end, p->quote, p->warn, cs,
p->flags[C->ambience]);
matchEndSequence(p->end, &l);
if (p->flags[C->ambience] & OUTPUT_DELIM)
sendout(C->buf + 1, cs - 1, 0);
if (!(p->flags[C->ambience] & OUTPUT_TEXT))
replace_definition_with_blank_lines(C->buf + 1,
C->buf + ce - 1, 0);
if (p->flags[C->ambience] & PARSE_MACROS) {
C->in_comment = 1;
s = ProcessText(C->buf + cs, ce - cs, C->ambience);
if (p->flags[C->ambience] & OUTPUT_TEXT)
sendout(s, strlen(s), 0);
C->in_comment = 0;
free(s);
} else if (p->flags[C->ambience] & OUTPUT_TEXT)
sendout(C->buf + cs, ce - cs, 0);
if (p->flags[C->ambience] & OUTPUT_DELIM)
sendout(C->buf + ce, l - ce, 0);
shiftIn(l);
parselevel--;
return;
}
}
if (ParsePossibleMeta() >= 0) {
parselevel--;
return;
}
if (ParsePossibleUser() >= 0) {
parselevel--;
return;
}
l = 1;
/* If matching numbered macro argument and inside a macro */
if (matchSequence(S->User.mArgRef, &l) && C->may_have_args) {
/* Process macro arguments referenced as #1,#2,... */
c = getChar(l);
if ((c >= '1') && (c <= '9')) {
c = c - '1';
if (c < C->argc)
sendout(C->argv[(int) c], strlen(C->argv[(int) c]), 0);
shiftIn(l + 1);
parselevel--;
return;
}
}
l = identifierEnd(1);
if (l == 1)
l = 2;
sendout(C->buf + 1, l - 1, 1);
shiftIn(l);
parselevel--;
}
void ProcessContext(void) {
if (C->len == 0) {
C->buf[0] = '\n';
C->len++;
}
while (!C->eof)
ParseText();
if (C->in != NULL )
fclose(C->in);
free(C->malloced_buf);
}
/* additions by M. Kifer - revised D.A. 12/16/01 */
/* copy SLASH-terminated name of the directory of fname */
static void getDirname(const char *fname, char *dirname) {
int i;
for (i = strlen(fname) - 1; i >= 0; i--) {
if (fname[i] == SLASH)
break;
}
if (i >= 0) {
strncpy(dirname, fname, i);
dirname[i] = SLASH;
} else
/* just a precaution: i must be -1 in this case anyway */
i = -1;
dirname[i + 1] = '\0';
}
static FILE *openInCurrentDir(const char *incfile) {
FILE *f;
char *absfile;
if (IncludeFile) {
return fopen(incfile, "r");
}
absfile = calloc(strlen(C->filename) + strlen(incfile) + 1, 1);
getDirname(C->filename, absfile);
strcat(absfile, incfile);
f = fopen(absfile, "r");
free(absfile);
return f;
}
/* skip = # of \n's already output by other mechanisms, to be skipped */
void replace_definition_with_blank_lines(const char *start, const char *end,
int skip) {
if ((include_directive_marker != NULL )&& (C->out->f != NULL)){
while (start <= end) {
if (*start == '\n') {
if (skip) skip--; else fprintf(C->out->f,"\n");
}
start++;
}
}
}
/* insert blank line where the metas IFDEF,ELSE,INCLUDE, etc., stood in the
input text
*/
void replace_directive_with_blank_line(FILE *f) {
if ((include_directive_marker != NULL )&& (f != NULL)
&& (!S->preservelf) && (S->Meta.mArgE[0]=='\n')){
fprintf(f,"\n");
}
}
/* If lineno is > 15 digits - the number won't be printed correctly */
void write_include_marker(FILE *f, int lineno, char *filename,
const char *marker) {
static char lineno_buf[MAX_GPP_NUM_SIZE];
static char *escapedfilename = NULL;
if ((include_directive_marker != NULL )&& (f != NULL)){
#ifdef WIN_NT
escape_backslashes(filename,&escapedfilename);
#else
escapedfilename = filename;
#endif
sprintf(lineno_buf,"%d", lineno);
fprintf(f, include_directive_marker, lineno_buf, escapedfilename, marker);
}
}
/* Under windows, files can have backslashes in them.
These should be escaped.
*/
void escape_backslashes(const char *instr, char **outstr) {
int out_idx = 0;
if (*outstr != NULL )
free(*outstr);
*outstr = malloc(2 * strlen(instr));
while (*instr != '\0') {
if (*instr == '\\') {
*(*outstr + out_idx) = '\\';
out_idx++;
}
*(*outstr + out_idx) = *instr;
out_idx++;
instr++;
}
*(*outstr + out_idx) = '\0';
}
/* includemarker_input should have 3 ?-marks, which are replaced with %s.
Also, @ is replaced with a space. These symbols can be escaped with a
backslash.
*/
void construct_include_directive_marker(char **include_directive_marker,
const char *includemarker_input) {
int len = strlen(includemarker_input);
char ch;
int in_idx = 0, out_idx = 0;
int quoted = 0, num_repl = 0;
/* only 6 extra chars are needed: 3 for the three %'s, 2 for \n, 1 for \0 */
*include_directive_marker = malloc(len + 18);
ch = *includemarker_input;
while (ch != '\0' && in_idx < len) {
if (quoted) {
*(*include_directive_marker + out_idx) = ch;
out_idx++;
quoted = 0;
} else {
switch (ch) {
case '\\':
quoted = 1;
break;
case '@':
*(*include_directive_marker + out_idx) = ' ';
out_idx++;
break;
case '%':
case '?':
*(*include_directive_marker + out_idx) = '%';
out_idx++;
*(*include_directive_marker + out_idx) = 's';
out_idx++;
if (++num_repl > 3)
bug("only 3 substitutions allowed in -includemarker");
break;
default:
*(*include_directive_marker + out_idx) = ch;
out_idx++;
}
}
in_idx++;
ch = *(includemarker_input + in_idx);
}
*(*include_directive_marker + out_idx) = '\n';
out_idx++;
*(*include_directive_marker + out_idx) = '\0';
}
int main(int argc, char **argv) {
initthings(argc, argv);
/* The include marker at the top of the file */
if (IncludeFile)
DoInclude(IncludeFile, 0);
IncludeFile = NULL;
write_include_marker(C->out->f, 1, C->filename, "");
ProcessContext();
fclose(C->out->f);
return EXIT_SUCCESS;
}