/* Copyright 1991 - 1997 J"orn Rennecke */
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#ifdef linux
#include <linux/mman.h>
#define MAP_ANON MAP_ANONYMOUS
#ifndef MAP_FILE
#define MAP_FILE 0
#endif
#endif
#include <errno.h>
#include "common.h"
#include "alloc.h"
#include "object.h"
#include "interpret.h"
#ifdef OS2
#include <io.h>
#endif
#include "lang.h"
#include "string.h"
#include "config.h"
#include "exec.h"
#include "lex.h"
#include "instrs.h"
#include "patchlevel.h"
#define FILE_END "\0"
#define IS_OCTAL(c) ((c) & ~7 == '0')
#define CURRENT_FILE inctop->file
/* Line numbers have to be kept for code in #include files, and the code
* is stored in line-number tagged nodes till all files have been read;
* therefore, line numbers have to be unique in each compilation.
* Thus, we simply count up current_line, remembering where each file starts.
*/
int current_line;
int pragma_strong_types; /* require call_other() values to be casted */
int pragma_save_types; /* Save argument types after compilation */
int pragma_optimize;
#if LEXDEBUG
int lexdebug;
#endif
svalue all_proto_closures;
static INLINE int number(p_int);
static char *handle_define(char *);
static void add_permanent_define(char *, int, char *, char);
char *add_input(const char *, mp_int, char *);
static char *expand_define(struct ident*, char *);
static char *expand_defarg(char *p);
static void myungetc(char);
static union svalue cond_get_exp(int priority);
static int exgetc();
static char *skip_comment(char *p);
static char *skip_pp_comment(char *p);
static char *skip2nl(char *p);
static char *skip_white_bsnl(char *p);
static char *efun_defined(char *, char **, struct expand_stack *);
static char *add_current_file(), *add_current_line(),
*add_hostname(), *add_domainname();
extern char *add_host_ip_number();
static union svalue *inc_list;
static svalue inc_hook_value;
static int inc_list_size;
static mp_int inc_list_maxlen;
static char *auto_include_string = (char *)0;
static int basestate_firstline = 0;
#define EXPANDMAX 25000
static int nexpands;
static void lexerror(int n);
#define INC_LIST_MAXLEN 1024
static struct ident *lookup_define(char *s, mp_int len, int hash, int type);
static struct ifstate {
struct ifstate *next;
p_int expect_else; /* compatible with svalues from cond_get_exp() */
int line;
} *iftop = 0;
static struct incstate {
struct incstate *next, *next_hash;
int line, lastif_line;
union svalue file;
char *outp;
int pragma_strong_types;
dev_t dev;
ino_t inode;
time_t mtime;
caddr_t mapstart;
off_t maplen;
} basestate, *maptab[16];
struct incstate *inctop;
#define EXPSTACK_SIZE 1024
static struct expand_stack expstack[EXPSTACK_SIZE], *expsp;
/*
* Two entrys in expstack are reserved for defarg expansion, defined()
* evaluation and #elif processing, so that these operations can be
* done without expsp overflow check.
*/
static char *outp;
static struct s_reswords reswords[] = {
{ "break", YYF_BREAK, RESWORD_CLOSURE(F_BREAK) },
{ "case", YYF_CASE, },
{ "catch", YYF_CATCH, RESWORD_CLOSURE(F_CATCH) },
{ "closure", YYF_CLOSURE_DECL, TYPE_CLOSURE, },
{ "const", YYF_VAR_TYPE_MODIFIER, TYPE__CONST, },
{ "continue", YYF_CONTINUE, RESWORD_CLOSURE(F_BRANCH) },
{ "default", YYF_DEFAULT, RESWORD_CLOSURE(F_CSHARED0) },
{ "do", YYF_DO, RESWORD_CLOSURE(F_BRANCH_ON_NON_ZERO) },
{ "else", YYF_ELSE, },
{ "float", YYF_BASIC_TYPE, TYPE_FLOAT, },
{ "for", YYF_FOR, },
{ "if", YYF_IF, RESWORD_CLOSURE(F_BRANCH_ON_ZERO) },
{ "inherit", YYF_INHERIT, },
{ "int", YYF_BASIC_TYPE, TYPE_NUMBER, },
{ "mapping", YYF_BASIC_TYPE, TYPE_MAPPING, },
{ "mixed", YYF_BASIC_TYPE, TYPE_ANY, },
{ "nomask", YYF_TYPE_MODIFIER, TYPE__NOMASK, },
{ "object", YYF_BASIC_TYPE, TYPE_OBJECT, },
{ "private", YYF_TYPE_MODIFIER, TYPE__PRIVATE, },
{ "protected", YYF_FUN_TYPE_MODIFIER, TYPE__PROTECTED, },
{ "public", YYF_TYPE_MODIFIER, TYPE__PUBLIC, },
{ "return", YYF_RETURN, RESWORD_CLOSURE(F_RETURN) },
{ "shared", YYF_VAR_TYPE_MODIFIER, TYPE__SHARED, },
{ "sscanf", YYF_SSCANF, RESWORD_CLOSURE(F_SSCANF) },
{ "static", YYF_TYPE_MODIFIER, TYPE__STATIC, },
{ "status", YYF_BASIC_TYPE, TYPE_NUMBER, },
{ "string", YYF_BASIC_TYPE, TYPE_STRING, },
{ "struct", YYF_STRUCT, },
{ "switch", YYF_SWITCH, RESWORD_CLOSURE(F_SWITCH) },
{ "symbol", YYF_BASIC_TYPE, TYPE_SYMBOL, },
{ "varargs", YYF_VARARGS, TYPE__VARARGS, },
{ "virtual", YYF_VIRTUAL, TYPE__VIRTUAL},
{ "void", YYF_VOID, TYPE_VOID },
{ "while", YYF_WHILE, RESWORD_CLOSURE(F_BRANCH_ON_ZERO) },
};
struct ident *ident_table[ITABLE_SIZE];
#define identhash(s, len) hashstr(s, len, 20)
static void set_inc_list(svalue sv);
#include "efun_defs.c"
struct ident *make_shared_identifier(char *s, mp_int len, uint16 hash, int n)
{
struct ident *curr, *prev, **q;
q = &ident_table[hash & (ITABLE_SIZE - 1)];
curr = *q;
prev = 0;
while (curr) {
if (curr->hash == hash /* make most collisions cheap */ &&
curr->namelen == len && !memcmp(curr->name, s, len))
{
/* found it */
if (prev) { /* not at head of list */
prev->next = curr->next;
curr->next = *q;
*q = curr;
}
if (n > curr->type) {
struct ident *inferior=curr;
if (curr = alloc_gen(sizeof *curr)) {
curr->name = inferior->name;
curr->namelen = inferior->namelen;
curr->next = inferior->next;
curr->type = I_TYPE_UNKNOWN;
curr->inferior = inferior;
curr->hash = hash;
*q = curr;
}
}
return curr;
}
prev = curr;
curr = curr->next;
} /* not found, create new one */
curr = alloc_gen(sizeof *curr);
if (!curr)
return 0;
curr->name = s;
curr->namelen = len;
curr->next = *q;
curr->type = I_TYPE_UNKNOWN;
curr->inferior = 0;
curr->hash = hash;
*q = curr;
return curr;
}
struct ident *new_ident(struct ident *id, int type, union ident_u u) {
if (id->type > type) {
struct ident *inferior;
do {
inferior = id->inferior;
if (!inferior || inferior->type < type) {
struct ident *new = alloc_gen(sizeof *new);
if (new) {
new->type = type;
new->u = u;
id->inferior = new;
new->inferior = inferior;
new->name = inferior->name;
new->namelen = inferior->namelen;
new->hash = inferior->hash;
}
return new;
}
id = inferior;
} while (id->type != type);
return id;
} else {
/* id->type < type */
struct ident *new;
if (new = alloc_gen(sizeof *new)) {
new->type = type;
new->u = u;
new->name = id->name;
new->namelen = id->namelen;
new->next = id->next;
new->next = id->next;
new->inferior = id;
new->hash = id->hash;
ident_table[new->hash & (ITABLE_SIZE - 1)] = new;
}
return new;
}
}
void free_shared_identifier(struct ident *p) {
struct ident *first, **q;
uint16 hash;
hash = p->hash;
q = &ident_table[hash & (ITABLE_SIZE - 1)];
first = *q;
for(;;) {
if (first->hash == hash) {
struct ident *curr = first;
do {
if (curr == p) { /* this is the right one */
if (first == curr) {
if (curr->inferior) {
curr->inferior->next = curr->next;
*q = curr->inferior;
free_gen((char *)curr);
return; /* success */
} else {
*q = curr->next;
free_gen((char *)curr);
return; /* success */
}
} else {
*q = curr->inferior;
free_gen((char *)curr);
return; /* success */
}
}
q = &curr->inferior;
curr = *q;
} while(curr);
}
q = &first->next;
first = *q;
} /* not found */
}
static void lexerror(int n) {
yyerrorn(n);
outp = FILE_END;
}
static p_int skip2nume(char *token) {
char *p;
char c;
char nl = '\n';
int nest;
p = outp;
for (nest = 0;;) {
do {
c = *p++;
check_newline:
} while (c > nl);
if (c != nl) {
if (!c) {
yyerrorn(CE_EOF_SKIP);
outp = FILE_END;
return 1;
}
continue;
}
current_line++;
c = *p++;
if (c == '#') {
do c = *p++; while(lexwhite(c));
if (c == 'i') {
if (*p != 'f')
continue;
if ( !isalunum(*++p) ||
( !memcmp(p, "def", 3) ?
!isalunum(*(p+=3)) :
( !memcmp(p, "ndef", 4) && !isalunum(*(p+=4)) )
) )
{
nest++;
}
continue;
} else if (c == 'e') {
if (nest > 0) {
if (!memcmp(p, "ndif", 4) && !isalunum(*(p+=5)))
nest--;
} else {
if (!memcmp(p, "ndif", 4) && !isalunum(*(p+=4))) {
do c = *p++; while (c && c != nl);
/* *(p-1) == nl */
outp = p;
return 0;
} else if (token) {
/* token == "lse" */
if (!memcmp(p, token, 3) && !isalunum(*(p+=3))) {
do c = *p++; while (c && c != nl);
/* *(p-1) == nl */
outp = p;
return 1;
} else if (!memcmp(p, "lif", 3) && !isalunum(*(p+=3))) {
static char iffake[] =
{ '\n','#','i','f',' ',LC_POP };
expsp[1].pop = expsp;
expsp[1].ret = p;
expsp++;
outp = &iffake[1];
return 0;
}
}
}
continue;
}
}
goto check_newline;
}
}
static void handle_cond(p_int c) {
struct ifstate *p;
if (!c) {
if (current_line - inctop->line == inctop->lastif_line) {
outp = FILE_END;
return;
}
if (!skip2nume("lse"))
return;
}
p = alloc_gen(sizeof(struct ifstate));
p->next = iftop;
iftop = p;
p->expect_else = c;
p->line = current_line;
}
extern size_t pagesize;
size_t pagemask;
caddr_t lex_map(int fd, struct incstate *is) {
struct stat sbuf;
off_t size;
caddr_t p, q;
int hash;
struct incstate *old;
if (fstat(fd, &sbuf)) switch(errno) {
default:
perror("fstat");
close(fd);
yyerrorn(CE_MAPFAIL);
return 0;
}
hash = sbuf.st_dev ^ sbuf.st_ino ^ sbuf.st_mtime;
hash ^= hash >> 16;
hash ^= hash >> 8;
hash ^= hash >> 4;
hash &= NELEM(maptab) - 1;
for (old = maptab[hash]; old; old = old->next_hash) {
if (sbuf.st_ino == old->inode && sbuf.st_mtime == old->mtime &&
sbuf.st_dev == old->dev)
{
is->mapstart = p = old->mapstart;
is->lastif_line = old->lastif_line;
is->maplen = 0;
goto mapping_done;
}
}
is->next_hash = maptab[hash];
maptab[hash] = is;
size = sbuf.st_size;
if ((size - 1 & pagemask) + 3 > pagemask) {
p = q = mmap((caddr_t)0, size+3, PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
if (p != (caddr_t)-1) {
p = mmap(p, size, PROT_READ, MAP_FILE|MAP_FIXED|MAP_PRIVATE, fd, 0);
if (p == (caddr_t)-1) {
munmap(q, size+3);
}
}
} else {
p = mmap((caddr_t)0, size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
}
if (p == (caddr_t)-1) switch(errno) {
default:
perror("mmap");
yyerrorn(CE_MAPFAIL);
return 0;
}
is->mapstart = p;
is->maplen = size+3;
is->lastif_line = -1;
mapping_done:
close(fd);
inctop = is;
pragma_strong_types = 0;
instrs[F_CALL_OTHER].ret_type = TYPE_ANY;
return p;
}
/*
* handle_include() is only used once, thus it is an obvious candidate for
* inline. But gcc 2.5.8 only makes a mess of it :-(
*/
static /* INLINE */ char *handle_include(char *yyp) {
char *after_string;
int concat; /* 2 for neutral path in double quotes,
1 for path starting with "/" or ".."
-1 for path in < > */
char buf[INC_LIST_MAXLEN+1024], *end;
char *relstart;
after_string = 0;
concat = 0;
for (;;) {
char c;
c = *yyp;
switch(c) {
{
struct ident *d;
char *start;
case LC_IDENT:
if (expsp == &expstack[0]) {
yyerrorn(CE_BADCHAR, (p_int)LC_IDENT << 1);
yyp++;
continue;
}
yyp = ALIGN(yyp + 1 + 2*sizeof(char *), char *);
d = lookup_define( ((char **)yyp)[-2], ((p_int *)yyp)[-1],
((short *)yyp)[0], I_TYPE_DEFINE);
yyp += sizeof(short);
goto try_expand_define;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b':
case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i':
case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':
case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w':
case 'x': case 'y': case 'z': case '_':
{
struct idhash_ret idhr;
start = yyp;
idhr = idhash(yyp);
yyp = idhr.p;
d = lookup_define(start, yyp - start, idhr.hash, I_TYPE_DEFINE);
}
try_expand_define:
if (!d) {
if (concat >= 0) {
yyerrorn(concat ? CE_INC_CONT : CE_INC_START);
return yyp;
}
if (end + (yyp - start) >= &buf[sizeof(buf)-1]) {
yyerrorn(CE_INC_NLEN);
return yyp;
}
memcpy(end+1, start, yyp - start);
end += yyp - start;
continue;
} else {
yyp = expand_define(d, yyp);
continue;
}
}
case ' ': case '\f': case '\r': case '\t': case '\v':
yyp++;
continue;
case '/':
c = *++yyp;
if (c == '*') {
yyp = skip_comment(yyp+1);
continue;
} else if (c == '/') {
yyp = skip_pp_comment(yyp+1)-1;
continue;
} else if (concat < 0) {
if (*end == '.') {
if (end[-1] == '.' && end[-2] == '/') {
end -= 2;
yyerrorn(CE_ILLPATH,
make_string(&buf[INC_LIST_MAXLEN],
(end + 1) - &buf[INC_LIST_MAXLEN]));
continue;
}
if (*--end == '/')
continue;
end++;
}
goto store_lg_char;
}
goto badchar;
case '\\':
yyp = skip_white_bsnl(yyp);
if (*yyp != '\\')
continue;
/* fall through */
default:
yyp++;
if (concat < 0) {
store_lg_char:
if (end == &buf[sizeof(buf)-2]) {
yyerrorn(CE_INC_NLEN);
return yyp;
}
*++end = c;
continue;
}
yyp--;
badchar:
if (concat)
yyerrorn(CE_INC_CONT);
yyp = skip2nl(yyp);
/* fall through */
{
int fd;
case '\n': case LC_EOF:
if (!concat) {
yyerrorn(CE_INC_START);
return yyp;
}
*++end = '\0';
/* try the '\"' relative match */
fd = open(&buf[INC_LIST_MAXLEN]+1, O_RDONLY|O_BINARY);
if (fd >= 0) {
relstart = &buf[INC_LIST_MAXLEN];
/* active variables: relstart, end, fd */
goto open_success;
}
/*
* semantics must not depend on temporary shortage of system
* resources. i.e. don't try standard include directories if the
* error was EMFILE, ENFILE or ENOMEM
*/
switch(errno) {
default:
if (concat != 2) {
case EMFILE:
#ifdef ENFILE
#if ENFILE != EMFILE
case ENFILE:
#endif
#endif
case ENOMEM:
relstart = &buf[INC_LIST_MAXLEN];
/* active variables: relstart, end */
goto open_failure;
}
}
goto stdinc_at_nl;
case '>':
{
int i;
union svalue sv;
struct incstate *is;
yyp = skip_white_bsnl(yyp + 1);
if (*yyp != '\n' && *yyp != LC_EOF) {
yyp = skip2nl(yyp);
}
*++end = '\0';
stdinc_at_nl:
/* path starts at relstart */
sv = driver_hook[H_INCLUDE_DIRS];
if (SV_GEN_TYPE(sv) == T_ARRAY) {
if (sv.p != inc_hook_value.p) {
set_inc_list(sv);
}
/*
* Search all include dirs specified.
*/
for (i=0; i < inc_list_size; i++) {
char *str;
mp_uint len;
sv = inc_list[i];
str = sv_string(sv, &len);
memcpy((relstart -= len), str, len);
fd = open(relstart, O_RDONLY|O_BINARY);
if (fd >= 0) {
open_success:
sv = make_string(relstart, end - relstart);
if (!sv.p) {
yyerrorn(CE_NOMEM);
return FILE_END;
}
break;
}
open_failure:
if (errno == EMFILE) {
yyerrorn(CE_MFILE, SV_NULL);
return FILE_END;
}
#ifdef ENFILE
else if (errno == ENFILE) {
yyerrorn(CE_NFILE, SV_NULL);
return FILE_END;
}
#endif
else {
/*
* In case of out of memory error, we'll pass the
* number 0 to the error hook.
*/
yyerrorn(CE_INC_NF,
make_string(relstart, end - relstart));
return yyp;
}
}
} else if (SV_TYPE(sv) == T_CLOSURE) {
char *cstr_start;
mp_int cstr_len;
struct counted_string cstr;
*++inter_sp = make_string(relstart, end - relstart);
push_svalue(CURRENT_FILE);
push_svalue(basestate.file);
sv = call_hook(
driver_hook[H_INCLUDE_DIRS], inter_fp->object, 3);
if (SV_IS_NUMBER(sv) || !SV_IS_STRING(sv) ||
(cstr = sv_string2(sv), cstr_start = cstr.start,
cstr_len = cstr.len, cstr_len >= sizeof(buf)))
{
yyerrorn(CE_HOOKFAIL_INC, sv);
return yyp;
}
if (!legal_path(cstr.start, cstr_len)) {
yyerrorn(CE_ILLPATH, sv);
return yyp;
}
memcpy(buf, cstr_start, cstr_len);
buf[cstr_len] = '\0';
fd = open(buf, O_RDONLY|O_BINARY);
if (fd < 0) {
/*
* Providing the filename for E[NM]FILE is not
* particularily useful, but it is less code than
* explicit freeing.
*/
if (errno == EMFILE) {
yyerrorn(CE_MFILE, sv);
return FILE_END;
}
#ifdef ENFILE
if (errno == ENFILE) {
yyerrorn(CE_NFILE, sv);
return FILE_END;
}
#endif
yyerrorn(CE_INC_NF, sv);
return yyp;
}
}
/* filename in sv */
store_include_info(sv);
is = alloc_gen(sizeof(struct incstate));
if (!is) {
yyerrorn(CE_NOMEM);
return FILE_END;
}
is->line = current_line;
/* actual linenumber is current_line - inctop->line + 1 */
is->file = sv;
is->outp = yyp;
is->next = inctop;
is->pragma_strong_types = pragma_strong_types;
if ( !(yyp = lex_map(fd, is)) ) {
free_gen(is);
return FILE_END;
}
return yyp;
}
}
case LC_POP:
yyp = expsp->ret;
expsp = expsp->pop;
continue;
case LC_DEFARG:
yyp = expand_defarg(yyp+1);
continue;
case LC_STRING:
if (expsp == &expstack[0]) {
yyp++;
continue;
}
yyp = ALIGN(yyp + 1 + 2*sizeof(char *), char *);
after_string = yyp;
yyp = ((char **)yyp)[-2];
yyp--;
/* fall through */
case '\"':
{
yyp++;
if (concat) {
if (concat < 0) {
yyerrorn(CE_INC_MIX);
return after_string ? after_string : yyp;
}
} else {
/* get first char to determine if it's an absolute path */
c = *yyp;
while (c == '\\' && (yyp[1] == '\n' || yyp[1] == '\r')) {
current_line++;
yyp += 2;
if (yyp[-1] ^ yyp[0] == '\n' ^ '\r')
yyp++;
c = *yyp;
}
if (c == '\"') {
/*
* No char in this string, ignore it.
* This code will accept #include "" <path> ,
* which can be considered a mostly harmless bug.
*/
yyp++;
continue;
}
if (c == '/') {
concat = 1;
yyp++;
end = &buf[INC_LIST_MAXLEN];
} else {
char *str;
mp_uint len;
concat = 2;
str = sv_string(CURRENT_FILE, &len);
end = &str[len];
yyp -= 3;
do {
concat = 1;
yyp += 3;
while (*--end != '/');
} while (end != str && !memcmp(yyp, "../", 3));
memcpy(&buf[INC_LIST_MAXLEN], str, end - str);
end += &buf[INC_LIST_MAXLEN] - str;
relstart = end;
}
*end = '/';
}
c = *yyp++;
for (;;) {
if (c > '/') { /* ASCII optimization */
do {
store_char:
if (end == &buf[sizeof(buf)-2]) {
yyerrorn(CE_INC_NLEN);
return yyp;
}
*++end = c;
c = *yyp++;
} while (c > '/');
}
if (c == '/') {
if (*end == '.') {
if (end[-1] == '.' && end[-2] == '/') {
concat = 1;
end -= 2;
if (end != &buf[INC_LIST_MAXLEN])
while (*--end != '/');
continue;
}
if (*--end == '/')
continue;
end++;
}
} else if (c == '\"') {
break;
} else if (c == '\n') {
yyerrorn(CE_NL_INC);
return yyp - 1;
} else if (c == LC_EOF) {
yyerrorn(CE_EOF_INC);
return yyp - 1;
}
goto store_char;
};
if (after_string) {
yyp = after_string;
after_string = 0;
}
/* Try to find extra strings to be concatenated in ANSI style */
continue;
}
case '<':
yyp++;
end = &buf[INC_LIST_MAXLEN+1];
*end = '/';
relstart = end;
concat = -1;
continue;
}
break;
}
}
static char *skip_comment(register char *p)
{
register char c;
for(;;) {
c = *p++;
if (c == '*') for (;;) {
c = *p++;
if (c == '*') continue;
if (c == '/') {
return p;
}
}
if (c <= LC_MAX) {
switch(c) {
case LC_NL:
nexpands=0;
current_line++;
break;
case LC_EOF:
yyerrorn(CE_EOF_COMMENT);
return p - 1;
case LC_POP:
p = expsp->ret;
expsp = expsp->pop;
break;
case LC_DEFARG:
p = expand_defarg(p);
break;
case LC_IDENT:
p = ALIGN(p + 2*sizeof(char *), char *);
p += 2;
break;
case LC_STRING:
p = ALIGN(p + 2*sizeof(char *), char *);
break;
}
}
}
}
static char *skip_pp_comment(p)
char *p;
{
char c;
while (expsp->pop != expsp) {
p = expsp->ret;
expsp = expsp->pop;
}
for (;;) {
c = *p;
if (!c) {
yyerrorn(CE_EOF_COMMENT);
return p;
}
p++;
if (c == '\n') {
nexpands=0;
current_line++;
return p;
}
}
}
/* skip white space in a #directive */
static char *skip_numdir_white(char *p) {
char c;
c = *p;
for(;;) {
if (lexwhite(c)) {
c = *++p;
continue;
}
if (c == '/') {
c = p[1];
if (c == '*') {
p = skip_comment(p+2);
c = *p;
continue;
} else if(c == '/') {
for (c = *(p += 2); c != '\n'; c = *++p);
return p;
}
}
break;
}
return p;
}
/* Skip white space and backslash/newline */
static char *skip_white_bsnl(char *p) {
char c;
c = *p;
for(;;) {
if (lexwhite(c)) {
c = *++p;
continue;
}
if (c == '/') {
c = p[1];
if (c == '*') {
p = skip_comment(p+2);
c = *p;
continue;
} else if(c == '/') {
return skip2nl(p+2);
}
}
if (c == '\\') {
c = p[1];
if (p[1] == '\n') {
current_line++;
c = *(p += 2);
continue;
}
if (p[1] == '\r' && p[2] == '\n') {
current_line++;
c = *(p += 3);
continue;
}
}
if (c <= LC_MAX_SPECIAL) {
if (c == LC_POP) {
p = expsp->ret;
expsp = expsp->pop;
c = *p;
continue;
} else if (c == LC_DEFARG) {
p = expand_defarg(p+1);
c = *p;
continue;
}
}
break;
};
return p;
}
static char *skip2nl(char *start) {
unsigned char c, *p;
p = (unsigned char *)start;
while (expsp->pop != expsp) {
p = expsp->ret;
expsp = expsp->pop;
}
c = *p;
for (;;) {
if (c == LC_EOF || c == '\n')
return p;
c = *++p;
}
}
static char *handle_pragma(str)
char *str;
{
if (LEXDEBUG && lexdebug)
printf("handle pragma:'%s'\n",str);
str = skip_numdir_white(str);
if (!memcmp(str, "strict_types", 12) && !isalunum(str[12])) {
pragma_strong_types = 2;
instrs[F_CALL_OTHER].ret_type = TYPE_UNKNOWN;
str += 12;
} else if (!memcmp(str, "save_types", 10) && !isalunum(str[10])) {
pragma_save_types = 1;
str += 10;
} else if (!memcmp(str, "strong_types", 12) && !isalunum(str[12])) {
pragma_strong_types = 1;
instrs[F_CALL_OTHER].ret_type = TYPE_ANY;
str += 12;
} else if (!memcmp(str, "optimize", 8) && !isalunum(str[8])) {
pragma_optimize = 1;
str += 14;
#if defined( DEBUG ) && defined ( TRACE_CODE )
} else if (!memcmp(str, "set_code_window", 15) && !isalunum(str[15])) {
extern void set_code_window();
set_code_window();
str += 15;
} else if (!memcmp(str, "show_code_window", 16) && !isalunum(str[16])) {
extern void show_code_window();
show_code_window();
str += 16;
#endif
}
str = skip_numdir_white(str);
if (*str != '\n') {
if (*str) {
yyerrorn(CE_UK_PRAGMA);
do ; while(*++str != '\n');
str++;
}
} else {
str++;
}
return str;
}
static struct ident *all_defines = 0, *permanent_defines = 0,
*undefined_permanent_defines = 0;
static INLINE int number(p_int i)
{
if (LEXDEBUG && lexdebug)
printf("returning number %ld.\n", (long)i);
yylval.constant.i = i << 1;
return YYF_CONSTANT;
}
int yylex();
char *count_string(char *yyp, struct counted_string *cstrp) {
char c;
mp_int len = -1;
cstrp->start = yyp;
for (;;) {
len++;
dont_count:
c = *yyp++;
if (c <= LC_MAX) {
if (c == '\n' || !c) {
cstrp->start--;
cstrp->len = 0;
yyerrorn(c ? CE_NL_STRING : CE_EOF_STRING);
return yyp - 1;
}
}
if (c == '\\') {
c = *yyp++;
if (!escchars[(unsigned char)c]) {
if (c == '\n' || c == '\r') {
yyp++;
if (c ^ *yyp == '\n' ^ '\r')
yyp++;
goto dont_count;
} else if (!c) {
cstrp->start--;
cstrp->len = 0;
yyerrorn(CE_EOF_STRING);
return yyp - 1;
} else {
if (IS_OCTAL(*yyp)) {
yyp++;
if (IS_OCTAL(*yyp))
yyp++;
}
continue;
}
}
}
if (c == '\"')
break;
}
cstrp->len = len;
return yyp;
}
static void free_mappings() {
int i;
struct incstate **pp;
i = NELEM(maptab);
pp = maptab;
do {
struct incstate *p, *next;
for (p = *pp, *pp++ = 0; p; p = next) {
next = p->next_hash;
munmap(p->mapstart, p->maplen);
if (p != &basestate)
free_gen(p);
}
} while(--i);
}
int yylex() {
register char *yyp;
register char c;
yyp = outp;
for(;;) {
switch(c = *yyp++) {
case 0:
{
if (inctop->next) {
static char call_other_return_types[] =
{ TYPE_ANY, TYPE_ANY, TYPE_UNKNOWN };
struct incstate *p;
p = inctop;
FREE_ALLOCED_SVALUE(p->file);
nexpands=0;
pragma_strong_types = p->pragma_strong_types;
instrs[F_CALL_OTHER].ret_type =
call_other_return_types[pragma_strong_types];
inctop = p->next;
yyp = p->outp;
if (!p->maplen)
free_gen(p);
store_include_end();
break;
}
if (iftop) {
struct ifstate *p = iftop;
yyerrorn(p->expect_else ? CE_NO_NUMELSE : CE_NO_NUMENDIF,
p->line - inctop->line + 1);
while(iftop) {
p = iftop;
iftop = p->next;
free_gen(p);
}
}
outp = yyp-1;
return 0;
}
case '\n':
nexpands=0;
current_line++;
case '\r':
/* if there are '\r's present, they usually come as '\r' '\n',
* which is OK with #directives. Alas, sometimes they come reversed.
* We don't want to reorder them because this would require to
* map the files read-write, and we would get page faults too.
*/
if (*yyp == '#') {
yyp++;
if (*yyp == '\'')
goto closure_literal;
if (yyp-1 != inctop->mapstart && yyp[-2] == '\n')
goto num_directive;
goto badchar;
}
case 0x1a: /* Used by some MSDOS editors as EOF */
case ' ': case '\t': case '\f': case '\v':
break;
case '+':
switch(c=*yyp++) {
case '+':
outp=yyp;
yylval.number = ULV_INC;
return YYF_ADDQ;
case '=':
yylval.number = ULV_ADD;
outp=yyp;
return YYF_ASSIGN;
default:
yyp--;
}
outp = yyp;
yylval.number = F_ADD;
return '+';
case '-':
switch(c=*yyp++) {
case '>':
outp=yyp;
return YYF_ARROW;
case '-':
outp=yyp;
yylval.number = ULV_DEC;
return YYF_ADDQ;
case '=':
yylval.number = ULV_SUB;
outp=yyp;
return YYF_ASSIGN;
default:
yyp--;
}
outp = yyp;
yylval.number = F_SUB;
return '-';
case '&':
switch(c=*yyp++) {
case '&':
outp=yyp;
yylval.number = F_LAND;
return YYF_LAND;
case '=':
yylval.number = ULV_AND;
outp=yyp;
return YYF_ASSIGN;
default:
yyp--;
}
outp = yyp;
yylval.number = F_AND;
return '&';
case '|':
switch(c=*yyp++) {
case '|':
outp=yyp;
yylval.number = F_LOR;
return YYF_LOR;
case '=':
yylval.number = ULV_OR;
outp=yyp;
return YYF_ASSIGN;
default: yyp--;
}
outp = yyp;
yylval.number = F_OR;
return '|';
case '^':
if (*yyp == '=') {
yyp++;
yylval.number = ULV_XOR;
outp=yyp;
return YYF_ASSIGN;
}
outp = yyp;
yylval.number = F_XOR;
return '^';
case '<':
c = *yyp++;;
if (c == '<') {
if (*yyp == '=') {
yyp++;
yylval.number = ULV_LSH;
outp=yyp;
return YYF_ASSIGN;
}
outp=yyp;
yylval.number = F_LSH;
return YYF_SHIFT;
}
if (c == '=') {
outp=yyp;
yylval.number = F_LE;
return YYF_COMPARE;
}
yyp--;
outp=yyp;
yylval.number = F_LT;
return '<';
case '>':
c = *yyp++;
if (c == '>') {
if (*yyp == '=') {
yyp++;
yylval.number = ULV_RSH;
outp=yyp;
return YYF_ASSIGN;
}
outp=yyp;
yylval.number = F_RSH;
return YYF_SHIFT;
}
if (c == '=') {
outp=yyp;
yylval.number = F_GE;
return YYF_COMPARE;
}
yyp--;
outp=yyp;
yylval.number = F_GT;
return YYF_COMPARE;
case '*':
if (*yyp == '=') {
yyp++;
yylval.number = ULV_MUL;
outp=yyp;
return YYF_ASSIGN;
}
outp=yyp;
yylval.number = F_MULTIPLY;
return '*';
case '%':
if (*yyp == '=') {
yyp++;
yylval.number = ULV_MOD;
outp=yyp;
return YYF_ASSIGN;
}
outp=yyp;
yylval.number = F_MOD;
return YYF_DIV;
case '/':
c = *yyp++;
if (c == '*') {
yyp = skip_comment(yyp);
break;
}
if (c == '/') {
yyp = skip_pp_comment(yyp);
break;
}
if (c == '=') {
yylval.number = ULV_DIV;
outp=yyp;
return YYF_ASSIGN;
}
yyp--;
outp=yyp;
yylval.number = F_DIVIDE;
return YYF_DIV;
case '=':
if (*yyp == '=') {
yyp++;
outp = yyp;
yylval.number = F_EQ;
return YYF_EQUALITY;
}
/* '=' is special because it is not only used for assignments, but
* also for initializations. Nontheless we store a value compatible
* to YYF_ASSIGN in yylval so that the parser can easily join
* the alternatives.
* note that is still has to check for possible call by reference,
* which necessiates ULV_HAIRY_ASSIGN
*/
yylval.number = ULV_ASSIGN;
outp=yyp;
return '=';
case '~':
yylval.number = F_COMPLEMENT;
outp=yyp;
return c;
case ':':
if (*yyp == c) {
yyp++;
outp = yyp;
return YYF_SCOPE;
}
outp=yyp;
return c;
case '?':
if (*yyp == '-' && yyp[1] == '>') {
/* New operator: test if an auto variable is defined in an object */
outp = yyp + 2;
return YYF_QARROW;
}
case ',': case ';':
case '(': case ')':
case '{': case '}':
case '[': case ']':
outp=yyp;
return c;
case '!':
if (*yyp == '=') {
yyp++;
outp = yyp;
yylval.number = F_NE;
return YYF_EQUALITY;
}
outp=yyp;
yylval.number = F_NOT;
return '!';
case '.':
if (*yyp == '.') {
yyp++;
outp = yyp;
return YYF_RANGE;
}
goto badchar;
case '#':
if (*yyp == '\'') {
struct ident *p;
struct idhash_ret idhr;
int efun_override;
closure_literal:
if (!isalunum(*++yyp)) {
extern int symbol_operator(char *, char **);
int i;
if ((i = symbol_operator(yyp, &outp)) < 0)
yyerrorn(CE_CL_NONAME);
yylval.closure.number = i + CLOSURE_EFUN_OFFS;
return F_CLOSURE;
}
idhr = idhash(yyp);
efun_override = 0;
if (idhr.p - yyp == 4 && !strncmp(yyp, "efun::", 6) ) {
efun_override = 1;
yyp = idhr.p + 2;
idhr = idhash(yyp);
}
outp = idhr.p;
p = make_shared_identifier(
yyp, idhr.p - yyp, idhr.hash, I_TYPE_GLOBAL);
if (!p) {
lexerror(CE_NOMEM);
return 0;
}
while (p->type > I_TYPE_GLOBAL) {
if (p->type == I_TYPE_RESWORD) {
int code;
switch(code = p->u.terminal.code) {
default:
{
/* There aren't efuns with reswords as names, and
* it is impossible to define local / global vars
* or functions with such a name. Thus, !p->inferior .
*/
yyerrorn(CE_NOCLOSURE_OP, code << 1);
code = CLOSURE_EFUN_OFFS;
break;
}
case YYF_IF:
code = F_BRANCH_ON_ZERO + CLOSURE_EFUN_OFFS;
break;
case YYF_DO:
code = F_BBRANCH_ON_NON_ZERO + CLOSURE_EFUN_OFFS;
break;
case YYF_WHILE:
/* the politically correct code /
/ was already taken, see above. */
code = F_BBRANCH_ON_ZERO + CLOSURE_EFUN_OFFS;
break;
case YYF_CONTINUE:
code = F_BRANCH + CLOSURE_EFUN_OFFS;
break;
case YYF_DEFAULT:
code = F_CSHARED0 + CLOSURE_EFUN_OFFS;
/* as bogus as we can possibly get :-) */
break;
case YYF_BREAK:
case YYF_RETURN:
case YYF_SSCANF:
case YYF_CATCH:
case YYF_SWITCH:
code += CLOSURE_EFUN_OFFS + F_BREAK - YYF_BREAK;
break;
}
yylval.closure.number = code;
return YYF_CLOSURE;
}
if ( !(p = p->inferior) )
break;
}
if (!p || p->type < I_TYPE_GLOBAL) {
if (p && p->type == I_TYPE_UNKNOWN)
free_shared_identifier(p);
yyerrorn(CE_CL_FUN_UNDEF, make_string(yyp, idhr.p - yyp));
yylval.closure.number = CLOSURE_EFUN_OFFS;
return YYF_CLOSURE;
}
if (efun_override && p->u.global.sim_efun >= 0 &&
simul_efun_table[p->u.global.sim_efun].nomask &&
p->u.global.efun >= 0 &&
master_ob.i)
{
union svalue res;
/* This privilege violation is special:
* Files loaded by the master must not get all privileges,
* thus the usual test for current_object == master_ob
* is not applicable.
*/
PUSH_NUMBER(PV_NOMASK_SIMUL_EFUN << 1);
push_svalue(CURRENT_FILE);
push_svalue(basestate.file);
PUSH_REFERENCED_SVALUE(make_string(p->name, p->namelen));
/* We couldn't reload the master right now, but we
* could might be able to reactivate a destructed one.
*/
assert_master_ob_loaded();
res = call_hook(
driver_hook[H_PRIVILEGE_VIOLATION], master_ob, 4);
if (!SV_IS_NUMBER(res) || res.i < 0)
{
yyerrorn(CE_NOMASK_SIM, p->name);
efun_override = 0;
} else if (!res.i) {
efun_override = 0;
}
}
switch(0) { default:
if (!efun_override) {
if (p->u.global.function >= 0) {
svalue sv;
sv = ALLOC_TTS(T_CLOSURE, 2, CLOSURE_PROTO_LFUN,
sizeof(struct lfun_closure));
if (sv.p) {
yylval.constant = sv;
SV_CLOSURE(sv).lfun.index = p->u.global.function;
SV_CLOSURE(sv).lfun.ob = all_proto_closures;
all_proto_closures = sv;
}
break;
}
if (p->u.global.sim_efun >= 0) {
yylval.closure.number =
p->u.global.sim_efun + CLOSURE_SIMUL_EFUN_OFFS;
break;
}
}
if (p->u.global.efun >= 0) {
yylval.closure.number =
p->u.global.efun + CLOSURE_EFUN_OFFS;
if (yylval.closure.number >
LAST_INSTRUCTION_CODE + CLOSURE_EFUN_OFFS)
{
yylval.closure.number =
efun_aliases[
yylval.closure.number - CLOSURE_EFUN_OFFS
- LAST_INSTRUCTION_CODE - 1
] + CLOSURE_EFUN_OFFS;
}
break;
}
if (p->u.global.variable >= 0) {
#if 0
extern int num_virtual_variables;
if (p->u.global.variable & VIRTUAL_VAR_TAG) {
/* Handling this would require an extra coding of
* this closure type, and special treatment in
* replace_program_lambda_adjust() .
*/
yyerrorn(CE_CL_VIRT_VAR);
yylval.closure.number = CLOSURE_IDENTIFIER_OFFS;
break;
}
yylval.closure.number =
p->u.global.variable + num_virtual_variables +
CLOSURE_IDENTIFIER_OFFS;
break;
#endif
}
yyerrorn(CE_CL_FUN_UNDEF, make_string(yyp, idhr.p - yyp));
yylval.closure.number = CLOSURE_EFUN_OFFS;
break;
}
return YYF_CONSTANT;
} else if ((yyp - 1 == inctop->mapstart || *(yyp-2) == '\n')) {
num_directive:
do {
c = *yyp++;
} while (lexwhite(c));
switch(c) {
case 'd':
if (!memcmp(yyp, "efine", 5) && lexwhite(yyp[5])) {
yyp = handle_define(yyp+6);
} else {
goto unrecognized_directive;
}
break;
case 'e':
if (!memcmp(yyp, "ndif", 4) && isspace(yyp[4])) {
yyp = skip_numdir_white(yyp+4);
if (iftop) {
struct ifstate *p = iftop;
if (p->expect_else &&
yyp[1] == '\n' && yyp[2] == LC_EOF)
{
inctop->lastif_line = p->line - inctop->line;
}
iftop = p->next;
free_gen(p);
} else {
yyerrorn(CE_NUMENDIF);
}
} else if (yyp[0] == 'l' &&
((yyp[1] == 's' && yyp[2] == 'e') ||
(yyp[1] == 'i' && yyp[2] == 'f') ) &&
isspace(yyp[3]))
{
/* #else of #elif */
if (iftop && iftop->expect_else) {
struct ifstate *p = iftop;
iftop = p->next;
free_gen(p);
outp = yyp + 3;
skip2nume((char *)0);
yyp = outp;
} else {
yyerrorn(CE_NUMELSE);
}
} else if (!memcmp(yyp, "cho", 3) && isspace(yyp[3])) {
yyp += 3;
do {
c = *yyp++;
if (c == '/') {
yyp = skip_numdir_white(yyp-1);
c = *yyp++;
}
fputc(c, stderr);
} while (c != '\n');
} else {
goto unrecognized_directive;
}
break;
case 'i':
if (!memcmp(yyp, "nclude", 6) && lexwhite(yyp[6])) {
yyp = handle_include(yyp+7);
break;
} else if (*yyp == 'f') {
yyp++;
if (lexwhite(*yyp)) {
union svalue cond;
outp = yyp;
cond = cond_get_exp(0);
FREE_SVALUE(cond);
outp = skip_white_bsnl(outp);
if (*outp != '\n' && *outp != LC_EOF) {
if (cond.p != CONST_INVALID.p)
yyerrorn(CE_NUMIF_GARBAGE);
outp = skip2nl(outp);
cond = CONST_INVALID;
} else if (*outp == '\n' && outp[1] == LC_POP) {
outp = expsp->ret - 1;
expsp = expsp->pop;
if (*outp != '\n')
fatal("Cannot prepare for skip2nume");
}
if (cond.p == CONST_INVALID.p)
skip2nume((char*)0);
else
handle_cond(cond.i);
yyp = outp;
break;
} else {
p_int cond;
cond = 0;
if (*yyp == 'n') {
yyp += cond = 1;
}
if (!memcmp(yyp, "def", 3) && lexwhite(yyp[3])) {
yyp = skip_white_bsnl(yyp + 3);
if (!isalunum(*yyp)) {
outp = yyp;
} else {
struct idhash_ret idhr;
idhr = idhash(yyp);
outp = idhr.p;
if (lookup_define(yyp, idhr.p - yyp, idhr.hash,
I_TYPE_DEFINE))
{
cond--;
}
}
handle_cond(cond);
yyp = outp;
break;
}
}
}
goto unrecognized_directive;
case 'p':
if (!memcmp(yyp, "ragma", 5) && lexwhite(yyp[5])) {
yyp = handle_pragma(yyp+6);
} else {
goto unrecognized_directive;
}
break;
case 'u':
if (!memcmp(yyp, "ndef", 4) && lexwhite(yyp[4])) {
struct idhash_ret idhr;
struct ident *p, **q;
yyp = skip_white_bsnl(yyp + 4);
if (!isalunum(*yyp))
break;
idhr = idhash(yyp);
p = lookup_define(
yyp, idhr.p - yyp, idhr.hash, I_TYPE_DEFINE);
yyp = idhr.p;
if (!p)
break;
/* lookup_define() moved p to *q */
q = &ident_table[idhr.hash & (ITABLE_SIZE-1)];
if (!p->u.define.permanent) {
if (p->inferior) {
p->inferior->next = p->next;
*q = p->inferior;
} else {
*q = p->next;
}
free_gen(p->u.define.exps.str - 1);
/* mark for later freeing by all_defines */
p->name = 0;
/* success */
} else {
if (p->inferior) {
p->inferior->next = p->next;
*q = p->inferior;
} else {
*q = p->next;
}
p->next = undefined_permanent_defines;
undefined_permanent_defines = p;
/* success */
}
} else {
goto unrecognized_directive;
}
break;
default:
yyp--;
unrecognized_directive:
yyerrorn(CE_UK_DIRECTIVE);
yyp = skip2nl(yyp);
if (*yyp == '\n') {
current_line++;
yyp++;
}
break;
}
break;
} else
goto badchar;
case '\'':
c = *yyp++;
if (c == '\\') {
c = escchars[*(unsigned char *)yyp++];
if (!c || *yyp++ != '\'') {
yyp--;
yyerrorn(CE_ILL_CHARCONST);
}
} else if (*yyp++ != '\'' || c == '\'') {
char *wordstart;
int quotes = 1;
union svalue name, symbol;
yyp -= 2;
while (*yyp == '\'') {
quotes++;
yyp++;
}
wordstart = yyp;
if (!isalpha(*yyp)) {
if (*yyp == '(' && yyp[1] == '{') {
outp = yyp + 2;
yylval.number = quotes;
return YYF_QUOTED_AGGREGATE;
}
if (*yyp == LC_IDENT) {
yyp = ALIGN(yyp + 1 + 2*sizeof(char *), char *);
outp = yyp + sizeof(short);
wordstart = ((char **)yyp)[-2];
yyp = wordstart + ((p_int *)yyp)[-1];
/* hash in ((short *)yyp[0] */
goto return_symbol;
}
yyerrorn(CE_ILL_CHARCONST);
outp = yyp;
yylval.constant.i = 0;
return YYF_CONSTANT;
}
while(isalunum(*++yyp));
outp = yyp;
return_symbol:
symbol = ALLOC(T_QUOTED, 1, sizeof(name));
yylval.constant = symbol;
if (symbol.p) {
name = make_global_string(wordstart, yyp - wordstart);
SV_QUOTES(symbol) = quotes;
SV_QUOTED(symbol) = name;
}
return YYF_CONSTANT;
}
yylval.constant.i = c << 1;
outp = yyp;
return YYF_CONSTANT;
case '\"':
outp = count_string(yyp, &yylval.string);
return YYF_STRING;
case '0':
c = *yyp++;
if ( c == 'X' || c == 'x' ) {
p_int l;
/* strtol() gets the sign bit wrong,
strtoul() isn't portable enough. */
l = 0;
--yyp;
while(leXdigit(c = *++yyp)) {
if (c > '9')
c = (c & 0xf) + ( '9' + 1 - ('a' & 0xf) );
l <<= 4;
l += c - '0';
}
outp=yyp;
return number(l);
}
yyp--;
if (!lexdigit(c) && (c != '.' || yyp[1] == '.') ) {
outp=yyp;
return number(0);
}
c = '0';
/* fall through */
case '1':case '2':case '3':case '4':
case '5':case '6':case '7':case '8':case '9':
{
char *numstart=yyp;
p_int l;
l = c - '0';
while(lexdigit(c = *yyp++)) l = (((l << 2)+l) << 1) +
#ifndef NOASCII
(c & 0xf ); /* can be done in the same step as the type conversion */
#else
(c - '0');
#endif
if (c == '.' && *yyp != '.') {
union svalue sv;
sv = ALLOC_FLOAT;
while(lexdigit(*yyp++));
c = *--yyp;
yylval.constant = sv;
if (sv.p) {
*yyp = 0;
SV_FLOAT(sv) = atof(numstart-1);
*yyp = c;
}
outp=yyp;
return YYF_CONSTANT;
}
--yyp;
outp = yyp;
return number(l);
}
case LC_POP:
yyp = expsp->ret;
expsp = expsp->pop;
continue;
case LC_DEFARG:
yyp = expand_defarg(yyp+1);
continue;
case LC_STRING:
if (expsp == &expstack[0])
goto badchar;
yyp = ALIGN(yyp + 1 + 2*sizeof(char *), char *);
yylval.string.start = ((char **)yyp)[-2];
yylval.string.len = ((p_int *)yyp)[-1];
outp = yyp;
return YYF_STRING;
{
struct ident *p;
case LC_IDENT:
if (expsp == &expstack[0])
goto badchar;
yyp = ALIGN(yyp + 1 + 2*sizeof(char *), char *);
p = make_shared_identifier( ((char **)yyp)[-2], ((p_int *)yyp)[-1],
((short *)yyp)[0], I_TYPE_UNKNOWN );
yyp += sizeof(short);
goto return_identifier;
case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':case 'G':case 'H':
case 'I':case 'J':case 'K':case 'L':case 'M':case 'N':case 'O':case 'P':
case 'Q':case 'R':case 'S':case 'T':case 'U':case 'V':case 'W':case 'X':
case 'Y':case 'Z':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':
case 'g':case 'h':case 'i':case 'j':case 'k':case 'l':case 'm':case 'n':
case 'o':case 'p':case 'q':case 'r':case 's':case 't':case 'u':case 'v':
case 'w':case 'x':case 'y':case 'z':case '_':
{
struct idhash_ret idhr;
yyp--;
idhr = idhash(yyp);
p = make_shared_identifier(yyp, idhr.p - yyp, idhr.hash,
I_TYPE_UNKNOWN);
yyp = idhr.p;
}
return_identifier:
if (!p) {
lexerror(CE_NOMEM);
return 0;
}
if (LEXDEBUG && lexdebug)
printf("returning identifier '%.*s'.\n", (int)p->namelen, p->name);
switch(p->type) {
case I_TYPE_DEFINE:
yyp = expand_define(p, yyp);
continue;
case I_TYPE_RESWORD:
outp = yyp;
yylval.number = p->u.terminal.value;
return p->u.terminal.code;
case I_TYPE_PARAM:
yylval.number = p->u.param;
outp = yyp;
return YYF_PARAM;
case I_TYPE_LOCAL:
yylval.ident = p;
outp = yyp;
return YYF_LOCAL;
default:
yylval.ident = p;
outp = yyp;
return YYF_IDENTIFIER;
}
}
badchar:
default:
outp = yyp;
yyerrorn(CE_BADCHAR, (p_int)c << 1);
continue;
}
}
}
extern YYSTYPE yylval;
void lex_close(p_int msg)
{
int i = 0;
while (inctop->next) {
struct incstate *p;
p = inctop;
i++;
FREE_ALLOCED_SVALUE(p->file);
inctop = p->next;
free_gen(p);
}
free_mappings();
while(iftop) {
struct ifstate *p;
p = iftop;
iftop = p->next;
free_gen(p);
}
if (msg)
yyerrorn(msg, i << 1);
outp = FILE_END;
}
void lex_open(int fd, svalue name) {
free_defines();
current_line = 1;
outp = lex_map(fd, &basestate);
if (outp)
inctop->file = name;
expsp = &expstack[0];
if (auto_include_string) {
expsp[1].ret = outp;
expsp[1].pop = expsp;
expsp++;
outp = auto_include_string;
}
basestate.line = basestate_firstline;
pragma_save_types = 0;
pragma_optimize = 0;
nexpands = 0;
}
struct ident *all_efuns = 0;
void initialize_lex()
{
extern struct ident builtin_identifiers[];
static short binary_operators[] = {
F_ADD, F_SUB, F_MULTIPLY, F_DIVIDE, F_MOD,
F_LT, F_GT, F_EQ, F_GE, F_LE, F_NE,
F_OR, F_XOR, F_LSH, F_RSH,
};
int i, n;
char mtext[MBUF_SIZE];
char *str;
#ifdef DEBUG
if (!BITNUM_IS_1(ITABLE_SIZE))
fatal("Select a power of two for ITABLE_SIZE\n");
#endif
pagemask = pagesize - 1;
for (i=0; i<ITABLE_SIZE; i++)
ident_table[i] = 0;
{ struct ident *p = builtin_identifiers; do {
struct idhash_ret idhr;
idhr = idhash(p->name);
p->hash = idhr.hash;
idhr.hash &= ITABLE_SIZE-1;
p->next = ident_table[idhr.hash];
ident_table[idhr.hash] = p;
} while (p = p->next_all); }
for (n=0; n < NELEM(instrs); n++) {
struct idhash_ret idhr;
struct ident *p;
if (instrs[n].Default == -1) continue;
idhr = idhash(instrs[n].name);
p = make_shared_identifier(
instrs[n].name, idhr.p - instrs[n].name, idhr.hash, I_TYPE_GLOBAL);
if (!p)
fatal("Out of memory\n");
p->type = I_TYPE_GLOBAL;
p->u.global.efun = n;
p->u.global.sim_efun = -1;
p->u.global.function = -2;
p->u.global.variable = -2;
p->next_all = all_efuns;
all_efuns = p;
}
for (i=0; i < NELEM(reswords); i++) {
struct idhash_ret idhr;
struct ident *p;
idhr = idhash(reswords[i].name);
p = make_shared_identifier(reswords[i].name,
idhr.p - reswords[i].name, idhr.hash, I_TYPE_RESWORD);
if (!p)
fatal("Out of memory\n");
p->type = I_TYPE_RESWORD;
p->u.terminal.code = reswords[i].code;
p->u.terminal.value = reswords[i].value;
}
for (i=0; i < NELEM(binary_operators); i++) {
n = binary_operators[i];
instrs[n].min_arg = instrs[n].max_arg = 2;
instrs[n].Default = 0;
instrs[n].ret_type = TYPE_ANY;
}
n = F_AND;
instrs[n].min_arg = instrs[n].max_arg = 2;
instrs[n].ret_type = TYPE_ANY;
n = F_COMPLEMENT;
instrs[n].min_arg = instrs[n].max_arg = 1;
instrs[n].Default = 0;
instrs[n].ret_type = TYPE_ANY;
n = F_NOT;
instrs[n].min_arg = instrs[n].max_arg = 1;
instrs[n].ret_type = TYPE_ANY;
add_permanent_define("__SIMA__", -1, "", 0);
mtext[0] = '"';
mtext[1] = '/';
SV_COUNT_STRING(master_name, str, n);
memcpy(mtext+2, str, n);
strcpy(mtext+2+n, "\"");
add_permanent_define("__MASTER_OBJECT__", -1, mtext, 0);
sprintf(mtext, "\"%5.5s%s\"", GAME_VERSION, PATCH_LEVEL);
add_permanent_define("__VERSION__", -1, mtext, 0);
add_permanent_define("__FILE__", -1, (char *)add_current_file, 1);
add_permanent_define("__LINE__", -1, (char *)add_current_line, 1);
add_permanent_define("__HOST_NAME__", -1, (char *)add_hostname, 1);
add_permanent_define("__DOMAIN_NAME__", -1, (char *)add_domainname, 1);
sprintf(mtext, "\"%s\"", query_host_ip_number());
add_permanent_define("__HOST_IP_NUMBER__", -1, mtext, 0);
sprintf(mtext, "%d", MAX_RECURSION);
add_permanent_define("__MAX_RECURSION__", -1, mtext, 0);
add_permanent_define("__EFUN_DEFINED__", 1, (char *)efun_defined, 1);
}
void commandline_define(char *flag) {
char mtext[MBUF_SIZE];
*mtext='\0';
sscanf(flag,"%*[^=]=%[ -~=]",mtext);
if ( strlen(mtext) >= MBUF_SIZE ) fatal("Macro name overflow\n");
add_permanent_define(flag, -1, mtext, 0);
}
/*
* string: "([^"\]|\.)*"
* parameter: [a-zA-Z_][a-zA-Z_0-9]*
* stringized parameter: # parameter
* token pasting: token ## token
*
* token pasting makes only sense if one of the tokens is a parameter
*/
#define GETID(r, yyp, type) \
{ \
struct idhash_ret idhr; \
\
idhr = idhash(yyp); \
(r) = make_shared_identifier((yyp), idhr.p - (yyp), idhr.hash, (type)); \
(yyp) = idhr.p; \
}
#define HIGH_WATERMARK (&mbuf[sizeof(mbuf)-12])
static char *handle_define(char *yyp) {
int nargs;
struct ident *r, *p, *last_arg;
char *cout;
char mbuf[MBUF_SIZE];
mp_int clen;
char *exps;
if (!isalunum(*yyp)) {
yyp = skip_white_bsnl(yyp);
if (!isalunum(*yyp)) {
yyerrorn(CE_SYNTAX);
return yyp;
}
}
GETID(r, yyp, I_TYPE_DEFINE)
if (!r) {
yyerrorn(CE_NOMEM);
return yyp;
}
if (r->type != I_TYPE_UNKNOWN) {
yyerrorn(CE_MREDEF, make_string(r->name, r->namelen));
return yyp;
}
r->type = I_TYPE_DEFINE;
nargs = -1;
last_arg = 0;
if (*yyp == '(') {
for(;;) {
do yyp++; while(lexwhite(*yyp));
if (!isalunum(*yyp)) {
if (*yyp == ')')
break;
yyp = skip_white_bsnl(yyp);
if (!isalunum(*yyp)) {
if (*yyp == ')')
break;
yyerrorn(CE_SYNTAX);
return yyp;
}
}
GETID(p, yyp, I_TYPE_DEFARG)
if (!p) {
yyerrorn(CE_NOMEM);
} else {
if (p->type != I_TYPE_UNKNOWN) {
yyerrorn(CE_MARG_DUP, make_string(p->name, p->namelen));
}
p->type = I_TYPE_DEFARG;
p->u.defarg = nargs++;
p->next_all = last_arg;
last_arg = p;
}
if (*yyp == ',') continue;
if (*yyp == ')') break;
yyp = skip_white_bsnl(yyp);
if (*yyp == ',') continue;
if (*yyp == ')') break;
yyerrorn(CE_SYNTAX);
return yyp;
}
nargs = ~nargs;
}
r->u.define.nargs = nargs;
cout = &mbuf[0];
for (;;) {
if (cout >= HIGH_WATERMARK) {
mtext_overflow:
yyerrorn(CE_MTEXT_OVERFL);
yyp = skip2nl(yyp);
break;
}
switch(*yyp) {
case '"':
*cout = LC_STRING;
cout = ALIGN(cout+1 + sizeof(struct counted_string), char *);
yyp = count_string(yyp+1, &((struct counted_string *)cout)[-1]);
continue;
case '\'':
do {
*cout++ = *yyp++;
if (cout >= HIGH_WATERMARK)
goto mtext_overflow;
} while (*yyp == '\'');
if (*yyp == '\\')
*cout++ = *yyp++;
if (!*yyp || *yyp == '\n') {
break;
}
if (yyp[1] == '\'') {
*cout++ = *yyp++;
*cout++ = *yyp++;
}
continue;
case '\\':
yyp = skip_white_bsnl(yyp);
if (*yyp == '\\')
yyp++;
continue;
case '\n':
break;
case ' ': case '\t':case '\f':case '\v':case '\r':
yyp++;
continue;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b':
case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i':
case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':
case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w':
case 'x': case 'y': case 'z': case '_':
{
struct idhash_ret idhr;
mp_int len;
idhr = idhash(yyp);
len = idhr.p - yyp;
p = lookup_define(yyp, len, idhr.hash, I_TYPE_DEFARG);
if (p) {
*cout++ = LC_DEFARG;
*cout++ = p->u.defarg;
} else {
*cout = LC_IDENT;
cout = ALIGN(cout+1, char *);
((char **)cout)[0] = yyp;
((p_int *)cout)[1] = len;
*(short *)&((char **)cout)[2] = idhr.hash;
cout += sizeof(char *) * 2 + sizeof(short);
}
yyp = idhr.p;
continue;
}
case '/':
if (yyp[1] == '*' || yyp[1] == '/') {
yyp = skip_white_bsnl(yyp);
continue;
}
/* else fall through */
default:
*cout++ = *yyp++;
continue;
}
break; /* loop end */
}
*cout++ = LC_POP;
while ((p = last_arg)) {
last_arg = p->next_all;
free_shared_identifier(p);
}
r->u.define.permanent = 0;
r->u.define.special = 0;
clen = ALIGNI((cout - &mbuf[0]) + 1, char *);
exps = alloc_gen(clen);
if (!exps) {
free_shared_identifier(r);
yyerrorn(CE_NOMEM);
return FILE_END;
}
*exps++ = ' '; /* won't match '\n' if we have a '#' as first char */
memcpy(exps, mbuf, clen-1);
r->u.define.exps.str = exps;
r->next_all = all_defines;
all_defines = r;
return yyp;
}
static void
add_permanent_define(char *name, int nargs, char *exps, char special)
{
struct ident *p;
struct idhash_ret idhr;
idhr = idhash(name);
p = make_shared_identifier(name, idhr.p - name, idhr.hash, I_TYPE_DEFINE);
if (!p) {
error(IE_NOMEM);
}
if (p->type != I_TYPE_UNKNOWN) {
error(IE_MACROREDEF, name);
return;
}
p->type = I_TYPE_DEFINE;
p->u.define.nargs = nargs;
p->u.define.permanent = 1;
p->u.define.special = special;
if (!special) {
mp_int len;
char *new;
len = strlen(exps);
new = alloc_gen(ALIGNI(len+1, char *));
memcpy(new, exps, len);
new[len] = LC_POP;
exps = new;
}
p->u.define.exps.str = exps;
p->next_all = permanent_defines;
permanent_defines = p;
}
void free_defines() {
struct ident *p, *q;
for(p = all_defines; p; p = q) {
q = p->next_all;
if (p->name) {
free_gen(p->u.define.exps.str - 1);
free_shared_identifier(p);
} else { /* has been undef'd. */
free_gen((char *)p);
}
}
all_defines = 0;
for (p = undefined_permanent_defines; p; p = q) {
struct ident *curr, **prev;
q = p->next;
p->next = 0;
prev = &ident_table[p->hash & (ITABLE_SIZE-1)];
while (curr = *prev) {
if (curr->hash == p->hash && curr->namelen == p->namelen &&
!memcmp(curr->name, p->name, p->namelen))
{
/* found it */
p->next = curr->next;
break;
}
prev = &curr->next;
} /* not found, create new one */
p->inferior = curr;
*prev = p;
}
undefined_permanent_defines = 0;
nexpands = 0;
}
static struct ident *lookup_define(char *s, mp_int len, int hash, int type) {
struct ident *curr, *prev;
int h;
h = hash & (ITABLE_SIZE - 1);
curr = ident_table[h];
prev = 0;
while (curr) {
if (curr->type == type && curr->namelen == len &&
!memcmp(curr->name, s, len))
{
/* found it */
if (prev) { /* not at head of list */
prev->next = curr->next;
curr->next = ident_table[h];
ident_table[h] = curr;
}
return curr;
}
prev = curr;
curr = curr->next;
} /* not found */
return 0;
}
static char *expand_define(struct ident *id, char *yyp) {
union {
struct expand_stack *e;
char *s, **sp;
p_int *ip; short *shp;
struct counted_string *cs;
} tmpsp;
char **args;
if (nexpands++ > EXPANDMAX) {
yyerrorn(CE_MEXP_NUM);
return FILE_END;
}
tmpsp.e = expsp;
if (id->u.define.nargs >= 0) {
char **arg_end;
int i;
int nest;
/*
* set i as a flag that we are searching the opening bracket,
* not the closing bracket yet.
*/
i = 1;
/*
* search for '(' . If there are to be no arguments, search for the
* closing bracket too.
*/
for (;;) {
switch (*yyp) {
case '\n':
current_line++;
case ' ': case '\t':case '\f':case '\v':case '\r':
yyp++;
continue;
case LC_POP:
yyp = expsp->ret;
expsp = expsp->pop;
continue;
case LC_DEFARG:
yyp = expand_defarg(yyp+1);
continue;
case '/':
if (yyp[1] == '*') {
yyp = skip_comment(yyp+2);
continue;
} else if (yyp[1] == '/') {
yyp = skip_pp_comment(yyp+2);
continue;
}
goto no_opening_bracket;
case ')':
yyp++;
if (!i)
goto arguments_fetched;
/* fall through */
default:
/*
* including '\\' in the switch would span a much larger
* table for ASCII .
*/
if (*yyp == '\\') {
if (yyp[1] == '\n' || yyp[1] == '\r') {
yyp += 2;
current_line++;
if (yyp[-1] ^ yyp[0] == '\n' ^ '\r')
yyp++;
continue;
}
}
no_opening_bracket:
yyerrorn(CE_MEXP_NOBRAC);
return yyp;
case '(':
i = -id->u.define.nargs;
if (!i) {
continue;
}
break;
}
break;
}
/* We might need space to expand macro arguments */
args = (void *)&tmpsp.e->arg;
i = -id->u.define.nargs;
tmpsp.s = (void *)((char *)args + id->u.define.nargs * sizeof(char *));
arg_end = tmpsp.sp;
if (tmpsp.e + 2 >= &expstack[EXPSTACK_SIZE - 1]) {
yyerrorn(CE_MEXP_NEST);
return FILE_END;
}
*tmpsp.s++ = ' '; /* avoid matching '\n' if first char is '#' */
arg_end[i] = tmpsp.s;
for (nest = 0;;) {
char c;
if (tmpsp.e + 2 >= &expstack[EXPSTACK_SIZE - 1]) {
yyerrorn(CE_MEXP_NEST);
return FILE_END;
}
/*
* '\\' '\n' is not implemented outside strings because it isn't
* really useful (you can use ordinary newlines) and it would
* hurt performance, either by enlargung the switch table &
* degrading locality, or by necessiating an extra test in the
* heavily used default case
*/
switch(c = *yyp++) {
case '(':
nest++;
*tmpsp.s++ = c;
continue;
case ')':
if (--nest < 0)
break;
*tmpsp.s++ = c;
continue;
case '\"':
*tmpsp.s = LC_STRING;
tmpsp.s = ALIGN(tmpsp.s+1+sizeof(char)*2, char *);
yyp = count_string(yyp, &tmpsp.cs[-1]);
continue;
case '\'':
*tmpsp.s++ = c;
if ((c = *yyp) == '\\') {
*tmpsp.s++ = '\\';
if (!escchars[*(unsigned char *)yyp]) {
if (IS_OCTAL(*yyp)) {
*tmpsp.s++ = *yyp++;
if (IS_OCTAL(*yyp)) {
*tmpsp.s++ = *yyp++;
if (IS_OCTAL(*yyp))
*tmpsp.s++ = *yyp++;
}
} else continue;
}
*tmpsp.s++ = *yyp++;
if (*yyp == '\'') {
yyp++;
*tmpsp.s++ = '\'';
}
} else {
if ((((int)c - LC_MAX) >> 8) + yyp[1] == '\'') {
tmpsp.s[0] = yyp[0];
tmpsp.s[1] = yyp[1];
yyp += 2;
tmpsp.s += 2;
}
}
continue;
case '\n':
current_line++;
case '\r':
continue;
case '#':
*tmpsp.s++ = c;
if (*yyp == '\'') {
yyp++;
*tmpsp.s++ = '\'';
if (isalunum(c = *yyp)) {
yyp++;
*tmpsp.s++ = c;
/*
* transferring the rest here would need overflow
* checks, thus we simply let the main loop do the
* rest.
*/
continue;
} else {
extern int symbol_operator(char *, char **);
char *end;
if (symbol_operator(yyp, &end) < 0)
continue;
/* longest item is #'[<..<] */
strncpy(tmpsp.s, yyp, end - yyp);
tmpsp.s += end - yyp;
yyp = end;
}
}
continue;
case '/':
if (*yyp == '*') {
yyp = skip_comment(yyp + 1);
continue;
} else if (*yyp == '/') {
yyp = skip_pp_comment(yyp + 1);
continue;
}
*tmpsp.s++ = '/';
continue;
case ',':
if (!nest) {
if (++i) {
*tmpsp.s++ = LC_POP;
arg_end[i] = tmpsp.s;
continue;
}
}
/* fall through */
default:
*tmpsp.s++ = c;
continue;
case LC_IDENT:
*tmpsp.s = c;
tmpsp.s = ALIGN(tmpsp.s+1+sizeof(char)*2, char *);
yyp = ALIGN(yyp+sizeof(struct counted_string), char *);
tmpsp.sp[-2] = ((char **)yyp)[-2];
tmpsp.ip[-1] = ((p_int *)yyp)[-1];
continue;
case LC_STRING:
*tmpsp.s = c;
tmpsp.s =
ALIGN(tmpsp.s+1+sizeof(struct counted_string), char *);
yyp = ALIGN(yyp, char *);
tmpsp.sp[-2] = ((char **)yyp)[-2];
tmpsp.ip[-1] = ((p_int *)yyp)[-1];
tmpsp.shp[0] = ((short *)yyp)[ 0];
tmpsp.s += sizeof(short);
yyp += sizeof(short);
continue;
case LC_POP:
yyp = expsp->ret;
expsp = expsp->pop;
continue;
case LC_DEFARG:
yyp = expand_defarg(yyp);
continue;
}
}
tmpsp.e = ALIGN(tmpsp.s, char *);
}
arguments_fetched:
if (tmpsp.e + 1 >= &expstack[EXPSTACK_SIZE - 1]) {
yyerrorn(CE_MEXP_NEST);
return FILE_END;
}
if (id->u.define.special) {
return (*id->u.define.exps.fun)(yyp, args, tmpsp.e);
}
tmpsp.e[1].pop = expsp;
tmpsp.e[1].ret = yyp;
tmpsp.e[1].arg = args;
expsp = tmpsp.e + 1;
return id->u.define.exps.str;
}
static char *expand_defarg(char *p) {
int n;
/*
* expand_define() already makes sure that there is space for
* one argument expansion in expstack.
*/
n = *(unsigned char *)p++;
expsp[1].pop = expsp;
expsp[1].ret = p;
if (expsp == &expstack[0]) {
yyerrorn(CE_BADCHAR, (p_int)LC_DEFARG << 1);
return p;
}
p = expsp->arg[n];
expsp++;
return p;
}
char mygetc() {
char c;
for (;;) {
c = *outp++;
switch(c) {
case LC_POP:
outp = expsp->ret;
expsp = expsp->pop;
break;
case LC_DEFARG:
outp = expand_defarg(outp);
break;
default:
return c;
}
}
}
void myungetc(char c) {
if ( ((p_int)(outp-1) & -sizeof(char *)) == (p_int)&expsp->arg ) {
/* We have been called before, and have some space left in expsp->arg */
*--outp = c;
return;
}
if (c == '\"') {
/*
* We must not separate the '\"' from the rest of the string, because
* this implementation tolerates LC_POP inside strings.
* The caller must not unget a '\"' unless it is freshly read.
*/
--outp;
return;
}
/*
* store the character like a macro argument expansion.
* (ab)use the space in expsp->arg to place the character itself
* and LC_POP in it.
*/
expsp[1].ret = outp;
expsp[1].pop = expsp;
expsp++;
outp = (char *)(&expsp->arg+1) - 2;
outp[0] = c;
outp[1] = LC_POP;
}
#define unget1c(c) (outp--)
/*
* once you got the starting character of a string, you shouldn't use exgetc,
* because it filters out comments without respecting strings
*/
static int exgetc() {
register char c;
for (;;) {
char *start;
mp_int len;
struct idhash_ret idhr;
c=mygetc();
if (isalpha(c) || c=='_') {
start = outp - 1;
idhr = idhash(start);
len = idhr.p - start;
check_defined:
if (len == 7 && !memcmp(start, "defined", 7)) {
idhr.p = skip_white_bsnl(idhr.p);
if (*idhr.p != '(') {
yyerrorn(CE_DEFD_NOBRAC);
outp = idhr.p;
c = mygetc();
continue;
}
idhr.p = skip_white_bsnl(idhr.p+1);
c = *idhr.p;
if (isalpha(c) || c == '_') {
start = idhr.p;
idhr = idhash(idhr.p);
len = idhr.p - start;
} else if (c == LC_IDENT && expsp != &expstack[0]) {
idhr.p =
ALIGN(idhr.p + 1 + sizeof(struct counted_string), char *);
start = ((char **)idhr.p)[-2];
len = ((p_int *)idhr.p)[-1];
idhr.hash = ((short*)idhr.p)[0];
idhr.p += sizeof(short);
} else {
len = 0;
}
idhr.p = skip_white_bsnl(idhr.p);
if (*idhr.p != ')') {
yyerrorn(CE_DEFD_ENDBRAC);
outp = idhr.p;
c = mygetc();
continue;
}
idhr.p++;
c = lookup_define(start, len, idhr.hash, I_TYPE_DEFINE) ?
'1' : '0';
unget_cond:
/* do the analogue to myungetc(' '),myungetc('c'); */
expsp[1].ret = idhr.p;
expsp[1].pop = expsp;
expsp++;
idhr.p = (char *)(&expsp->arg+1) - 3;
idhr.p[0] = c;
idhr.p[1] = ' ';
idhr.p[2] = LC_POP;
outp = idhr.p;
return ' ';
} else {
struct ident *p;
p = lookup_define(start, len, idhr.hash, I_TYPE_DEFINE);
if (!p) {
c = '0';
goto unget_cond;
} else {
outp = expand_define(p, idhr.p);
}
}
} else if (c == LC_IDENT && expsp != &expstack[0]) {
idhr.p = ALIGN(outp + 1 + sizeof(struct counted_string), char *);
start = ((char **)idhr.p)[-2];
len = ((p_int *)idhr.p)[-1];
idhr.hash = ((short*)idhr.p)[0];
idhr.p += sizeof(short);
goto check_defined;
} else if (c == '\\' && (*outp == '\n' || *outp == '\r') ||
c == '/' && (*outp == '*' || *outp == '/' ) )
{
outp = skip_white_bsnl(outp-1);
return ' ';
} else {
break;
}
}
return c;
}
#define BNOT 1
#define LNOT 2
#define UMINUS 3
#define UPLUS 4
#define LAND 1
#define LOR 2
#define QMARK 3
/* lookup table for characters >= ' ' and <= '~'.
* 0 for no operator, else index into optab2.
*/
static const char _optab[]=
{0,6,0,0,0,46,50,0,0,0,2,18,0,14,0,10,0,0,0,0,0,0,0,0,0,0,0,0,22,42,32,68,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,0,1};
/* optab2[index-1] : operation code for unary operator, 0 for none.
* optab[index+0 .. +3 .. +6 ...] :
* two character binary operators: second character, operation code, priority
* one character binary operator & end: 0, operation code, priority
* end: 0, 0
*/
static const unsigned char optab2[]=
{
BNOT,0,0, F_MULTIPLY,11,
LNOT,'=',F_NE,7,0,0, F_DIVIDE,11,
UMINUS,0,F_SUB,10,
UPLUS,0,F_ADD,10,
0,'<',F_LSH,9,'=',F_LE,8,0,F_LT,8,
0,'>',F_RSH,9,'=',F_GE,8,0,F_GT,8,
0,'=',F_EQ,7,0,0, F_MOD,11,
0,'&',LAND,3,0,F_AND,6,0,'|',LOR,2,0,F_OR,4,
0,0,F_XOR,5,0,0,QMARK,1
};
#define optab1 (_optab-' ')
static union svalue cond_get_exp(int priority) {
int c, x;
union svalue sv1, sv2;
do c=exgetc(); while ( lexwhite(c) );
if ( c=='(' ) {
sv1 = cond_get_exp(0);
if (sv1.p == CONST_INVALID.p)
return CONST_INVALID;
do c=exgetc(); while ( lexwhite(c) );
if ( c!=')' ) {
yyerrorn(CE_NUMIF_BNPAIRD);
if (c == '\n') unget1c('\n');
}
} else if ( ispunct(c) ) {
if (c == '"') {
struct counted_string cstr;
outp = count_string(outp, &cstr);
sv1 = concat_strings(cstr.start, cstr.len, 0);
} else {
x=optab1[c];
if (!x) {
yyerrorn(CE_NUMIF_BADCHAR);
return CONST_INVALID;
}
sv1 = cond_get_exp(12);
x = optab2[x-1];
if (x == LNOT) {
sv1.i = !sv1.i << 1;
} else {
if (!SV_IS_NUMBER(sv1)) {
if (sv1.p != CONST_INVALID.p) {
yyerrorn(CE_NUMIF_IT_UOP);
FREE_ALLOCED_SVALUE(sv1);
}
return CONST_INVALID;
}
switch (x) {
case BNOT : sv1.i ^= ~1; break;
case UMINUS: sv1.i = -sv1.i; break;
case UPLUS : sv1.i = sv1.i; break;
default :
yyerrorn(CE_NUMIF_ILL_UOP);
return CONST_INVALID;
}
}
}
} else {
if ( !lexdigit(c) ) {
if (c == LC_STRING) {
struct counted_string *csp;
csp = ALIGN(outp+sizeof(*csp), char *);
outp = (char *)csp;
sv1 = concat_strings(csp->start, csp->len, 0);
} else if (c == '\n' || c == LC_EOF) {
yyerrorn(CE_NL_NUMIF);
unget1c(c);
} else yyerrorn(CE_NUMIF_BADCHAR);
return CONST_INVALID;
} else {
p_int value;
int base;
value=0;
if ( c!='0' ) {
base=10;
} else {
c=mygetc();
if ( c=='x' || c=='X' ) {
base=16;
c=mygetc();
} else base=8;
}
for(;;) {
if ( isdigit(c) ) x = -'0';
else if ( isupper(c) ) x = -'A'+10;
else if ( islower(c) ) x = -'a'+10;
else break;
x+=c;
if ( x > base ) break;
value=value*base+x;
c=mygetc();
}
myungetc(c);
sv1.i = value << 1;
}
}
for (;;) {
int value;
do c=exgetc(); while ( lexwhite(c) );
if ( !ispunct(c) ) break;
if (c == '"') {
unget1c('"');
c = '+';
}
x=optab1[c];
if (!x) break;
value = mygetc();
for(;;x+=3) {
if ( !optab2[x] ) {
unget1c(value);
if ( !optab2[x+1] ) {
yyerrorn(CE_NUMIF_ILL_OPU);
FREE_SVALUE(sv1);
return CONST_INVALID;
}
break;
}
if (value == optab2[x]) break;
}
if (priority >= optab2[x+2]) {
if (optab2[x]) myungetc(value);
break;
}
sv2 = cond_get_exp(optab2[x+2]);
x = optab2[x+1];
switch(x) {
case LAND:
if (sv1.i) {
FREE_SVALUE(sv1);
sv1 = sv2;
} else {
FREE_SVALUE(sv2);
}
break;
case LOR:
if (!sv1.i)
sv1 = sv2;
else
FREE_SVALUE(sv2);
break;
case QMARK:
FREE_SVALUE(sv1);
do c=exgetc(); while( lexwhite(c) );
if (c != ':') {
yyerrorn(CE_NUMIF_QMARK);
myungetc(c);
FREE_SVALUE(sv2);
return CONST_INVALID;
}
if (sv1.i) {
sv1 = cond_get_exp(1);
FREE_SVALUE(sv1);
sv1 = sv2;
} else {
FREE_SVALUE(sv2);
sv1 = cond_get_exp(1);
}
break;
default:
{
static struct efun_closure tmp_closure = {
T_CLOSURE, /* type */
1, /* ref */
CLOSURE_EFUN, /* closure_type */
TO_SVALUE(&nil_object)
};
tmp_closure.closure_type = CLOSURE_EFUN + x;
*++inter_sp = (sv1);
*++inter_sp = (sv2);
call_lambda(TO_SVALUE(&tmp_closure), 2);
sv1 = *inter_sp--;
if (inter_errno) {
inter_errno = 0;
yyerrorn(CE_NUMIF_ERROR);
FREE_SVALUE(sv1);
return CONST_INVALID;
}
}
}
}
myungetc(c);
return sv1;
}
static void set_inc_list(svalue hook)
{
struct array *v;
int i;
char *p;
p_uint len, max;
union svalue *svp, sv;
v = &SV_ARRAY(hook);
svp = v->member;
for (i = 0, max = 0; i < VEC_SIZE(v); i++, svp++) {
sv = *svp;
if (SV_IS_NUMBER(sv) || !SV_IS_STRING(sv)) {
error(IE_BADINCPATH, sv);
}
p = sv_string(sv, &len);
for(;;) {
if (!len)
break;
if (*p == '/') {
p++;
len--;
} else if (*p == '.' && len > 1 && p[1] == '/') {
p += 2;
len -= 2;
} else {
break;
}
}
/*
* Make sure that no master error compromises the security of the
* account.
*/
if (!legal_path(p, len)) {
error(IE_BADINCPATH, p);
}
if (*p == '.' && len == 1) {
error(IE_BADINCPATH, p);
return;
}
if (len >= 2 && p[len -1] == '.' && p[len - 2] == '/') {
error(IE_BADINCPATH, p);
return;
}
sv = make_global_string(p, len);
if (!sv.p) {
error(IE_NOMEM);
return;
}
if (max < len)
max = len;
FREE_ALLOCED_SVALUE(*svp);
*svp = sv;
}
if (max > INC_LIST_MAXLEN) {
return;
}
inc_hook_value = hook;
inc_list = v->member;
inc_list_size = VEC_SIZE(v);
inc_list_maxlen = max;
}
void clear_auto_include_string()
{
if (auto_include_string) {
x_free(auto_include_string-1);
auto_include_string = 0;
}
}
union svalue *f_set_auto_include_string(sp)
union svalue *sp;
{
char *s;
mp_uint len;
union svalue sv;
sv = *sp;
if (SV_IS_NUMBER(sv) || !SV_IS_STRING(sv)) {
bad_efun_arg(1);
return sp;
}
if (_privilege_violation(PV_SET_AUTO_INCLUDE_STRING << 1, *sp, sp) > 0)
{
clear_auto_include_string();
s = sv_string(sp, &len);
auto_include_string = x_alloc(len+2);
*auto_include_string++ = '\n';
memcpy(auto_include_string, s, len);
s = auto_include_string;
for (basestate_firstline = 0; *s; ) {
if (*s < LC_MAX_SPECIAL)
*s = ' ';
basestate_firstline -= *s++ == '\n';
}
*s = LC_POP;
}
FREE_ALLOCED_SVALUE(sv);
return sp - 1;
}
/* mode < 0 : count mode == 0: count & quote mode > 0: quote */
char *add_input(const char *str, mp_int mode, char *yyp) {
mp_int len;
char *dest;
struct expand_stack *newsp;
len = mode;
if (mode <= 0)
len = strlen(str);
dest = (char *)&expsp[1];
newsp = ALIGN(dest + len + 3, char *);
if (newsp > &expstack[EXPSTACK_SIZE-1]) {
yyerrorn(CE_MEXP_NEST);
return yyp;
}
newsp->pop = expsp;
newsp->ret = yyp;
expsp = newsp;
yyp = dest;
if (mode >= 0)
*dest++ = '"';
memcpy(dest, str, mode);
str += mode;
if (mode >= 0)
*dest++ = '"';
*dest = LC_POP;
return yyp;
}
static char *add_current_file(char *yyp, char **args) {
struct counted_string cstr;
cstr = sv_string2(CURRENT_FILE);
return add_input(cstr.start, cstr.len, yyp);
}
static char *add_current_line(char *yyp, char **args) {
char buf[12];
sprintf(buf, "%d", current_line - inctop->line + - inctop->line + 1);
return add_input(buf, -1, yyp);
}
static char *add_hostname(char *yyp, char **args) {
return add_input(query_host_name(), 0, yyp);
}
static char *add_domainname(char *yyp, char **args) {
char tmp[257];
getdomainname(&tmp[1], sizeof(tmp)-1);
tmp[sizeof(tmp)-1] = '\0';
return add_input(tmp, 0, yyp);
}
static char *efun_defined(char *yyp, char **args, struct expand_stack *sp) {
static char fakemain[] = { '~', LC_POP };
struct expand_stack *oldsp;
struct ident *p;
int token;
char *expand;
oldsp = sp;
sp++;
sp->pop = sp;
sp->ret = fakemain;
expsp = sp;
outp = args[0];
token = yylex();
expsp = oldsp;
expand = " 0 ";
if (token == YYF_IDENTIFIER) {
p = yylval.ident;
if (p->type == I_TYPE_UNKNOWN) {
free_shared_identifier(p);
} else {
/* p->type == I_TYPE_GLOBAL */
if (p->u.global.efun >= 0)
expand = " 1 ";
}
}
yyp = add_input(expand, -1, yyp);
return yyp;
}
void remove_unknown_identifier() {
int i;
struct ident *id, *next;
for (i = ITABLE_SIZE; --i >= 0; ) {
id = ident_table[i];
for ( ; id; id = next) {
next = id->next;
if (id->type == I_TYPE_UNKNOWN)
free_shared_identifier(id);
}
}
}
#ifdef MALLOC_smalloc
void count_lex_refs() {
int i;
struct ident *id;
/* Identifier */
for (i = ITABLE_SIZE; --i >= 0; ) {
id = ident_table[i];
for ( ; id; id = id->next) {
count_ref_from_string(id->name);
note_malloced_block_ref((char *)id);
}
}
for (id = permanent_defines; id; id = id->next_all) {
if (!id->u.define.special)
note_malloced_block_ref(id->u.define.exps.str);
}
if (auto_include_string)
note_malloced_block_ref(auto_include_string-1);
}
#endif /* MALLOC_smalloc */
char *lex_error_context() {
extern int yychar;
static char buf[20];
char *end;
mp_int len;
strcpy(buf, yychar == -1 ? (len = 5, "near ") : (len = 7, "before "));
if (!yychar) {
end = buf;
} else {
strncpy(buf + len, outp, sizeof buf - 1 - len);
buf[sizeof buf - 1] = '\n';
end = strchr(buf, '\n');
}
for (;;) {
if (end == buf) {
strcpy(buf+len, "end of line");
break;
}
if (!lexwhite(end[-1])) {
*end = '\0';
break;
}
end--;
}
return buf;
}