X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=userv.git;a=blobdiff_plain;f=parser.c;h=b2c2b03896c41bb3c3450cf8d12d08d1b8f6128e;hp=4b55743c1791d99a2ffab60f8bda6e43e662b5d1;hb=ae541decab234d800211990e9f077b1aada92d06;hpb=464d71c37246e556de9ec05f7b97af834a5224ee diff --git a/parser.c b/parser.c index 4b55743..b2c2b03 100644 --- a/parser.c +++ b/parser.c @@ -3,9 +3,10 @@ * configuration file parser; this file is actually #included from * lexer.c, which is generated using flex from lexer.l, in turn from * lexer.l.m4. It's in a separate file so that we don't have to worry - * about m4 quoting &c. + * about m4 quoting &c., but we have to #include it so that the C + * objects from the lexer are available. * - * Copyright (C)1996-1997 Ian Jackson + * Copyright (C)1996-1999 Ian Jackson * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by @@ -22,7 +23,13 @@ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -static void useless(void) { (void)yyunput; (void)useless; /* to shut up GCC ! */ } +static int parse_file(const char *string, int *didexist); +static int parser(int allowce); +int parse_string(const char *string, const char *descrip, int isinternal); + +/* + * Error-handling routines + */ static void closeerrorfile(void) { if (eh.file && !eh.filekeep) { @@ -91,7 +98,7 @@ static void errwhere(struct parser_state *tstate, char *bufput, int bufputlen) { tstate->filename,tstate->reportlineno); } -void parseerrprint(const char *fmt, ...) { +int parseerrprint(const char *fmt, ...) { va_list al; char errmsg[MAX_ERRMSG_LEN]; @@ -100,8 +107,27 @@ void parseerrprint(const char *fmt, ...) { vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al); senderrmsg(errmsg,eh.handling); va_end(al); + + return tokv_error; +} + +static int unexpected(int found, int wanted, const char *wantedstr) { + /* pass wanted==-1 if you know it's not what you wanted; + * otherwise this function will check it for you. + */ + if (found == wanted) return 0; + if (found == tokv_error) return found; + return parseerrprint("found %s, expected %s",printtoken(found),wantedstr); } +static int stringoverflow(const char *where) { + return parseerrprint("string buffer became far too large building %s",where); +} + +/* + * General assistance functions + */ + static void freecharparray(char **array) { char **pp; @@ -110,63 +136,82 @@ static void freecharparray(char **array) { free(array); } +static void countnewlines(void) { + char *p; + for (p=yytext; *p; p++) + if (*p == '\n') + cstate->lineno++; +} + static int dequote(char *inplace) { char *p, *q, buf[4], *bep; int v; - + p=q=inplace; assert(*p++ = '"'); while (*p && *p != '"') { if (*p != '\\') { *q++= *p++; continue; } switch (*++p) { - case 'n': *q++= '\n'; continue; - case 'r': *q++= '\r'; continue; - case 't': *q++= '\t'; continue; + case 'n': *q++= '\n'; p++; continue; + case 'r': *q++= '\r'; p++; continue; + case 't': *q++= '\t'; p++; continue; case 'x': - assert(buf[0]= *++p); assert(buf[1]= *++p); buf[2]= 0; - v= strtoul(buf,&bep,16); assert(bep == buf+2); - assert(!(v & ~0xff)); *q++= v; p++; continue; + p++; + if (!((buf[0]= *p++) && (buf[1]= *p++))) + return parseerrprint("quoted string ends inside \\x sequence"); + buf[2]= 0; + v= strtoul(buf,&bep,16); + if (bep != buf+2) + return parseerrprint("invalid \\ sequence \\x%s in quoted string",buf); + assert(!(v & ~0xff)); + *q++= v; + continue; default: - if (isalpha(*p)) { - parseerrprint("unknown \\ sequence \\%c in quoted string",*p); - return tokv_error; - } else if (isdigit(*p)) { - assert(buf[0]= *++p); assert(buf[1]= *++p); assert(buf[2]= *++p); + if (isalpha(*p)) + return parseerrprint("unknown \\ sequence \\%c in quoted string",*p); + if (isdigit(*p)) { + if (!((buf[0]= *p++) && (buf[1]= *p++) && (buf[2]= *p++))) abort(); buf[3]= 0; v= strtoul(buf,&bep,8); - if (bep != buf+3 || (v & ~0xff)); { - parseerrprint("invalid \\ sequence \\%s in quoted string",buf); - return tokv_error; - } - *q++= v; p++; continue; + if (bep != buf+3 || (v & ~0xff)) + return parseerrprint("invalid \\ sequence \\%s in quoted string",buf); + *q++= v; continue; } else if (ispunct(*p)) { *q++= *p++; continue; } else { while (*p==' ' || *p=='\t') p++; - assert(*p=='\n'); + v= *p++; assert(v=='\n'); } } } - assert(*p); assert(!*++p); return tokv_quotedstring; + assert(*p); assert(!*++p); + *q++= 0; + return tokv_quotedstring; } const char *printtoken(int token) { + /* Returns pointer to static buffer, overwritten by next call. */ + static const char keywordfmt[]= "keyword `%s'"; + static const char operatorfmt[]= "operator `%s'"; + static char buf[250]; char *q; const char *p; - int i, c; + int i, c, l; if ((token & tokm_repres) == tokr_word) { - assert(strlen(yytext)+500 && (c= *p++)) { + l= strlen(buf); i= sizeof(buf)-l-2; p= yytext; q= buf+l; + while ((c= *p++)) { + if (i-- <= 0) { q--; strcpy(q-3,"..."); break; } if (isspace(c)) c= ' '; else if (!isprint(c) || iscntrl(c)) c= '?'; - *q++= c; + else *q++= c; } strcpy(q,"'"); return buf; @@ -196,57 +242,34 @@ const char *printtoken(int token) { } static const char *string2path(const char *in) { - static char *p=0; - static int pl=0; + /* Returned pointers become invalid on next call. + * May return 0, having printed an error message. + */ + static char *p; + static int pl; if (strncmp(in,"~/",2)) return in; - makeroom(&p,&pl,strlen(serviceuser_dir)+1+strlen(in+2)+1); + if (makeroom(&p,&pl,strlen(serviceuser_dir)+1+strlen(in+2)+1)) { + stringoverflow("pathname"); + return 0; + } snyprintf(p,pl,"%s/%s",serviceuser_dir,in+2); return p; } -static void parser_push(struct parser_state *usestate, - const char *newfile, - YY_BUFFER_STATE ybuf, - int isinternal) { - usestate->lineno= 1; - usestate->reportlineno= 1; - usestate->filename= newfile; - usestate->notedreferer= 0; - usestate->isinternal= isinternal; - usestate->ybuf= ybuf; - usestate->upstate= cstate; - - cstate= usestate; - yy_switch_to_buffer(ybuf); -} - -static void parser_pop(void) { - struct parser_state *oldstate; - - oldstate= cstate; - cstate= cstate->upstate; - if (cstate) yy_switch_to_buffer(cstate->ybuf); - yy_delete_buffer(oldstate->ybuf); -} - -/* parser component functions pa_ return tokv_error or 0, - * having scanned exactly what they were expecting - * complete argument list parsers paa_ return tokv_error or 0, - * having scanned up to and including the newline +/* + * Parser component functions for parsing parameters to directives + * + * Functions pa_... parse just one parameter, + * and return tokv_error or 0, having scanned exactly what they were expecting + * Functions paa_... parse complete parameter lists, including the leading lwsp, + * and return tokv_error or 0, having scanned up to and including the newline + * + * For any function which takes `const char **rv' the + * value returned in *rv is overwritten by repeated calls to + * any of those functions (whether the same or another). */ -static int unexpected(int found, int wanted, const char *wantedstr) { - /* pass wanted==-1 if you know it's not what you wanted; - * otherwise this function will check it for you. - */ - if (found == wanted) return 0; - if (found != tokv_error) { - parseerrprint("found %s, expected %s",printtoken(found),wantedstr); - } - return tokv_error; -} - static int pa_mnl(void) { return unexpected(yylex(),tokv_newline,"newline"); } @@ -254,16 +277,7 @@ static int pa_mwsp(void) { return unexpected(yylex(),tokv_lwsp,"linear whitespace"); } -static void parm_1string(char ***rvalues, const char *tocopy) { - char **a; - a= xmalloc(sizeof(char*)*2); - a[0]= xstrsave(tocopy); - a[1]= 0; - *rvalues= a; -} - static int pa_string(const char **rv) { - /* Value returned in *rv is overwritten by repeated calls */ static char *p= 0; static int pl= 0; @@ -273,14 +287,24 @@ static int pa_string(const char **rv) { r= yylex(); if (r == tokv_error) return r; if ((r & tokm_repres) == tokr_nonstring) return unexpected(r,-1,"string"); l= strlen(yytext)+1; - makeroom(&p,&pl,l); + if (makeroom(&p,&pl,l)) return stringoverflow("string argument"); strcpy(p,yytext); *rv= p; return 0; } +static int pa_numberdollar(int *value_r) { + /* Also parses the whitespace beforehand. */ + int r; + + r= pa_mwsp(); if (r) return r; + r= yylex(); if (r == tokv_error || r == tokv_dollar) return r; + if (unexpected(r,tokv_ordinal,"expected number or dollar")) return tokv_error; + *value_r= lr_min; + return r; +} + static int paa_1string(const char **rv) { - /* Value returned in *rv is overwritten by repeated calls */ int r; r= pa_string(rv); if (r) return r; @@ -288,112 +312,95 @@ static int paa_1string(const char **rv) { } static int paa_1path(const char **rv) { - /* Value returned in *rv is overwritten by repeated calls */ const char *cp; int r; r= paa_1string(&cp); if (r) return r; - *rv= string2path(cp); return 0; + *rv= string2path(cp); if (!*rv) return tokv_error; + return 0; } -static int pa_parameter(char ***rvalues, char **rname) { - /* Scans a single parameter token and calls the appropriate parameter - * function, returning tokv_error or 0 just like the parameter function. - * If rname is non-null then the name of the parameter (malloc'd) will - * be stored in it. +static int paa_pathargs(const char **path_r, char ***newargs_r) { + /* Repeated calls do _not_ overwrite newargs_r; caller must free. + * Repeated calls _do_ overwrite string returned in path_r. + * Any call to this function invalidates previous returns in *rv. */ - int token, r, i; - char *name; + char **newargs; + const char *string; + int used, size, r; - token= yylex(); if (token == tokv_error) return token; - name= xstrsave(yytext); - if ((token & tokm_repres) != tokr_nonstring && - !memcmp(yytext,"u-",2) && strlen(yytext)>=3) { - for (i=0; - i=request_mbuf.nvars) { - *rvalues= xmalloc(sizeof(char*)); - **rvalues= 0; - } else { - parm_1string(rvalues,defvararray[i].value); + r= pa_string(&string); if (r == tokv_error) return r; + *path_r= string2path(string); if (!*path_r) return tokv_error; + + used=0; size=0; + newargs= xmalloc(sizeof(char*)*(size+1)); + + for (;;) { + r= yylex(); if (r == tokv_error) goto error; + if (r==tokv_newline) break; + if (unexpected(r,tokv_lwsp,"newline after or whitespace between arguments")) { + r= tokv_error; goto error; } - } else { - if (!(token & tokt_parameter)) { - free(name); - return unexpected(token,-1,"parameter name"); + r= yylex(); if (r==tokv_error) goto error; + if ((r & tokm_repres) == tokr_nonstring) { + r= unexpected(r,-1,"string for command argument"); + goto error; } - r= (lr_parameter)(token,rvalues); - if (r) { free(name); return r; } - } - debug_dumpparameter(name,*rvalues); - if (rname) *rname= name; - else free(name); - return 0; -} - -static int pa_condition(int *rtrue) { - /* Scans up to and including the newline following the condition; - * may scan more than one line if the condition is a multi-line - * one. Returns 0 (setting *rtrue) or tokv_error. Expects to - * scan the whitespace before its condition. - */ - int r, token, andor, ctrue, actrue; - char **parmvalues; - - r= pa_mwsp(); if (r) return r; - token= yylex(); - if (token == tokv_error) { - return token; - } else if (token == tokv_not) { - r= pa_condition(&ctrue); if (r) return r; - *rtrue= !ctrue; return 0; - } else if (token == tokv_openparen) { - andor= 0; actrue= -1; - for (;;) { - cstate->reportlineno= cstate->lineno; - r= pa_condition(&ctrue); if (r) return r; - switch (andor) { - case 0: assert(actrue==-1); actrue= ctrue; break; - case tokv_and: assert(actrue>=0); actrue= actrue && ctrue; break; - case tokv_or: assert(actrue>=0); actrue= actrue || ctrue; break; - default: abort(); - } - do { token= yylex(); } while (token == tokv_lwsp); - if (token == tokv_error) return token; - if (token == tokv_closeparen) break; - if (andor) { - r= unexpected(token,andor,"same conjunction as before"); if (r) return r; - } else { - if (token != tokv_and && token != tokv_or) - return unexpected(token,-1,"first conjunction inside connective"); - andor= token; + if (used>=size) { + if (used >= MAX_ARGSDEFVAR) { + r= parseerrprint("far too many arguments to service program"); + goto error; } + size= (used+5)<<1; + newargs= xrealloc(newargs,sizeof(char*)*(size+1)); } - r= pa_mnl(); if (r) return r; - *rtrue= actrue; return 0; - } else if (token & tokt_parmcondition) { - r= pa_mwsp(); if (r) return r; - r= pa_parameter(&parmvalues,0); if (r) return r; - r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues); - return r; + newargs[used++]= xstrsave(yytext); } - return unexpected(token,-1,"condition"); + newargs[used]= 0; + *newargs_r= newargs; + return 0; + +error: + newargs[used]=0; + freecharparray(newargs); + return r; } -static int pa_numberdollar(int *rv) { - /* Also parses the whitespace beforehand. */ - int r; +static int paa_message(const char **message_r) { + /* Returned value is invalidated by repeated calls. */ + static char *buildbuf; + static int buildbuflen; + const char *usetext; + + int r, tl; r= pa_mwsp(); if (r) return r; - r= yylex(); if (r == tokv_error || r == tokv_dollar) return r; - if (unexpected(r,tokv_ordinal,"expected number or dollar")) return tokv_error; - *rv= lr_min; return r; + tl= 1; + if (makeroom(&buildbuf,&buildbuflen,50)) return stringoverflow("start of message"); + buildbuf[0]= 0; + for (;;) { + r= yylex(); if (r == tokv_error) return r; + if (r == tokv_eof) + return parseerrprint("unexpected end of file in message text"); + if (r == tokv_newline) break; + usetext= r == tokv_lwsp ? " " : yytext; + tl+= strlen(usetext); + if (makeroom(&buildbuf,&buildbuflen,tl)) return stringoverflow("message"); + strcat(buildbuf,usetext); + } + *message_r= buildbuf; + return 0; } -/* Main parser routines */ +/* + * Skipping routines (used by e.g. the `if' code). + */ static int skiptoeol(void) { + /* Returns 0 if OK, having just parsed the newline + * or tokv_error if an error occurs, leaving the position at the error + * (which may be EOF or a syntax error token). + */ int token; do { token= yylex(); } @@ -401,14 +408,15 @@ static int skiptoeol(void) { if (token == tokv_newline) return 0; if (token == tokv_error) return token; assert(token == tokv_eof); - parseerrprint("unexpected end of file while looking for end of line"); - return tokv_error; + return parseerrprint("unexpected end of file while looking for end of line"); } static int skip(int allowce) { - /* Scans a piece of config without executing it. - * Returns tokv_error, or the tokt_controlend token - * with type allowce if one was found. + /* Scans a piece of config without executing it. Returns + * tokv_error (leaving the parsing state at the error), + * or the tokt_controlend token with type allowce if one + * was found (in which case rest of line, including any whitespace + * after tokt_controlend, has not been parsed), or tokv_eof. */ int token, r; @@ -419,7 +427,7 @@ static int skip(int allowce) { return token; } else if (token & tokt_controlend) { if (allowce == lr_controlend) return token; - return unexpected(token,-1,"control structure end of a different kind"); + else return unexpected(token,-1,"control structure end of a different kind"); } else if (token & tokt_controlstart) { r= token; while (r & tokt_controlstart) { @@ -428,107 +436,136 @@ static int skip(int allowce) { r= skip(token); if (r & tokt_exception) return r; } } else if (!(token & tokt_directive) && !(token & tokt_condop)) { - parseerrprint("not a directive (or conditional operator) " - "while looking for control structure end"); - return tokv_error; + return parseerrprint("not a directive (or conditional operator) " + "while looking for control structure end"); } r= skiptoeol(); if (r) return r; } } - -static int parser(int allowce) { - /* Returns: - * an exception (error, eof or quit) - * then rest of `file' is uninteresting - * or - * token if allowce was !0 and equal to token's controlend - * then rest of `file' not scanned yet - */ - int token, r; - for (;;) { /* loop over lines */ - cstate->reportlineno= cstate->lineno; - do { token= yylex(); } while (token == tokv_lwsp); - if (token & tokt_exception) { - return token; - } else if (token & tokt_controlend) { - if (lr_controlend == allowce) return token; - return unexpected(token,-1,"directive (not this kind of control structure end)"); - } else if (token & tokt_directive) { - r= (lr_dir)(token); if (r) { assert(r & tokt_exception); return r; } - } else if (token == tokv_newline) { - /* ignore blank links (and comment-only lines) */ - } else { - return unexpected(token,-1,"directive"); - } - } -} +/* + * Routines for parsing and getting the values of parameters + */ -int parse_string(const char *string, const char *descrip, int isinternal) { - /* Returns the same things as parser, except that tokv_eof - * is turned into 0. */ - struct parser_state usestate; - YY_BUFFER_STATE ybuf; - int r; +static void parm_1string(char ***rvalues, const char *tocopy) { + char **a; + a= xmalloc(sizeof(char*)*2); + a[0]= xstrsave(tocopy); + a[1]= 0; + *rvalues= a; +} - ybuf= yy_scan_string(string); - if (!ybuf) syscallerror("unable to create flex buffer for internal string"); - parser_push(&usestate,descrip,ybuf,isinternal); - - r= parser(0); +static char *parm_ulong(unsigned long ul) { + char *p; + int l; - parser_pop(); - if (r == tokv_eof) r= 0; - return r; + l= CHAR_BIT*sizeof(unsigned long)/3+4; + p= xmalloc(l); + snyprintf(p,l,"%lu",ul); + return p; } -static int parse_file(const char *string, int *didexist) { - /* Returns the same things as parser, except that tokv_eof - * is turned into 0. */ - static int fileparselevel= 0; +static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) { + char **a; - struct parser_state usestate; - YY_BUFFER_STATE ybuf; - int r; - FILE *file; + a= xmalloc(sizeof(char*)*3); + a[0]= xstrsave(name); + a[1]= parm_ulong(id); + a[2]= 0; + *rvalues= a; + return 0; +} - if (fileparselevel >= MAX_INCLUDE_NEST) { - parseerrprint("too many nested levels of included files"); - return tokv_error; - } - file= fopen(string,"r"); if (!file) { - if (errno == ENOENT) { - if (didexist) *didexist= 0; - return 0; - } - parseerrprint("unable to open config file `%s': %s",string,strerror(errno)); - return tokv_error; +static int parm_groups(char ***rvalues, int size, + gid_t *gids, const char *const *groups) { + char **a; + int i; + + if (size >= 2 && gids[0] == gids[1]) { size--; gids++; groups++; } + a= xmalloc(sizeof(char*)*(size+1)*2); + for (i=0; i=3) { + for (i=0; + i=request_mbuf.nvars) { + *rvalues= xmalloc(sizeof(char*)); + **rvalues= 0; + } else { + parm_1string(rvalues,defvararray[i].value); + } + } else if (token & tokt_parameter) { + r= (lr_parameter)(token,rvalues); + if (r) { free(name); return r; } + } else { + free(name); + return unexpected(token,-1,"parameter name"); } - - fileparselevel--; - parser_pop(); - fclose(file); - if (r == tokv_eof) r= 0; - return r; + debug_dumpparameter(name,*rvalues); + if (rname) *rname= name; + else free(name); + return 0; } -/* Parameter-based conditional functions (parmcondition's) */ +/* + * Routines for parsing conditions, including + * parameter-based conditional functions (parmcondition's). + */ -int pcf_glob(int ctoken, char **pv, int *rtrue) { +int pcf_glob(int ctoken, char *const *pv, int *rtrue) { int token, actrue, r; - char **pp; + char *const *pp; actrue= 0; r= pa_mwsp(); if (r) return r; @@ -546,9 +583,10 @@ int pcf_glob(int ctoken, char **pv, int *rtrue) { return 0; } -int pcf_range(int ctoken, char **pv, int *rtrue) { +int pcf_range(int ctoken, char *const *pv, int *rtrue) { int mintoken, min, maxtoken, max, r; - char **pp, *ep; + char *const *pp; + char *ep; unsigned long v; r= pa_mwsp(); if (r) return r; @@ -565,17 +603,17 @@ int pcf_range(int ctoken, char **pv, int *rtrue) { *rtrue= 0; return 0; } -int pcf_grep(int ctoken, char **pv, int *rtrue) { +int pcf_grep(int ctoken, char *const *pv, int *rtrue) { FILE *file; const char *cp; - char **pp, *buf, *p; + char *const *pp; + char *buf, *p; int r, maxlen, l, c, actrue, posstrue; r= paa_1path(&cp); if (r) return r; - file= fopen(cp,"r"); if (!file) { - parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno)); - return tokv_error; - } + file= fopen(cp,"r"); + if (!file) + return parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno)); maxlen= 0; for (pp= pv; *pp; pp++) { l= strlen(*pp); if (l > maxlen) maxlen= l; } buf= xmalloc(maxlen+2); actrue= 0; c= 0; @@ -587,7 +625,8 @@ int pcf_grep(int ctoken, char **pv, int *rtrue) { if (c=='\n' || c==EOF || isspace(c)) { while (p>buf && isspace(p[-1])) --p; *p= 0; posstrue= 0; - for (pp= pv; *pp; pp++) if (!strcmp(*pp,buf)) { posstrue= 1; break; } + for (pp= pv; !posstrue && *pp; pp++) + if (!strcmp(*pp,buf)) posstrue= 1; } else { posstrue= 0; } @@ -605,125 +644,67 @@ int pcf_grep(int ctoken, char **pv, int *rtrue) { fclose(file); free(buf); return tokv_error; } assert(actrue || feof(file)); - fclose(file); free(buf); *rtrue= actrue; return 0; + fclose(file); free(buf); *rtrue= actrue; + return 0; } -/* Parameter functions and associated `common code' functions */ - -int pf_service(int ptoken, char ***rvalues) { - parm_1string(rvalues,service); return 0; -} - -static char *parm_ulong(unsigned long ul) { - char *p; - int l; - - l= CHAR_BIT*sizeof(unsigned long)/3+4; - p= xmalloc(l); - snyprintf(p,l,"%lu",ul); - return p; -} - -static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) { - char **a; - a= xmalloc(sizeof(char*)*3); - a[0]= xstrsave(name); - a[1]= parm_ulong(id); - a[2]= 0; - *rvalues= a; return 0; -} - -int pf_callinguser(int ptoken, char ***rvalues) { - return parm_usernameuid(rvalues,logname,request_mbuf.callinguid); -} - -int pf_serviceuser(int ptoken, char ***rvalues) { - return parm_usernameuid(rvalues,serviceuser,serviceuser_uid); -} - -static char *parm_gidname(gid_t id) { - static char ebuf[200]; - - struct group *ge; - - ge= getgrgid(id); - if (!ge) { - sprintf(ebuf,"look up group with id %lu",(unsigned long)id); - syscallerror(ebuf); - } - return xstrsave(ge->gr_name); -} - -static int parm_gids(char ***rvalues, int size, gid_t *list) { - char **a; - int i; - - if (size >= 2 && list[0] == list[1]) { size--; list++; } - a= xmalloc(sizeof(char*)*(size+1)*2); - for (i=0; i=size) { - size= (used+5)<<2; - newargs= xrealloc(newargs,sizeof(char*)*(size+1)); + r= pa_mwsp(); if (r) return r; + token= yylex(); + if (token == tokv_error) { + return token; + } else if (token == tokv_not) { + r= pa_condition(&ctrue); if (r) return r; + *rtrue= !ctrue; return 0; + } else if (token == tokv_openparen) { + andor= 0; actrue= -1; + for (;;) { + cstate->reportlineno= cstate->lineno; + r= pa_condition(&ctrue); if (r) return r; + switch (andor) { + case 0: assert(actrue==-1); actrue= ctrue; break; + case tokv_and: assert(actrue>=0); actrue= actrue && ctrue; break; + case tokv_or: assert(actrue>=0); actrue= actrue || ctrue; break; + default: abort(); + } + do { token= yylex(); } while (token == tokv_lwsp); + if (token == tokv_error) return token; + if (token == tokv_closeparen) break; + if (andor) { + r= unexpected(token,andor,"same conjunction as before"); if (r) return r; + } else { + if (token != tokv_and && token != tokv_or) + return unexpected(token,-1,"first conjunction inside connective"); + andor= token; + } } - newargs[used++]= xstrsave(yytext); + r= pa_mnl(); if (r) return r; + *rtrue= actrue; return 0; + } else if (token & tokt_parmcondition) { + r= pa_mwsp(); if (r) return r; + r= pa_parameter(&parmvalues,0); if (r) return r; + r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues); + return r; + } else { + return unexpected(token,-1,"condition"); } - newargs[used]= 0; - *newargs_r= newargs; - return 0; - -error: - newargs[used]=0; - freecharparray(newargs); - return r; } +/* + * Directive functions and associated `common code' functions + */ + +/* Directives specifying the service program (execute-... and reset) */ + static void execreset(void) { execute= 0; execbuiltin= 0; @@ -797,7 +778,7 @@ int df_executefromdirectory(int dtoken) { } if (!S_ISREG(stab.st_mode)) { parseerrprint("file `%s' in execute-from-directory is not an ordinary file" - " or link to one (mode=0%o)",fn,stab.st_mode); + " or link to one (mode=0%lo)",fn,(unsigned long)stab.st_mode); free(fn); freecharparray(newargs); return tokv_error; } execreset(); @@ -807,6 +788,8 @@ int df_executefromdirectory(int dtoken) { return 0; } +/* Parsing builtin service requests (execute-builtin) */ + static int bispa_none(char ***rnewargs) { return pa_mnl(); } @@ -848,110 +831,138 @@ int df_executebuiltin(int dtoken) { return 0; } -int df_errorstostderr(int dtoken) { +/* Directives for changing other execution parameters */ + +int dfg_setflag(int dtoken) { int r; - + r= pa_mnl(); if (r) return r; - closeerrorfile(); eh.handling= dtoken; return 0; + *lr_flag= lr_flagval; + return 0; } -int df_errorstosyslog(int dtoken) { - int token, level, facility; +int df_reset(int dtoken) { + int r; - facility= DEFUSERLOGFACILITY; - level= DEFUSERLOGLEVEL; - token= yylex(); - if (token == tokv_lwsp) { - token= yylex(); if (token == tokv_error) return token; - if (!(token & tokt_logfacility)) - return unexpected(token,-1,"syslog facility (or end of line)"); - facility= lr_logfacility; - token= yylex(); - } - if (token == tokv_lwsp) { - token= yylex(); if (token == tokv_error) return token; - if (!(token & tokt_loglevel)) - return unexpected(token,-1,"syslog level (or end of line) after facility"); - level= lr_loglevel; - token= yylex(); - } - if (unexpected(token,tokv_newline,"end of line somewhere after errors-to-syslog")) - return tokv_error; - closeerrorfile(); eh.handling= tokv_word_errorstosyslog; - eh.logfacility= facility; eh.loglevel= level; return 0; + r= pa_mnl(); if (r) return r; + r= parse_string(RESET_CONFIGURATION,"",1); + return r; } -int df_errorstofile(int dtoken) { - const char *cp; - FILE *file; - int r; - - r= paa_1path(&cp); if (r) return r; - file= fopen(cp,"a"); - if (!file) { - parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno)); - return tokv_error; - } - if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) { - parseerrprint("unable to set line buffering on errors file: %s",strerror(errno)); - fclose(file); return tokv_error; +int dfg_fdwant(int dtoken) { + int fdmin, fdmax, r, needreadwrite, havereadwrite, fd; + + needreadwrite= lr_fdwant_readwrite; + r= pa_mwsp(); if (r) return r; + r= yylex(); if (r == tokv_error) return r; + if (!(r & tokt_fdrange)) return unexpected(r,-1,"file descriptor range"); + fdmin= lr_min; fdmax= lr_max; + if (fdmin<0 || fdmin>MAX_ALLOW_FD || + (fdmax != -1 && fdmax<0) || fdmax>MAX_ALLOW_FD) + return parseerrprint("file descriptor in range is negative or far too large"); + r= yylex(); if (r == tokv_error) return r; + if (r == tokv_newline) { + if (needreadwrite > 0) + return parseerrprint("read or write is required"); + havereadwrite= 0; + } else if (r == tokv_lwsp) { + if (needreadwrite < 0) + return parseerrprint("read or write not allowed"); + r= yylex(); if (r == tokv_error) return r; + if (!(r & tokt_readwrite)) + return unexpected(r,-1,"read or write (or perhaps newline)"); + havereadwrite= r; + r= pa_mnl(); if (r) return r; + } else { + return unexpected(r,-1,"whitespace before read or write or newline"); } - closeerrorfile(); eh.handling= tokv_word_errorstofile; - eh.file= file; eh.filename= xstrsave(cp); return 0; + ensurefdarray(fdmin); + if (fdmax == -1) { + if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd)) + return parseerrprint("unspecified maximum only allowed" + " with reject-fd and ignore-fd"); + fdmax= fdarrayused-1; + restfdwantstate= dtoken; + restfdwantrw= havereadwrite; + } + ensurefdarray(fdmax); + for (fd=fdmin; fd<=fdmax; fd++) { + fdarray[fd].wantstate= dtoken; + fdarray[fd].wantrw= havereadwrite; + } + return 0; } -int dfg_setflag(int dtoken) { - int r; - - r= pa_mnl(); if (r) return r; - *lr_flag= lr_flagval; return 0; -} +/* Directives for changing error handling */ -int df_reset(int dtoken) { +int df_errorstostderr(int dtoken) { int r; - + r= pa_mnl(); if (r) return r; - r= parse_string(RESET_CONFIGURATION,"",1); - assert(!r); return 0; + closeerrorfile(); eh.handling= dtoken; + return 0; } -int df_cd(int dtoken) { - const char *cp; - int r; +int df_errorstosyslog(int dtoken) { + int token, level, facility; - r= paa_1path(&cp); if (r) return r; - if (!chdir(cp)) return 0; - parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno)); - return tokv_error; + facility= DEFUSERLOGFACILITY; + level= DEFUSERLOGLEVEL; + token= yylex(); + if (token == tokv_lwsp) { + token= yylex(); if (token == tokv_error) return token; + if (!(token & tokt_logfacility)) + return unexpected(token,-1,"syslog facility (or end of line)"); + facility= lr_logfacility; + token= yylex(); + } + if (token == tokv_lwsp) { + token= yylex(); if (token == tokv_error) return token; + if (!(token & tokt_loglevel)) + return unexpected(token,-1,"syslog level (or end of line) after facility"); + level= lr_loglevel; + token= yylex(); + } + if (unexpected(token,tokv_newline,"end of line after errors-to-syslog")) + return tokv_error; + closeerrorfile(); eh.handling= tokv_word_errorstosyslog; + eh.logfacility= facility; eh.loglevel= level; + return 0; } -int df_userrcfile(int dtoken) { +int df_errorstofile(int dtoken) { const char *cp; + FILE *file; int r; - + r= paa_1path(&cp); if (r) return r; - free(userrcfile); userrcfile= xstrsave(cp); + file= fopen(cp,"a"); + if (!file) + return parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno)); + if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) { + parseerrprint("unable to set line buffering on errors file: %s",strerror(errno)); + fclose(file); return tokv_error; + } + closeerrorfile(); eh.handling= tokv_word_errorstofile; + eh.file= file; eh.filename= xstrsave(cp); return 0; } +/* Directives for including other files or configuration data */ + int dfi_includeuserrcfile(int dtoken) { int r; r= pa_mnl(); if (r) return r; - if (userrcfile) return parse_file(userrcfile,0); - parseerrprint("_include-user-rcfile (for-internal-use directive) " - "found but user-rcfile not set"); return tokv_error; + assert(userrcfile); + return parse_file(userrcfile,0); } int dfi_includeclientconfig(int dtoken) { int r; r= pa_mnl(); if (r) return r; - if (!overridedata) { - parseerrprint("_include-client-config (for-internal-use directive) " - "found but configuration not overridden"); - return tokv_error; - } + assert(overridedata); return parse_string(overridedata,"",0); } @@ -962,52 +973,10 @@ int df_include(int dtoken) { r= paa_1path(&cp); if (r) return r; r= parse_file(cp,&found); if (r) return r; if (found || dtoken == tokv_word_includeifexist) return 0; - parseerrprint(dtoken == tokv_word_includesysconfig ? - "system configuration file `%s' does not exist" : - "included file `%s' does not exist", - cp); - return tokv_error; -} - -static int paa_message(const char **message_r) { - static char *buildbuf; - static int buildbuflen; - - int r, tl; - - r= pa_mwsp(); if (r) return r; - tl= 1; - makeroom(&buildbuf,&buildbuflen,10); - buildbuf[0]= 0; - for (;;) { - r= yylex(); if (r == tokv_error) return r; - if (r == tokv_eof) { - parseerrprint("unexpected end of file in message text"); return tokv_error; - } - if (r == tokv_newline) break; - tl+= strlen(yytext); - makeroom(&buildbuf,&buildbuflen,tl); - strcat(buildbuf,yytext); - } - *message_r= buildbuf; - return 0; -} - -int df_message(int dtoken) { - const char *mp; - int r; - - r= paa_message(&mp); if (r) return r; - parseerrprint("`message' directive: %s",mp); - return 0; -} - -int df_error(int dtoken) { - const char *mp; - int r; - - r= paa_message(&mp); if (r) return r; - parseerrprint("`error' directive: %s",mp); return tokv_error; + return parseerrprint(dtoken == tokv_word_includesysconfig ? + "system configuration file `%s' does not exist" : + "included file `%s' does not exist", + cp); } int df_includedirectory(int dtoken) { @@ -1017,116 +986,148 @@ int df_includedirectory(int dtoken) { int r, cpl, tel, c, found; DIR *d; struct dirent *de; - const char *p, *cp; + const char *p, *cpget; + char *cp; - r= paa_1path(&cp); if (r) return r; - d= opendir(cp); - if (!d) { - parseerrprint("unable to open directory `%s': %s",cp,strerror(errno)); - return tokv_error; - } + r= paa_1path(&cpget); if (r) return r; + d= opendir(cpget); + if (!d) + return parseerrprint("unable to open directory `%s': %s",cpget,strerror(errno)); + cp= xstrsave(cpget); cpl= strlen(cp); while ((de= readdir(d))) { tel= strlen(de->d_name); if (!tel) continue; p= de->d_name; - if (!isalnum(*p)) continue; + if (!*p || !isalnum(*p)) continue; while ((c= *++p)) if (!(isalnum(c) || c=='-')) break; if (c) continue; - makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1); + if (makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1)) { + stringoverflow("pathname in directory"); + r= tokv_error; goto x_err; + } snyprintf(buildbuf,buildbuflen,"%s/%s",cp,de->d_name); - r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; } + r= parse_file(buildbuf,&found); if (r) goto x_err; if (!found) { - parseerrprint("unable to open file `%s' in included directory `%s': %s", - de->d_name,cp,strerror(errno)); - closedir(d); return tokv_error; + r= parseerrprint("unable to open file `%s' in included directory `%s': %s", + de->d_name,cp,strerror(errno)); + goto x_err; } } if (closedir(d)) { parseerrprint("error closing directory `%s': %s",cp,strerror(errno)); + free(cp); return tokv_error; } + free(cp); return 0; + +x_err: + closedir(d); + free(cp); + return r; } int df_includelookup(int dtoken) { static char *buildbuf=0; int buildbuflen=0; - char **parmvalues, **pp, *p, *q; - const char *cp; + char **parmvalues, **pp, *p, *q, *cp; + const char *cpget; struct stat stab; int r, done, thisdone, cpl, c; r= pa_mwsp(); if (r) return r; r= pa_parameter(&parmvalues,0); if (r) return r; - r= paa_1path(&cp); if (r) { freecharparray(parmvalues); return r; } - if (stat(cp,&stab)) { - parseerrprint("unable to access directory `%s': %s",cp,strerror(errno)); + r= paa_1path(&cpget); if (r) { freecharparray(parmvalues); return r; } + if (stat(cpget,&stab)) { + parseerrprint("unable to access directory `%s': %s",cpget,strerror(errno)); freecharparray(parmvalues); return tokv_error; } if (!S_ISDIR(stab.st_mode)) { - parseerrprint("object `%s' is not a directory or link to one",cp); + parseerrprint("object `%s' is not a directory or link to one",cpget); freecharparray(parmvalues); return tokv_error; } done= 0; + cp= xstrsave(cpget); cpl= strlen(cp); if (!parmvalues[0]) { - makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP)); + if (makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP))) { + stringoverflow("pathname in directory for lookup of undefined parameter"); + r= tokv_error; goto x_err; + } snyprintf(buildbuf,buildbuflen,"%s/" NONEINCLUDELOOKUP,cp); - r= parse_file(buildbuf,&thisdone); - if (r) { freecharparray(parmvalues); return r; } + r= parse_file(buildbuf,&thisdone); if (r) goto x_err; if (thisdone) done= 1; } else { for (pp=parmvalues; *pp && (!done || dtoken == tokv_word_includelookupall); pp++) { - makeroom(&buildbuf,&buildbuflen, - cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1); + if (makeroom(&buildbuf,&buildbuflen, + cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1)) { + stringoverflow("pathname in directory for lookup"); + r= tokv_error; goto x_err; + } strcpy(buildbuf,cp); p= *pp; q= buildbuf+cpl; *q++= '/'; - if (*p=='.') *q++= ':'; - while ((c= *p++)) { - if (c=='/') { *q++= ':'; c='-'; } - else if (c==':') { *q++= ':'; } - *q++= c; + if (!*p) { + strcpy(q,EMPTYINCLUDELOOKUP); + } else { + if (*p=='.') *q++= ':'; + while ((c= *p++)) { + if (c=='/') { + *q++= ':'; + c= '-'; + } else if (!((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + c == '-' || c == '_')) { + *q++= ':'; + } + *q++= c; + } + *q++= 0; } - *q++= 0; r= parse_file(buildbuf,&thisdone); - if (r) { freecharparray(parmvalues); return r; } + if (r) goto x_err; if (thisdone) done= 1; } } - freecharparray(parmvalues); if (!done) { - makeroom(&buildbuf,&buildbuflen, - cpl+1+sizeof(DEFAULTINCLUDELOOKUP)); + if (makeroom(&buildbuf,&buildbuflen, + cpl+1+sizeof(DEFAULTINCLUDELOOKUP))) { + stringoverflow("pathname in directory for lookup of default"); + r= tokv_error; goto x_err; + } snyprintf(buildbuf,buildbuflen,"%s/" DEFAULTINCLUDELOOKUP,cp); - r= parse_file(buildbuf,0); if (r) return r; + r= parse_file(buildbuf,0); if (r) goto x_err; } - return 0; + r= 0; + +x_err: + freecharparray(parmvalues); + free(cp); + return r; } +/* Control constructs */ + int df_catchquit(int dtoken) { int r; r= pa_mnl(); if (r) return r; r= parser(tokv_word_catchquit); - if (r & tokt_controlend) { - assert(r == tokv_word_hctac); - r= pa_mnl(); - } else if (r == tokv_quit || r == tokv_error) { + if (r == tokv_quit || r == tokv_error) { if (r == tokv_error) { r= parse_string(RESET_CONFIGURATION, "",1); assert(!r); } r= skip(tokv_word_catchquit); - if (r & tokt_controlend) { - assert(r == tokv_word_hctac); - r= pa_mnl(); - } + } + if (r & tokt_controlend) { + assert(r == tokv_word_hctac); + r= pa_mnl(); } return r; } @@ -1139,65 +1140,75 @@ int df_if(int dtoken) { r= pa_condition(&true); if (r) return r; if (!done && true) { r= parser(tokv_word_if); done= 1; } else { r= skip(tokv_word_if); } - if (!(r & tokv_word_if)) return r; + if (!(r & tokt_controlend)) return r; } while (r == tokv_word_elif); if (r == tokv_word_else) { r= pa_mnl(); if (r) return r; cstate->reportlineno= cstate->lineno; if (done) r= skip(tokv_word_if); else r= parser(tokv_word_if); - if (!(r & tokv_word_if)) return r; + if (!(r & tokt_controlend)) return r; } if (unexpected(r,tokv_word_fi,"`fi' to end `if'")) return tokv_error; return pa_mnl(); } -int dfg_fdwant(int dtoken) { - int fdmin, fdmax, r, needreadwrite, havereadwrite, fd; +int df_errorspush(int dt) { + struct error_handling save; + int r; - needreadwrite= lr_fdwant_readwrite; - r= pa_mwsp(); if (r) return r; - r= yylex(); if (r == tokv_error) return r; - if (!(r & tokt_fdrange)) return r; - fdmin= lr_min; fdmax= lr_max; - r= yylex(); if (r == tokv_error) return r; - if (r == tokv_newline) { - if (needreadwrite > 0) { - parseerrprint("read or write is required"); return tokv_error; - } - havereadwrite= 0; - } else if (r == tokv_lwsp) { - if (needreadwrite < 0) { - parseerrprint("read or write not allowed"); return tokv_error; - } - r= yylex(); if (r == tokv_error) return r; - if (!(r & tokt_readwrite)) - return unexpected(r,-1,"read or write (or perhaps newline)"); - havereadwrite= r; - r= pa_mnl(); if (r) return r; - } else { - return unexpected(r,-1,"whitespace before read or write or newline"); - } - ensurefdarray(fdmin); - if (fdmax == -1) { - if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd)) - parseerrprint("unspecified maximum only allowed with reject-fd and ignore-fd"); - fdmax= fdarrayused-1; - restfdwantstate= dtoken; - restfdwantrw= havereadwrite; - } - for (fd=fdmin; fd<=fdmax; fd++) { - fdarray[fd].wantstate= dtoken; - fdarray[fd].wantrw= havereadwrite; + r= pa_mnl(); if (r) return r; + + save= eh; + eh.filekeep= 1; + + r= parser(tokv_word_errorspush); + + closeerrorfile(); + eh= save; + + if (r & tokt_controlend) { + assert(r == tokv_word_srorre); + r= pa_mnl(); } + return r; +} + +/* Miscelleanous directives */ + +int df_cd(int dtoken) { + const char *cp; + int r; + + r= paa_1path(&cp); if (r) return r; + if (!chdir(cp)) return 0; + return parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno)); +} + +int df_userrcfile(int dtoken) { + const char *cp; + int r; + + r= paa_1path(&cp); if (r) return r; + free(userrcfile); userrcfile= xstrsave(cp); return 0; } -int df_quit(int dtoken) { +int df_message(int dtoken) { + const char *mp; int r; - r= pa_mnl(); if (r) return r; - return tokv_quit; + r= paa_message(&mp); if (r) return r; + parseerrprint("`message' directive: %s",mp); + return 0; +} + +int df_error(int dtoken) { + const char *mp; + int r; + + r= paa_message(&mp); if (r) return r; + return parseerrprint("`error' directive: %s",mp); } int df_eof(int dtoken) { @@ -1207,23 +1218,150 @@ int df_eof(int dtoken) { return tokv_eof; } -int df_errorspush(int dt) { - struct error_handling save; +int df_quit(int dtoken) { int r; r= pa_mnl(); if (r) return r; + return tokv_quit; +} - save= eh; - eh.filekeep= 1; +/* + * Main parser routines + */ + +static void parser_push(struct parser_state *usestate, + const char *newfile, + const struct stat *newfilestab, + YY_BUFFER_STATE ybuf, + int isinternal) { + usestate->lineno= 1; + usestate->reportlineno= 1; + usestate->filename= newfile; + usestate->filestab= *newfilestab; + usestate->notedreferer= 0; + usestate->isinternal= isinternal; + usestate->ybuf= ybuf; + usestate->upstate= cstate; - r= parser(tokv_word_errorspush); + cstate= usestate; + yy_switch_to_buffer(ybuf); +} - closeerrorfile(); - eh= save; +static void parser_pop(void) { + struct parser_state *oldstate; - if (r & tokt_controlend) { - assert(r == tokv_word_srorre); - r= pa_mnl(); + oldstate= cstate; + cstate= cstate->upstate; + if (cstate) yy_switch_to_buffer(cstate->ybuf); + yy_delete_buffer(oldstate->ybuf); +} + +int parse_string(const char *string, const char *descrip, int isinternal) { + /* Returns the same things as parser, except that tokv_eof is turned + * into 0. *string must be statically allocated or copied, so that + * it is not overwritten while the parsing takes place (unlike with + * parse_file). + */ + static const struct stat blankstab; + + struct parser_state usestate; + YY_BUFFER_STATE ybuf; + int r; + + ybuf= yy_scan_string(string); + if (!ybuf) syscallerror("unable to create flex buffer for internal string"); + parser_push(&usestate,descrip,&blankstab,ybuf,isinternal); + + r= parser(0); + + parser_pop(); + if (r == tokv_eof) r= 0; + return r; +} + +static int parse_file(const char *string, int *didexist) { + /* Returns the same things as parser, except that tokv_eof is turned + * into 0. If *didexist is 0 then errno will have been set. + * *string will be copied by parse_file so it may be be overwritten + * during the parsing (so, for example, yytext need not be copied). + */ + static int fileparselevel= 0; + + struct parser_state usestate, *checkrecurse; + YY_BUFFER_STATE ybuf; + int r; + FILE *file; + char *filename; + struct stat newstab; + + if (fileparselevel >= MAX_INCLUDE_NEST) + return parseerrprint("too many nested levels of included files"); + file= fopen(string,"r"); + if (!file) { + if (errno == ENOENT) { + if (didexist) *didexist= 0; + return 0; + } + return parseerrprint("unable to open config file `%s': %s",string,strerror(errno)); + } + r= fstat(fileno(file),&newstab); if (r) syscallerror("unable to fstat new file"); + for (checkrecurse= cstate; checkrecurse; checkrecurse= checkrecurse->upstate) { + if (!checkrecurse->filestab.st_mode) continue; + if (newstab.st_dev==checkrecurse->filestab.st_dev && + newstab.st_ino==checkrecurse->filestab.st_ino) { + fclose(file); + return parseerrprint("recursion detected - config file `%s' calls itself",string); + } } + + if (didexist) *didexist= 1; + + ybuf= yy_create_buffer(file,YY_BUF_SIZE); + if (!ybuf) syscallerror("unable to create flex buffer for file"); + filename= xstrsave(string); + parser_push(&usestate,filename,&newstab,ybuf,0); + fileparselevel++; + + r= parser(0); + if (ferror(file)) + r= parseerrprint("error reading configuration file `%s'",string); + + fileparselevel--; + parser_pop(); + free(filename); + fclose(file); + if (r == tokv_eof) r= 0; return r; } + +static int parser(int allowce) { + /* Returns: + * an exception (error, eof or quit) + * then rest of `file' is uninteresting + * or + * token if allowce was !0 and equal to token's controlend + * then rest of `file' (including rest of line with the + * controlend - even the whitespace) not scanned yet + */ + int token, r; + + for (;;) { /* loop over lines */ + cstate->reportlineno= cstate->lineno; + do { token= yylex(); } while (token == tokv_lwsp); + if (token & tokt_exception) { + return token; + } else if (token & tokt_controlend) { + if (lr_controlend == allowce) return token; + else return unexpected(token,-1,"directive (not this kind of" + " control structure end)"); + } else if (token & tokt_directive) { + if ((token & tokt_internal) && !cstate->isinternal) + return unexpected(token,-1,"published directive, not internal-use-only one"); + r= (lr_dir)(token); if (r) { assert(r & tokt_exception); return r; } + } else if (token == tokv_newline) { + /* ignore blank lines (and comment-only lines) */ + } else { + return unexpected(token,-1,"directive"); + } + } +}