* 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
+ * userv is
+ * Copyright 1996-2017 Ian Jackson <ian@davenant.greenend.org.uk>.
+ * Copyright 2000 Ben Harris <bjh21@cam.ac.uk>
+ * Copyright 2016-2017 Peter Benie <pjb1008@cam.ac.uk>
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with userv; if not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with userv; if not, see <http://www.gnu.org/licenses/>.
*/
-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) {
tstate->filename,tstate->reportlineno);
}
-void parseerrprint(const char *fmt, ...) {
+int parseerrprint(const char *fmt, ...) {
va_list al;
char errmsg[MAX_ERRMSG_LEN];
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;
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++ = '"');
+ assert(*p=='"'); 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<hex> sequence");
+ buf[2]= 0;
+ v= strtoul(buf,&bep,16);
+ if (bep != buf+2)
+ return parseerrprint("invalid \\<hex> sequence \\x%s in quoted string",buf);
+ assert(!(v & ~0xff));
+ *q++= v;
+ continue;
default:
- if (isalpha(*p)) {
- parseerrprint("unknown \\<letter> 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 (ISCHAR(isalpha,*p))
+ return parseerrprint("unknown \\<letter> sequence \\%c in quoted string",*p);
+ if (ISCHAR(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 \\<octal> sequence \\%s in quoted string",buf);
- return tokv_error;
- }
- *q++= v; p++; continue;
- } else if (ispunct(*p)) {
+ if (bep != buf+3 || (v & ~0xff))
+ return parseerrprint("invalid \\<octal> sequence \\%s in quoted string",buf);
+ *q++= v; continue;
+ } else if (ISCHAR(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); 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)+50<sizeof(buf));
- sprintf(buf,"word `%s'",yytext);
+ assert(strlen(yytext)+sizeof(keywordfmt)<sizeof(buf));
+ snyprintf(buf,sizeof(buf),keywordfmt,yytext);
return buf;
} else if (token & tokt_number) {
- sprintf(buf,"number %d",lr_min); return buf;
+ snyprintf(buf,sizeof(buf),"number %d",lr_min); return buf;
+ } else if (token & tokt_fdrange && token & tokr_word) {
+ snyprintf(buf,sizeof(buf),"fd %s",buf); return buf;
+ } else if (token & tokt_fdrange && lr_max==-1) {
+ snyprintf(buf,sizeof(buf),"fdrange %d-",lr_min); return buf;
} else if (token & tokt_fdrange) {
- sprintf(buf,"fdrange %d..%d",lr_min,lr_max); return buf;
+ snyprintf(buf,sizeof(buf),"fdrange %d-%d",lr_min,lr_max); return buf;
} else if ((token & tokm_repres) == tokr_punct) {
- assert(strlen(yytext)+50<sizeof(buf));
- sprintf(buf,"operator `%s'",yytext);
+ assert(strlen(yytext)+sizeof(operatorfmt)<sizeof(buf));
+ snyprintf(buf,sizeof(buf),operatorfmt,yytext);
return buf;
} else if (token & tokt_string) {
switch (token) {
default: abort();
}
strcat(buf," `");
- i= sizeof(buf)-50; p= yytext; q= buf+strlen(buf);
- while (i-- >0 && (c= *p++)) {
- if (isspace(c)) c= ' ';
- else if (!isprint(c) || iscntrl(c)) c= '?';
- *q++= c;
+ 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 (ISCHAR(isspace,c)) c= ' ';
+ else if (!ISCHAR(isprint,c) || ISCHAR(iscntrl,c)) c= '?';
+ else *q++= c;
}
strcpy(q,"'");
return buf;
}
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");
}
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;
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;
}
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 && strcmp(yytext+2,defvararray[i].key);
- i++);
- if (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(); }
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;
for (;;) { /* loop over lines */
cstate->reportlineno= cstate->lineno;
- do { token= yylex(); } while (token == tokv_lwsp);
+ do { token= yylex(); } while (token == tokv_lwsp || token == tokv_newline);
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= 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<size; i++) {
+ a[i]= xstrsave(groups[i]);
+ a[size+i]= parm_ulong(gids[i]);
}
- if (didexist) *didexist= 1;
+ a[size*2]= 0;
+ *rvalues= a;
+ return 0;
+}
- ybuf= yy_create_buffer(file,YY_BUF_SIZE);
- if (!ybuf) syscallerror("unable to create flex buffer for file");
- parser_push(&usestate,string,ybuf,0);
- fileparselevel++;
-
- r= parser(0);
- if (ferror(file)) {
- parseerrprint("error reading configuration file `%s'",string);
- r= tokv_error;
+static int pf_service(int ptoken, char ***rvalues) {
+ parm_1string(rvalues,service); return 0;
+}
+
+static int pf_callinguser(int ptoken, char ***rvalues) {
+ return parm_usernameuid(rvalues,loginname,request_mbuf.callinguid);
+}
+
+static int pf_serviceuser(int ptoken, char ***rvalues) {
+ return parm_usernameuid(rvalues,serviceuser,serviceuser_uid);
+}
+
+static int pf_callinggroup(int ptoken, char ***rvalues) {
+ return parm_groups(rvalues,request_mbuf.ngids,calling_gids,calling_groups);
+}
+
+static int pf_servicegroup(int ptoken, char ***rvalues) {
+ return parm_groups(rvalues,service_ngids,service_gids,service_groups);
+}
+
+static int pf_callingusershell(int ptoken, char ***rvalues) {
+ parm_1string(rvalues,callinguser_shell); return 0;
+}
+
+static int pf_serviceusershell(int ptoken, char ***rvalues) {
+ parm_1string(rvalues,serviceuser_shell); return 0;
+}
+
+static int pa_parameter(char ***rvalues, char **rname) {
+ /* Scans a single parameter token and calls the appropriate parameter
+ * function, returning tokv_error (leaving the parser state wherever),
+ * or 0 on success (having just parsed the parameter name).
+ * If rname is non-null then the name of the parameter (malloc'd) will
+ * be stored in it.
+ *
+ * Caller must free *rvalues using freecharparray.
+ */
+ int token, r, i;
+ char *name;
+
+ 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 && strcmp(yytext+2,defvararray[i].key);
+ i++);
+ if (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;
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;
mintoken= pa_numberdollar(&min); if (mintoken == tokv_error) return mintoken;
- r= pa_mwsp(); if (r) return r;
maxtoken= pa_numberdollar(&max); if (maxtoken == tokv_error) return maxtoken;
r= pa_mnl(); if (r) return r;
for (pp= pv; *pp; pp++) {
*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;
while (!actrue && c!=EOF) {
c= getc(file); if (c==EOF) break;
- if (isspace(c)) continue;
+ if (ISCHAR(isspace,c)) continue;
l= maxlen+1; p= buf;
while (l>0 && c!='\n' && c!=EOF) { *p++= c; l--; c= getc(file); }
- if (c=='\n' || c==EOF || isspace(c)) {
- while (p>buf && isspace(p[-1])) --p;
+ if (c=='\n' || c==EOF || ISCHAR(isspace,c)) {
+ while (p>buf && ISCHAR(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;
}
for (;;) {
c= getc(file);
if (c==EOF || c=='\n') break;
- if (!isspace(c)) posstrue= 0;
+ if (!ISCHAR(isspace,c)) posstrue= 0;
}
}
if (posstrue) actrue= 1;
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; i++) {
- a[i]= parm_gidname(list[i]);
- a[size+i]= parm_ulong(list[i]);
- }
- a[size*2]= 0;
- *rvalues= a; return 0;
-}
-
-int pf_callinggroup(int ptoken, char ***rvalues) {
- return parm_gids(rvalues,request_mbuf.ngids,calling_gids);
-}
-
-int pf_servicegroup(int ptoken, char ***rvalues) {
- return parm_gids(rvalues,service_ngids,service_gids);
-}
-
-int pf_callingusershell(int ptoken, char ***rvalues) {
- parm_1string(rvalues,callinguser_shell); return 0;
-}
-
-int pf_serviceusershell(int ptoken, char ***rvalues) {
- parm_1string(rvalues,serviceuser_shell); return 0;
-}
-
-/* Directive functions and associated `common code' functions */
-
-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.
+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.
*/
- char **newargs;
- int used, size, r;
+ int r, token, andor, ctrue, actrue;
+ char **parmvalues;
- 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));
+ 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;
r= paa_pathargs(&rv,&newargs); if (r) return r;
p= strrchr(service,'/'); if (p) p++; else p= service;
- if (!*p || !isalnum(*p)) {
+ if (!*p || !ISCHAR(isalnum,*p)) {
parseerrprint("execute-from-directory requires initial char of service "
"portion to be alphanumeric (service portion was `%s')",
p);
return tokv_error;
}
for (q=p+1; *q; q++) {
- if (!isalnum(*q) && *q != '-') {
+ if (!ISCHAR(isalnum,*q) && *q != '-') {
parseerrprint("execute-from-directory requires service portion to "
"contain only alphanumerics and hyphens (was `%s')",
p);
}
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();
return 0;
}
+/* Parsing builtin service requests (execute-builtin) */
+
static int bispa_none(char ***rnewargs) {
return pa_mnl();
}
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,"<builtin 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;
+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");
}
- 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;
+ 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;
}
- closeerrorfile(); eh.handling= tokv_word_errorstofile;
- eh.file= file; eh.filename= xstrsave(cp); return 0;
+ 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,"<builtin 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,"<configuration override data>",0);
}
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) {
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))) {
+ while ((errno=0, de= readdir(d))) {
tel= strlen(de->d_name);
if (!tel) continue;
p= de->d_name;
- if (!isalnum(*p)) continue;
- while ((c= *++p)) if (!(isalnum(c) || c=='-')) break;
+ if (!*p || !ISCHAR(isalnum,*p)) continue;
+ while ((c= *++p)) if (!(ISCHAR(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 (errno) {
+ parseerrprint("error reading directory `%s': %s",cp,strerror(errno));
+ closedir(d);
+ free(cp);
+ return tokv_error;
+ }
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;
+}
+
+static int oldquote = 0;
+
+int dfg_lookupquotemode(int dtoken) {
+ int r;
+ r= pa_mnl(); if (r) return r;
+ oldquote = dtoken == tokv_word_includelookupquoteold;
+ 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 (oldquote ?
+ !((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'z') ||
+ c == '-' || 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,
"<builtin reset configuration (caught error)>",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;
}
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) {
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");
+ }
+ }
+}