X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=userv.git;a=blobdiff_plain;f=parser.c;h=898bbfac2c3f2f0d9e1d8ac6e3d2ba1cfd0092fa;hp=23958373ddb563a8db860bd90a90702bf946ca42;hb=c44b074953734e3b487946b974d44e51f4fd3859;hpb=b613cadfdf189af1f4ee0b5bdc12cedda052c672 diff --git a/parser.c b/parser.c index 2395837..898bbfa 100644 --- a/parser.c +++ b/parser.c @@ -3,7 +3,8 @@ * 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 * @@ -22,28 +23,25 @@ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -struct parser_state { - int lineno, atnewline, notedreferer; - const char *filename; - YY_BUFFER_STATE ybuf; - struct parser_state *upstate; -}; - -static const char *currentfile= 0; -static struct parser_state *parser_topstate= 0; +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); -static directive_fnt *lr_dir=0; -static parmcondition_fnt *lr_parmcond=0; -static parameter_fnt *lr_parameter=0; -static int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag; -static int lr_flagval, lr_controlend; -static int lr_fdwant_readwrite; - -static void useless(void) { (void)yyunput; (void)useless; /* to shut up GCC ! */ } +/* + * Error-handling routines + */ static void closeerrorfile(void) { - if (ehfile && !ehfilekeep) { fclose(ehfile); free(ehfilename); } - ehfile= 0; ehfilename= 0; ehfilekeep= 0; + if (eh.file && !eh.filekeep) { + if (fclose(eh.file)) { + eh.handling= tokv_word_errorstostderr; + parseerrprint("error writing to error log file `%s': %s", + eh.filename,strerror(errno)); + } + free(eh.filename); + } + eh.handling= 0; + eh.file= 0; eh.filename= 0; eh.filekeep= 0; } static void senderrmsg(const char *errmsg, int useehandling) { @@ -57,24 +55,24 @@ static void senderrmsg(const char *errmsg, int useehandling) { senderrmsgstderr(errmsg); break; case tokv_word_errorstosyslog: - ensurelogopen(ehlogfacility); - syslog(ehloglevel,"%s",errmsg); + ensurelogopen(eh.logfacility); + syslog(eh.loglevel,"%s",errmsg); break; case tokv_word_errorstofile: if (time(&now)==-1) syscallerror("get current time"); lt= localtime(&now); if (!lt) syscallerror("convert current time"); - if (fprintf(ehfile,"%d-%02d-%02d %02d:%02d:%02d: uservd: %s\n", + if (fprintf(eh.file,"%d-%02d-%02d %02d:%02d:%02d: uservd: %s\n", lt->tm_year+1900, lt->tm_mon+1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, errmsg) == EOF) { e= errno; - closeerrorfile(); ehandling= tokv_word_errorstofile; + closeerrorfile(); eh.handling= tokv_word_errorstofile; snyprintf(suberrmsg,sizeof(suberrmsg), "error writing to error log file `%.*s': %s;" " reverting to errors-to-stderr", - (int)(sizeof(suberrmsg)>>1),ehfilename,strerror(e)); - senderrmsg(suberrmsg,ehandling); - senderrmsg(errmsg,ehandling); + (int)(sizeof(suberrmsg)>>1),eh.filename,strerror(e)); + senderrmsg(suberrmsg,eh.handling); + senderrmsg(errmsg,eh.handling); } break; default: @@ -82,22 +80,22 @@ static void senderrmsg(const char *errmsg, int useehandling) { } } -static void errwhere(int iflineno, const char *filename, int *ifnotedreferer, - struct parser_state *upstate, char *bufput, int bufputlen) { +static void errwhere(struct parser_state *tstate, char *bufput, int bufputlen) { static const char suffix[]= "references ..."; char errmsg[MAX_ERRMSG_LEN]; - if (!upstate) { - filename= ""; - } else if (!*ifnotedreferer && upstate->upstate && upstate->upstate->upstate) { - errwhere(upstate->lineno-upstate->atnewline,upstate->filename, - &upstate->notedreferer,upstate->upstate, - errmsg,sizeof(errmsg)-sizeof(suffix)); + if (!tstate) { + strnycpy(bufput,": ",bufputlen); + return; + } + if (!tstate->notedreferer && tstate->upstate && !tstate->upstate->isinternal) { + errwhere(tstate->upstate,errmsg,sizeof(errmsg)-sizeof(suffix)); strcat(errmsg,suffix); - senderrmsg(errmsg,ehandling); - *ifnotedreferer= 1; + senderrmsg(errmsg,eh.handling); + tstate->notedreferer= 1; } - snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,filename,iflineno); + snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10, + tstate->filename,tstate->reportlineno); } void parseerrprint(const char *fmt, ...) { @@ -105,13 +103,31 @@ void parseerrprint(const char *fmt, ...) { char errmsg[MAX_ERRMSG_LEN]; va_start(al,fmt); - errwhere(lineno-atnewline,currentfile,¬edreferer,parser_topstate, - errmsg,sizeof(errmsg)>>1); + errwhere(cstate,errmsg,sizeof(errmsg)>>1); vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al); - senderrmsg(errmsg,ehandling); + senderrmsg(errmsg,eh.handling); va_end(al); } +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; + parseerrprint("found %s, expected %s",printtoken(found),wantedstr); + return tokv_error; +} + +static int stringoverflow(const char *where) { + parseerrprint("string buffer became far too large building %s",where); + return tokv_error; +} + +/* + * General assistance functions + */ + static void freecharparray(char **array) { char **pp; @@ -156,27 +172,34 @@ static int dequote(char *inplace) { } } } - assert(*p); assert(!*++p); return tokv_quotedstring; + assert(*p); assert(!*++p); + 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; @@ -206,66 +230,34 @@ const char *printtoken(int token) { } static const char *string2path(const char *in) { - static char *p=0; - static int pl=0; - - int l; + /* Returned pointers become invalid on next call. + * May return 0, having printed an error message. + */ + static char *p; + static int pl; - if (strncmp(in,USERDIRPREFIX,sizeof(USERDIRPREFIX)-1)) return in; - l= strlen(serviceuser_dir)+strlen(in)+1-(sizeof(USERDIRPREFIX)-1)+ - sizeof(DIRSEP)-1; - if (l>pl) { p= realloc(p,l); pl=l; } - strcpy(p,serviceuser_dir); strcat(p,DIRSEP); - strcat(p,in+(sizeof(USERDIRPREFIX)-1)); + if (strncmp(in,"~/",2)) return in; + 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 *saveinto, - const char *newfile) { - saveinto->lineno= lineno; - saveinto->atnewline= atnewline; - saveinto->filename= currentfile; - saveinto->notedreferer= notedreferer; - saveinto->ybuf= YY_CURRENT_BUFFER; - saveinto->upstate= parser_topstate; - - lineno= 1; - notedreferer= 0; - currentfile= newfile; - parser_topstate= saveinto; -} - -static void parser_pop(void) { - YY_BUFFER_STATE ybuf; - ybuf= YY_CURRENT_BUFFER; - - lineno= parser_topstate->lineno; - atnewline= parser_topstate->atnewline; - currentfile= parser_topstate->filename; - notedreferer= parser_topstate->notedreferer; - if (parser_topstate->ybuf) yy_switch_to_buffer(parser_topstate->ybuf); - parser_topstate= parser_topstate->upstate; - - yy_delete_buffer(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"); } @@ -273,16 +265,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]= xstrdup(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; @@ -292,14 +275,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; @@ -307,102 +300,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) { - /* Scans a single parameter token and calls the appropriate parameter - * function, returning tokv_error or 0 just like the parameter function. +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 **newargs; + const char *string; + int used, size, r; - token= yylex(); if (token == tokv_error) return token; - 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][1]); + 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)) return unexpected(token,-1,"parameter name"); - r= (lr_parameter)(token,rvalues); - if (r) return r; + r= yylex(); if (r==tokv_error) goto error; + if ((r & tokm_repres) == tokr_nonstring) { + r= unexpected(r,-1,"string for command argument"); + goto error; + } + if (used>=size) { + if (used >= MAX_ARGSDEFVAR) { + parseerrprint("far too many arguments to service program"); + r= tokv_error; goto error; + } + size= (used+5)<<1; + newargs= xrealloc(newargs,sizeof(char*)*(size+1)); + } + newargs[used++]= xstrsave(yytext); } - debug_dumpparameter(yytext,*rvalues); + newargs[used]= 0; + *newargs_r= newargs; return 0; + +error: + newargs[used]=0; + freecharparray(newargs); + return r; } -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; +static int paa_message(const char **message_r) { + /* Returned value is invalidated by repeated calls. */ + static char *buildbuf; + static int buildbuflen; + + int r, tl; 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 (;;) { - 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; - } + 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) { + parseerrprint("unexpected end of file in message text"); + return tokv_error; } - 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); if (r) return r; - r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues); - return r; + if (r == tokv_newline) break; + tl+= strlen(yytext); + if (makeroom(&buildbuf,&buildbuflen,tl)) return stringoverflow("message"); + strcat(buildbuf,yytext); } - return unexpected(token,-1,"condition"); -} - -static int pa_numberdollar(int *rv) { - /* 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; - *rv= lr_min; return r; + *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(); } @@ -415,23 +401,27 @@ static int skiptoeol(void) { } 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; 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 (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) { r= skiptoeol(); if (r) return r; + cstate->reportlineno= cstate->lineno; r= skip(token); if (r & tokt_exception) return r; } } else if (!(token & tokt_directive) && !(token & tokt_condop)) { @@ -442,101 +432,130 @@ static int skip(int allowce) { 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 */ - 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 + */ + +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; } -int parse_string(const char *string, const char *descrip) { - /* Returns the same things as parser, except that tokv_eof - * is turned into 0. */ - struct parser_state save; - YY_BUFFER_STATE ybuf; - int r; - - parser_push(&save,descrip); - ybuf= yy_scan_string(string); - if (!ybuf) syscallerror("unable to create flex buffer for internal string"); - yy_switch_to_buffer(ybuf); - - 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 save; - 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; @@ -554,9 +573,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; @@ -573,14 +593,16 @@ 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) { + file= fopen(cp,"r"); + if (!file) { parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno)); return tokv_error; } @@ -595,7 +617,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; } @@ -613,103 +636,80 @@ 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]= xstrdup(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 xstrdup(ge->gr_name); -} +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, and parsing up to and including + * the last newline) or tokv_error. + * Expects to scan the whitespace before its condition. + */ + int r, token, andor, ctrue, actrue; + char **parmvalues; -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; ireportlineno= 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; + } + } + 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"); } - a[size*2]= 0; - *rvalues= a; return 0; -} - -int pf_callinggroup(int ptoken, char ***rvalues) { - return parm_gids(rvalues,request_mbuf.ngids,gidarray); } -int pf_servicegroup(int ptoken, char ***rvalues) { - static int size=-1; - static gid_t *list; - - if (size == -1) { - size= getgroups(0,0); if (size == -1) syscallerror("getgroups(0,0)"); - list= xmalloc(sizeof(gid_t)*(size+1)); - if (getgroups(size,list+1) != size) syscallerror("getgroups(size,list)"); - list[0]= serviceuser_gid; - } - return parm_gids(rvalues,size,list); -} +/* + * Directive functions and associated `common code' functions + */ -int pf_callingusershell(int ptoken, char ***rvalues) { - parm_1string(rvalues,callinguser_shell); return 0; -} +/* Directives specifying the service program (execute-... and reset) */ -int pf_serviceusershell(int ptoken, char ***rvalues) { - parm_1string(rvalues,serviceuser_shell); return 0; +static void execreset(void) { + execute= 0; + execbuiltin= 0; + free(execpath); execpath= 0; + freecharparray(execargs); execargs= 0; } -/* Directive functions and associated `common code' functions */ - int df_reject(int dtoken) { int r; r= pa_mnl(); if (r) return r; + execreset(); execute= tokv_word_reject; - free(execpath); execpath= 0; - freecharparray(execargs); execargs= 0; return 0; } @@ -717,48 +717,9 @@ int df_executefrompath(int dtoken) { int r; r= pa_mnl(); if (r) return r; + execreset(); execute= tokv_word_executefrompath; - free(execpath); execpath= 0; - freecharparray(execargs); execargs= 0; - return 0; -} - -static int paa_pathargs(const char **rv, char ***newargs_r) { - /* Repeated calls do _not_ overwrite newargs_r; caller must free. - * Repeated calls _do_ overwrite string returned in rv. - */ - char **newargs; - int used, size, r; - - used=0; size=0; - newargs= xmalloc(sizeof(char*)*(size+1)); - r= pa_string(rv); if (r == tokv_error) return r; - 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; - } - r= yylex(); if (r==tokv_error) goto error; - if ((r & tokm_repres) == tokr_nonstring) { - r= unexpected(r,-1,"string for command argument"); - goto error; - } - if (used>=size) { - size= (used+5)<<2; - newargs= xrealloc(newargs,sizeof(char*)*(size+1)); - } - newargs[used++]= xmstrsave(yytext); - } - newargs[used]= 0; - *newargs_r= newargs; return 0; - -error: - newargs[used]=0; - freecharparray(newargs); - return r; } int df_execute(int dtoken) { @@ -767,9 +728,10 @@ int df_execute(int dtoken) { int r; r= paa_pathargs(&rv,&newargs); if (r) return r; + execreset(); execute= tokv_word_execute; - freecharparray(execargs); execargs= newargs; - free(execpath); execpath= xmstrsave(rv); + execargs= newargs; + execpath= xstrsave(rv); return 0; } @@ -797,8 +759,9 @@ int df_executefromdirectory(int dtoken) { return tokv_error; } } - l= strlen(rv)+sizeof(DIRSEP)+strlen(p); - fn= xmalloc(l); strcpy(fn,rv); strcat(fn,DIRSEP); strcat(fn,p); + l= strlen(rv)+1+strlen(p)+1; + fn= xmalloc(l); + snyprintf(fn,l,"%s/%s",rv,p); if (stat(fn,&stab)) { if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; } parseerrprint("failed to stat `%s' for execute-from-directory: %s", @@ -807,20 +770,134 @@ 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(); execute= tokv_word_executefromdirectory; - freecharparray(execargs); execargs= newargs; - free(execpath); execpath= fn; + execargs= newargs; + execpath= fn; + return 0; +} + +/* Parsing builtin service requests (execute-builtin) */ + +static int bispa_none(char ***rnewargs) { + return pa_mnl(); +} + +static int bispa_parameter(char ***rnewargs) { + int r, i; + char **parmvalues, *name, **newargs; + + r= pa_mwsp(); if (r) return r; + r= pa_parameter(&parmvalues,&name); if (r) return r; + for (i=0; parmvalues[i]; i++); + newargs= xmalloc(sizeof(char*)*(i+2)); + newargs[0]= name; + memcpy(newargs+1,parmvalues,sizeof(char*)*(i+1)); + free(parmvalues); + r= pa_mnl(); if (r) { free(newargs); return r; } + *rnewargs= newargs; + return 0; +} + +int df_executebuiltin(int dtoken) { + int r; + builtinserviceexec_fnt *bisexec; + char *newpath, **newargs; + + r= pa_mwsp(); if (r) return r; + r= yylex(); if (r == tokv_error) return r; + if (!(r & tokt_builtinservice)) return unexpected(r,-1,"builtin service name"); + bisexec= lr_bisexec; + newpath= xstrsave(yytext); + newargs= 0; + r= lr_bispa(&newargs); if (r) { free(newpath); return r; } + + execreset(); + execute= tokv_word_executebuiltin; + execbuiltin= bisexec; + execpath= newpath; + execargs= newargs; + return 0; +} + +/* Directives for changing other execution parameters */ + +int dfg_setflag(int dtoken) { + int r; + + r= pa_mnl(); if (r) return r; + *lr_flag= lr_flagval; + return 0; +} + +int df_reset(int dtoken) { + int r; + + r= pa_mnl(); if (r) return r; + r= parse_string(RESET_CONFIGURATION,"",1); + assert(!r); + return 0; +} + +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) { + parseerrprint("file descriptor in range is negative or far too large"); + return tokv_error; + } + 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; + } + ensurefdarray(fdmax); + for (fd=fdmin; fd<=fdmax; fd++) { + fdarray[fd].wantstate= dtoken; + fdarray[fd].wantrw= havereadwrite; + } return 0; } +/* Directives for changing error handling */ + int df_errorstostderr(int dtoken) { int r; r= pa_mnl(); if (r) return r; - closeerrorfile(); ehandling= dtoken; return 0; + closeerrorfile(); eh.handling= dtoken; + return 0; } int df_errorstosyslog(int dtoken) { @@ -843,10 +920,11 @@ int df_errorstosyslog(int dtoken) { level= lr_loglevel; token= yylex(); } - if (unexpected(token,tokv_newline,"end of line somewhere after errors-to-syslog")) + if (unexpected(token,tokv_newline,"end of line after errors-to-syslog")) return tokv_error; - closeerrorfile(); ehandling= tokv_word_errorstosyslog; - ehlogfacility= facility; ehloglevel= level; return 0; + closeerrorfile(); eh.handling= tokv_word_errorstosyslog; + eh.logfacility= facility; eh.loglevel= level; + return 0; } int df_errorstofile(int dtoken) { @@ -864,63 +942,27 @@ int df_errorstofile(int dtoken) { parseerrprint("unable to set line buffering on errors file: %s",strerror(errno)); fclose(file); return tokv_error; } - closeerrorfile(); ehandling= tokv_word_errorstofile; - ehfile= file; ehfilename= xmstrsave(cp); ehfilekeep= 0; return 0; -} - -int dfg_setflag(int dtoken) { - int r; - - r= pa_mnl(); if (r) return r; - *lr_flag= lr_flagval; return 0; -} - -int df_reset(int dtoken) { - int r; - - r= pa_mnl(); if (r) return r; - r= parse_string(RESET_CONFIGURATION,""); - assert(!r); return 0; -} - -int df_cd(int dtoken) { - const char *cp; - int r; - - 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; -} - -int df_userrcfile(int dtoken) { - const char *cp; - int r; - - r= paa_1path(&cp); if (r) return r; - free(userrcfile); userrcfile= xstrdup(cp); + 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; - } - return parse_string(overridedata,""); + assert(overridedata); + return parse_string(overridedata,"",0); } int df_include(int dtoken) { @@ -937,47 +979,6 @@ int df_include(int dtoken) { 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; -} - int df_includedirectory(int dtoken) { static char *buildbuf=0; static int buildbuflen=0; @@ -998,18 +999,18 @@ int df_includedirectory(int dtoken) { 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+tel+sizeof(DIRSEP)); - strcpy(buildbuf,cp); - strcat(buildbuf,DIRSEP); - strcat(buildbuf,de->d_name); + if (makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1)) + return stringoverflow("pathname in directory"); + snyprintf(buildbuf,buildbuflen,"%s/%s",cp,de->d_name); r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; } if (!found) { parseerrprint("unable to open file `%s' in included directory `%s': %s", de->d_name,cp,strerror(errno)); - closedir(d); return tokv_error; + closedir(d); + return tokv_error; } } if (closedir(d)) { @@ -1029,7 +1030,7 @@ int df_includelookup(int dtoken) { int r, done, thisdone, cpl, c; r= pa_mwsp(); if (r) return r; - r= pa_parameter(&parmvalues); 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)); @@ -1042,9 +1043,9 @@ int df_includelookup(int dtoken) { done= 0; cpl= strlen(cp); if (!parmvalues[0]) { - makeroom(&buildbuf,&buildbuflen,cpl+sizeof(DIRSEP NONEINCLUDELOOKUP)); - strcpy(buildbuf,cp); - strcat(buildbuf,DIRSEP NONEINCLUDELOOKUP); + if (makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP))) + return stringoverflow("pathname in directory for lookup of undefined parameter"); + snyprintf(buildbuf,buildbuflen,"%s/" NONEINCLUDELOOKUP,cp); r= parse_file(buildbuf,&thisdone); if (r) { freecharparray(parmvalues); return r; } if (thisdone) done= 1; @@ -1052,18 +1053,23 @@ int df_includelookup(int dtoken) { for (pp=parmvalues; *pp && (!done || dtoken == tokv_word_includelookupall); pp++) { - makeroom(&buildbuf,&buildbuflen, - cpl+sizeof(DIRSEP)+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)); + if (makeroom(&buildbuf,&buildbuflen, + cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1)) + return stringoverflow("pathname in directory for lookup"); strcpy(buildbuf,cp); - strcat(buildbuf,DIRSEP); - p= *pp; q= buildbuf+cpl+sizeof(DIRSEP)-1; - if (*p=='.') *q++= ':'; - while ((c= *p++)) { - if (c=='/') { *q++= ':'; c='-'; } - else if (c==':') { *q++= ':'; } - *q++= c; + p= *pp; q= buildbuf+cpl; + *q++= '/'; + if (!*p) { + strcpy(q,EMPTYINCLUDELOOKUP); + } else { + if (*p=='.') *q++= ':'; + while ((c= *p++)) { + if (c=='/') { *q++= ':'; c='-'; } + else if (c==':') { *q++= ':'; } + *q++= c; + } + *q++= 0; } - *q++= 0; r= parse_file(buildbuf,&thisdone); if (r) { freecharparray(parmvalues); return r; } if (thisdone) done= 1; @@ -1071,37 +1077,33 @@ int df_includelookup(int dtoken) { } freecharparray(parmvalues); if (!done) { - makeroom(&buildbuf,&buildbuflen, - cpl+sizeof(DIRSEP)+sizeof(DEFAULTINCLUDELOOKUP)); - strcpy(buildbuf,cp); - strcat(buildbuf,DIRSEP DEFAULTINCLUDELOOKUP); + if (makeroom(&buildbuf,&buildbuflen, + cpl+1+sizeof(DEFAULTINCLUDELOOKUP))) + return stringoverflow("pathname in directory for lookup of default"); + snyprintf(buildbuf,buildbuflen,"%s/" DEFAULTINCLUDELOOKUP,cp); r= parse_file(buildbuf,0); if (r) return r; } return 0; } +/* 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); - while (!atnewline) { - r= yylex(); if (r == tokv_error) return 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; } @@ -1114,64 +1116,77 @@ 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; + parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno)); + return tokv_error; +} + +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; + parseerrprint("`error' directive: %s",mp); + return tokv_error; } int df_eof(int dtoken) { @@ -1181,34 +1196,150 @@ int df_eof(int dtoken) { return tokv_eof; } -int df_errorspush(int dt) { - int saveehandling, saveehlogfacility, saveehloglevel, saveehfilekeep; - FILE *saveehfile; - char *saveehfilename; +int df_quit(int dtoken) { int r; r= pa_mnl(); if (r) return r; + return tokv_quit; +} + +/* + * 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; + + cstate= usestate; + yy_switch_to_buffer(ybuf); +} - saveehandling= ehandling; - saveehlogfacility= ehlogfacility; - saveehloglevel= ehloglevel; - saveehfile= ehfile; - saveehfilekeep= ehfilekeep; - saveehfilename= ehfilename; - if (ehandling == tokv_word_errorstofile) ehfilekeep= 1; +static void parser_pop(void) { + struct parser_state *oldstate; - r= parser(tokv_word_errorspush); + 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. + */ + static const struct stat blankstab; - ehandling= saveehandling; - ehlogfacility= saveehlogfacility; - ehloglevel= saveehloglevel; - ehfile= saveehfile; - ehfilekeep= saveehfilekeep; - ehfilename= saveehfilename; + struct parser_state usestate; + YY_BUFFER_STATE ybuf; + int r; - if (r & tokt_controlend) { - assert(r == tokv_word_srorre); - r= pa_mnl(); + 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. + */ + static int fileparselevel= 0; + + struct parser_state usestate, *checkrecurse; + YY_BUFFER_STATE ybuf; + int r; + FILE *file; + struct stat newstab; + + 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; + } + 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) { + parseerrprint("recursion detected - config file `%s' calls itself",string); + fclose(file); + return tokv_error; + } } + + if (didexist) *didexist= 1; + + ybuf= yy_create_buffer(file,YY_BUF_SIZE); + if (!ybuf) syscallerror("unable to create flex buffer for file"); + parser_push(&usestate,string,&newstab,ybuf,0); + fileparselevel++; + + r= parser(0); + if (ferror(file)) { + parseerrprint("error reading configuration file `%s'",string); + r= tokv_error; + } + + fileparselevel--; + parser_pop(); + 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"); + } + } +}