3 * configuration file parser; this file is actually #included from
4 * lexer.c, which is generated using flex from lexer.l, in turn from
5 * lexer.l.m4. It's in a separate file so that we don't have to worry
8 * Copyright (C)1996-1997 Ian Jackson
10 * This is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with userv; if not, write to the Free Software
22 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 static void useless(void) { (void)yyunput; (void)useless; /* to shut up GCC ! */ }
27 static void closeerrorfile(void) {
28 if (eh.file && !eh.filekeep) {
29 if (fclose(eh.file)) {
30 eh.handling= tokv_word_errorstostderr;
31 parseerrprint("error writing to error log file `%s': %s",
32 eh.filename,strerror(errno));
37 eh.file= 0; eh.filename= 0; eh.filekeep= 0;
40 static void senderrmsg(const char *errmsg, int useehandling) {
41 char suberrmsg[MAX_ERRMSG_LEN];
46 switch (useehandling) {
47 case tokv_word_errorstostderr:
48 senderrmsgstderr(errmsg);
50 case tokv_word_errorstosyslog:
51 ensurelogopen(eh.logfacility);
52 syslog(eh.loglevel,"%s",errmsg);
54 case tokv_word_errorstofile:
55 if (time(&now)==-1) syscallerror("get current time");
56 lt= localtime(&now); if (!lt) syscallerror("convert current time");
57 if (fprintf(eh.file,"%d-%02d-%02d %02d:%02d:%02d: uservd: %s\n",
58 lt->tm_year+1900, lt->tm_mon+1, lt->tm_mday,
59 lt->tm_hour, lt->tm_min, lt->tm_sec,
62 closeerrorfile(); eh.handling= tokv_word_errorstofile;
63 snyprintf(suberrmsg,sizeof(suberrmsg),
64 "error writing to error log file `%.*s': %s;"
65 " reverting to errors-to-stderr",
66 (int)(sizeof(suberrmsg)>>1),eh.filename,strerror(e));
67 senderrmsg(suberrmsg,eh.handling);
68 senderrmsg(errmsg,eh.handling);
76 static void errwhere(struct parser_state *tstate, char *bufput, int bufputlen) {
77 static const char suffix[]= "references ...";
78 char errmsg[MAX_ERRMSG_LEN];
81 strnycpy(bufput,"<initialisation>: ",bufputlen);
84 if (!tstate->notedreferer && tstate->upstate && !tstate->upstate->isinternal) {
85 errwhere(tstate->upstate,errmsg,sizeof(errmsg)-sizeof(suffix));
86 strcat(errmsg,suffix);
87 senderrmsg(errmsg,eh.handling);
88 tstate->notedreferer= 1;
90 snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,
91 tstate->filename,tstate->reportlineno);
94 void parseerrprint(const char *fmt, ...) {
96 char errmsg[MAX_ERRMSG_LEN];
99 errwhere(cstate,errmsg,sizeof(errmsg)>>1);
100 vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al);
101 senderrmsg(errmsg,eh.handling);
105 static void freecharparray(char **array) {
109 for (pp=array; *pp; pp++) free(*pp);
113 static int dequote(char *inplace) {
114 char *p, *q, buf[4], *bep;
119 while (*p && *p != '"') {
120 if (*p != '\\') { *q++= *p++; continue; }
122 case 'n': *q++= '\n'; continue;
123 case 'r': *q++= '\r'; continue;
124 case 't': *q++= '\t'; continue;
126 assert(buf[0]= *++p); assert(buf[1]= *++p); buf[2]= 0;
127 v= strtoul(buf,&bep,16); assert(bep == buf+2);
128 assert(!(v & ~0xff)); *q++= v; p++; continue;
131 parseerrprint("unknown \\<letter> sequence \\%c in quoted string",*p);
133 } else if (isdigit(*p)) {
134 assert(buf[0]= *++p); assert(buf[1]= *++p); assert(buf[2]= *++p);
135 buf[3]= 0; v= strtoul(buf,&bep,8);
136 if (bep != buf+3 || (v & ~0xff)); {
137 parseerrprint("invalid \\<octal> sequence \\%s in quoted string",buf);
140 *q++= v; p++; continue;
141 } else if (ispunct(*p)) {
142 *q++= *p++; continue;
144 while (*p==' ' || *p=='\t') p++;
149 assert(*p); assert(!*++p); return tokv_quotedstring;
152 const char *printtoken(int token) {
153 static char buf[250];
159 if ((token & tokm_repres) == tokr_word) {
160 assert(strlen(yytext)+50<sizeof(buf));
161 sprintf(buf,"word `%s'",yytext);
163 } else if (token & tokt_number) {
164 sprintf(buf,"number %d",lr_min); return buf;
165 } else if (token & tokt_fdrange) {
166 sprintf(buf,"fdrange %d..%d",lr_min,lr_max); return buf;
167 } else if ((token & tokm_repres) == tokr_punct) {
168 assert(strlen(yytext)+50<sizeof(buf));
169 sprintf(buf,"operator `%s'",yytext);
171 } else if (token & tokt_string) {
173 case tokv_barestring: strcpy(buf,"unquoted string (bare word)"); break;
174 case tokv_quotedstring: strcpy(buf,"quoted string"); break;
178 i= sizeof(buf)-50; p= yytext; q= buf+strlen(buf);
179 while (i-- >0 && (c= *p++)) {
180 if (isspace(c)) c= ' ';
181 else if (!isprint(c) || iscntrl(c)) c= '?';
188 case tokv_lwsp: return "linear whitespace";
189 case tokv_newline: return "newline (or comment followed by newline)";
190 case tokv_eof: return "end of input file";
191 case tokv_error: return "syntax error token";
193 sprintf(buf,"token#0%o",token); return buf;
198 static const char *string2path(const char *in) {
202 if (strncmp(in,"~/",2)) return in;
203 makeroom(&p,&pl,strlen(serviceuser_dir)+1+strlen(in+2)+1);
204 snyprintf(p,pl,"%s/%s",serviceuser_dir,in+2);
208 static void parser_push(struct parser_state *usestate,
210 YY_BUFFER_STATE ybuf,
213 usestate->reportlineno= 1;
214 usestate->filename= newfile;
215 usestate->notedreferer= 0;
216 usestate->isinternal= isinternal;
217 usestate->ybuf= ybuf;
218 usestate->upstate= cstate;
221 yy_switch_to_buffer(ybuf);
224 static void parser_pop(void) {
225 struct parser_state *oldstate;
228 cstate= cstate->upstate;
229 if (cstate) yy_switch_to_buffer(cstate->ybuf);
230 yy_delete_buffer(oldstate->ybuf);
233 /* parser component functions pa_ return tokv_error or 0,
234 * having scanned exactly what they were expecting
235 * complete argument list parsers paa_ return tokv_error or 0,
236 * having scanned up to and including the newline
239 static int unexpected(int found, int wanted, const char *wantedstr) {
240 /* pass wanted==-1 if you know it's not what you wanted;
241 * otherwise this function will check it for you.
243 if (found == wanted) return 0;
244 if (found != tokv_error) {
245 parseerrprint("found %s, expected %s",printtoken(found),wantedstr);
250 static int pa_mnl(void) {
251 return unexpected(yylex(),tokv_newline,"newline");
253 static int pa_mwsp(void) {
254 return unexpected(yylex(),tokv_lwsp,"linear whitespace");
257 static void parm_1string(char ***rvalues, const char *tocopy) {
259 a= xmalloc(sizeof(char*)*2);
260 a[0]= xstrsave(tocopy);
265 static int pa_string(const char **rv) {
266 /* Value returned in *rv is overwritten by repeated calls */
272 r= pa_mwsp(); if (r) return r;
273 r= yylex(); if (r == tokv_error) return r;
274 if ((r & tokm_repres) == tokr_nonstring) return unexpected(r,-1,"string");
277 strcpy(p,yytext); *rv= p;
282 static int paa_1string(const char **rv) {
283 /* Value returned in *rv is overwritten by repeated calls */
286 r= pa_string(rv); if (r) return r;
290 static int paa_1path(const char **rv) {
291 /* Value returned in *rv is overwritten by repeated calls */
295 r= paa_1string(&cp); if (r) return r;
296 *rv= string2path(cp); return 0;
299 static int pa_parameter(char ***rvalues, char **rname) {
300 /* Scans a single parameter token and calls the appropriate parameter
301 * function, returning tokv_error or 0 just like the parameter function.
302 * If rname is non-null then the name of the parameter (malloc'd) will
308 token= yylex(); if (token == tokv_error) return token;
309 name= xstrsave(yytext);
310 if ((token & tokm_repres) != tokr_nonstring &&
311 !memcmp(yytext,"u-",2) && strlen(yytext)>=3) {
313 i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i].key);
315 if (i>=request_mbuf.nvars) {
316 *rvalues= xmalloc(sizeof(char*));
319 parm_1string(rvalues,defvararray[i].value);
322 if (!(token & tokt_parameter)) {
324 return unexpected(token,-1,"parameter name");
326 r= (lr_parameter)(token,rvalues);
327 if (r) { free(name); return r; }
329 debug_dumpparameter(name,*rvalues);
330 if (rname) *rname= name;
335 static int pa_condition(int *rtrue) {
336 /* Scans up to and including the newline following the condition;
337 * may scan more than one line if the condition is a multi-line
338 * one. Returns 0 (setting *rtrue) or tokv_error. Expects to
339 * scan the whitespace before its condition.
341 int r, token, andor, ctrue, actrue;
344 r= pa_mwsp(); if (r) return r;
346 if (token == tokv_error) {
348 } else if (token == tokv_not) {
349 r= pa_condition(&ctrue); if (r) return r;
350 *rtrue= !ctrue; return 0;
351 } else if (token == tokv_openparen) {
352 andor= 0; actrue= -1;
354 cstate->reportlineno= cstate->lineno;
355 r= pa_condition(&ctrue); if (r) return r;
357 case 0: assert(actrue==-1); actrue= ctrue; break;
358 case tokv_and: assert(actrue>=0); actrue= actrue && ctrue; break;
359 case tokv_or: assert(actrue>=0); actrue= actrue || ctrue; break;
362 do { token= yylex(); } while (token == tokv_lwsp);
363 if (token == tokv_error) return token;
364 if (token == tokv_closeparen) break;
366 r= unexpected(token,andor,"same conjunction as before"); if (r) return r;
368 if (token != tokv_and && token != tokv_or)
369 return unexpected(token,-1,"first conjunction inside connective");
373 r= pa_mnl(); if (r) return r;
374 *rtrue= actrue; return 0;
375 } else if (token & tokt_parmcondition) {
376 r= pa_mwsp(); if (r) return r;
377 r= pa_parameter(&parmvalues,0); if (r) return r;
378 r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues);
381 return unexpected(token,-1,"condition");
384 static int pa_numberdollar(int *rv) {
385 /* Also parses the whitespace beforehand. */
388 r= pa_mwsp(); if (r) return r;
389 r= yylex(); if (r == tokv_error || r == tokv_dollar) return r;
390 if (unexpected(r,tokv_ordinal,"expected number or dollar")) return tokv_error;
391 *rv= lr_min; return r;
394 /* Main parser routines */
396 static int skiptoeol(void) {
399 do { token= yylex(); }
400 while (token != tokv_newline && !(token & tokt_exception));
401 if (token == tokv_newline) return 0;
402 if (token == tokv_error) return token;
403 assert(token == tokv_eof);
404 parseerrprint("unexpected end of file while looking for end of line");
408 static int skip(int allowce) {
409 /* Scans a piece of config without executing it.
410 * Returns tokv_error, or the tokt_controlend token
411 * with type allowce if one was found.
415 for (;;) { /* loop over lines */
416 cstate->reportlineno= cstate->lineno;
417 do { token= yylex(); } while (token == tokv_lwsp);
418 if (token & tokt_exception) {
420 } else if (token & tokt_controlend) {
421 if (allowce == lr_controlend) return token;
422 return unexpected(token,-1,"control structure end of a different kind");
423 } else if (token & tokt_controlstart) {
425 while (r & tokt_controlstart) {
426 r= skiptoeol(); if (r) return r;
427 cstate->reportlineno= cstate->lineno;
428 r= skip(token); if (r & tokt_exception) return r;
430 } else if (!(token & tokt_directive) && !(token & tokt_condop)) {
431 parseerrprint("not a directive (or conditional operator) "
432 "while looking for control structure end");
435 r= skiptoeol(); if (r) return r;
439 static int parser(int allowce) {
441 * an exception (error, eof or quit)
442 * then rest of `file' is uninteresting
444 * token if allowce was !0 and equal to token's controlend
445 * then rest of `file' not scanned yet
449 for (;;) { /* loop over lines */
450 cstate->reportlineno= cstate->lineno;
451 do { token= yylex(); } while (token == tokv_lwsp);
452 if (token & tokt_exception) {
454 } else if (token & tokt_controlend) {
455 if (lr_controlend == allowce) return token;
456 return unexpected(token,-1,"directive (not this kind of control structure end)");
457 } else if (token & tokt_directive) {
458 r= (lr_dir)(token); if (r) { assert(r & tokt_exception); return r; }
459 } else if (token == tokv_newline) {
460 /* ignore blank links (and comment-only lines) */
462 return unexpected(token,-1,"directive");
467 int parse_string(const char *string, const char *descrip, int isinternal) {
468 /* Returns the same things as parser, except that tokv_eof
469 * is turned into 0. */
470 struct parser_state usestate;
471 YY_BUFFER_STATE ybuf;
474 ybuf= yy_scan_string(string);
475 if (!ybuf) syscallerror("unable to create flex buffer for internal string");
476 parser_push(&usestate,descrip,ybuf,isinternal);
481 if (r == tokv_eof) r= 0;
485 static int parse_file(const char *string, int *didexist) {
486 /* Returns the same things as parser, except that tokv_eof
487 * is turned into 0. */
488 static int fileparselevel= 0;
490 struct parser_state usestate;
491 YY_BUFFER_STATE ybuf;
495 if (fileparselevel >= MAX_INCLUDE_NEST) {
496 parseerrprint("too many nested levels of included files");
499 file= fopen(string,"r"); if (!file) {
500 if (errno == ENOENT) {
501 if (didexist) *didexist= 0;
504 parseerrprint("unable to open config file `%s': %s",string,strerror(errno));
507 if (didexist) *didexist= 1;
509 ybuf= yy_create_buffer(file,YY_BUF_SIZE);
510 if (!ybuf) syscallerror("unable to create flex buffer for file");
511 parser_push(&usestate,string,ybuf,0);
516 parseerrprint("error reading configuration file `%s'",string);
523 if (r == tokv_eof) r= 0;
527 /* Parameter-based conditional functions (parmcondition's) */
529 int pcf_glob(int ctoken, char **pv, int *rtrue) {
530 int token, actrue, r;
534 r= pa_mwsp(); if (r) return r;
537 if ((token & tokm_repres) == tokr_nonstring)
538 return unexpected(token,-1,"glob pattern");
539 for (pp= pv; !actrue && *pp; pp++) if (!fnmatch(yytext,*pp,0)) actrue= 1;
541 if (token == tokv_newline) break;
542 if (unexpected(token,tokv_lwsp,"newline after or whitespace between glob patterns"))
549 int pcf_range(int ctoken, char **pv, int *rtrue) {
550 int mintoken, min, maxtoken, max, r;
554 r= pa_mwsp(); if (r) return r;
555 mintoken= pa_numberdollar(&min); if (mintoken == tokv_error) return mintoken;
556 r= pa_mwsp(); if (r) return r;
557 maxtoken= pa_numberdollar(&max); if (maxtoken == tokv_error) return maxtoken;
558 r= pa_mnl(); if (r) return r;
559 for (pp= pv; *pp; pp++) {
560 v= strtoul(*pp,&ep,10); if (*ep) continue;
561 if (mintoken != tokv_dollar && v < min) continue;
562 if (maxtoken != tokv_dollar && v > max) continue;
568 int pcf_grep(int ctoken, char **pv, int *rtrue) {
572 int r, maxlen, l, c, actrue, posstrue;
574 r= paa_1path(&cp); if (r) return r;
575 file= fopen(cp,"r"); if (!file) {
576 parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno));
580 for (pp= pv; *pp; pp++) { l= strlen(*pp); if (l > maxlen) maxlen= l; }
581 buf= xmalloc(maxlen+2); actrue= 0; c= 0;
582 while (!actrue && c!=EOF) {
583 c= getc(file); if (c==EOF) break;
584 if (isspace(c)) continue;
586 while (l>0 && c!='\n' && c!=EOF) { *p++= c; l--; c= getc(file); }
587 if (c=='\n' || c==EOF || isspace(c)) {
588 while (p>buf && isspace(p[-1])) --p;
590 for (pp= pv; *pp; pp++) if (!strcmp(*pp,buf)) { posstrue= 1; break; }
594 if (c!='\n' && c!=EOF) {
597 if (c==EOF || c=='\n') break;
598 if (!isspace(c)) posstrue= 0;
601 if (posstrue) actrue= 1;
604 parseerrprint("error while reading `%s' for grep: %s",cp,strerror(errno));
605 fclose(file); free(buf); return tokv_error;
607 assert(actrue || feof(file));
608 fclose(file); free(buf); *rtrue= actrue; return 0;
611 /* Parameter functions and associated `common code' functions */
613 int pf_service(int ptoken, char ***rvalues) {
614 parm_1string(rvalues,service); return 0;
617 static char *parm_ulong(unsigned long ul) {
621 l= CHAR_BIT*sizeof(unsigned long)/3+4;
623 snyprintf(p,l,"%lu",ul);
627 static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) {
629 a= xmalloc(sizeof(char*)*3);
630 a[0]= xstrsave(name);
631 a[1]= parm_ulong(id);
633 *rvalues= a; return 0;
636 int pf_callinguser(int ptoken, char ***rvalues) {
637 return parm_usernameuid(rvalues,logname,request_mbuf.callinguid);
640 int pf_serviceuser(int ptoken, char ***rvalues) {
641 return parm_usernameuid(rvalues,serviceuser,serviceuser_uid);
644 static char *parm_gidname(gid_t id) {
645 static char ebuf[200];
651 sprintf(ebuf,"look up group with id %lu",(unsigned long)id);
654 return xstrsave(ge->gr_name);
657 static int parm_gids(char ***rvalues, int size, gid_t *list) {
661 if (size >= 2 && list[0] == list[1]) { size--; list++; }
662 a= xmalloc(sizeof(char*)*(size+1)*2);
663 for (i=0; i<size; i++) {
664 a[i]= parm_gidname(list[i]);
665 a[size+i]= parm_ulong(list[i]);
668 *rvalues= a; return 0;
671 int pf_callinggroup(int ptoken, char ***rvalues) {
672 return parm_gids(rvalues,request_mbuf.ngids,calling_gids);
675 int pf_servicegroup(int ptoken, char ***rvalues) {
676 return parm_gids(rvalues,service_ngids,service_gids);
679 int pf_callingusershell(int ptoken, char ***rvalues) {
680 parm_1string(rvalues,callinguser_shell); return 0;
683 int pf_serviceusershell(int ptoken, char ***rvalues) {
684 parm_1string(rvalues,serviceuser_shell); return 0;
687 /* Directive functions and associated `common code' functions */
689 static int paa_pathargs(const char **rv, char ***newargs_r) {
690 /* Repeated calls do _not_ overwrite newargs_r; caller must free.
691 * Repeated calls _do_ overwrite string returned in rv.
697 newargs= xmalloc(sizeof(char*)*(size+1));
698 r= pa_string(rv); if (r == tokv_error) return r;
700 r= yylex(); if (r == tokv_error) goto error;
701 if (r==tokv_newline) break;
702 if (unexpected(r,tokv_lwsp,"newline after or whitespace between arguments")) {
706 r= yylex(); if (r==tokv_error) goto error;
707 if ((r & tokm_repres) == tokr_nonstring) {
708 r= unexpected(r,-1,"string for command argument");
713 newargs= xrealloc(newargs,sizeof(char*)*(size+1));
715 newargs[used++]= xstrsave(yytext);
723 freecharparray(newargs);
727 static void execreset(void) {
730 free(execpath); execpath= 0;
731 freecharparray(execargs); execargs= 0;
734 int df_reject(int dtoken) {
737 r= pa_mnl(); if (r) return r;
739 execute= tokv_word_reject;
743 int df_executefrompath(int dtoken) {
746 r= pa_mnl(); if (r) return r;
748 execute= tokv_word_executefrompath;
752 int df_execute(int dtoken) {
757 r= paa_pathargs(&rv,&newargs); if (r) return r;
759 execute= tokv_word_execute;
761 execpath= xstrsave(rv);
765 int df_executefromdirectory(int dtoken) {
766 const char *p, *q, *rv;
771 r= paa_pathargs(&rv,&newargs); if (r) return r;
772 p= strrchr(service,'/'); if (p) p++; else p= service;
773 if (!*p || !isalnum(*p)) {
774 parseerrprint("execute-from-directory requires initial char of service "
775 "portion to be alphanumeric (service portion was `%s')",
777 freecharparray(newargs);
780 for (q=p+1; *q; q++) {
781 if (!isalnum(*q) && *q != '-') {
782 parseerrprint("execute-from-directory requires service portion to "
783 "contain only alphanumerics and hyphens (was `%s')",
785 freecharparray(newargs);
789 l= strlen(rv)+1+strlen(p)+1;
791 snyprintf(fn,l,"%s/%s",rv,p);
792 if (stat(fn,&stab)) {
793 if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; }
794 parseerrprint("failed to stat `%s' for execute-from-directory: %s",
796 free(fn); freecharparray(newargs); return tokv_error;
798 if (!S_ISREG(stab.st_mode)) {
799 parseerrprint("file `%s' in execute-from-directory is not an ordinary file"
800 " or link to one (mode=0%o)",fn,stab.st_mode);
801 free(fn); freecharparray(newargs); return tokv_error;
804 execute= tokv_word_executefromdirectory;
810 static int bispa_none(char ***rnewargs) {
814 static int bispa_parameter(char ***rnewargs) {
816 char **parmvalues, *name, **newargs;
818 r= pa_mwsp(); if (r) return r;
819 r= pa_parameter(&parmvalues,&name); if (r) return r;
820 for (i=0; parmvalues[i]; i++);
821 newargs= xmalloc(sizeof(char*)*(i+2));
823 memcpy(newargs+1,parmvalues,sizeof(char*)*(i+1));
825 r= pa_mnl(); if (r) { free(newargs); return r; }
830 int df_executebuiltin(int dtoken) {
832 builtinserviceexec_fnt *bisexec;
833 char *newpath, **newargs;
835 r= pa_mwsp(); if (r) return r;
836 r= yylex(); if (r == tokv_error) return r;
837 if (!(r & tokt_builtinservice)) return unexpected(r,-1,"builtin service name");
839 newpath= xstrsave(yytext);
841 r= lr_bispa(&newargs); if (r) { free(newpath); return r; }
844 execute= tokv_word_executebuiltin;
845 execbuiltin= bisexec;
851 int df_errorstostderr(int dtoken) {
854 r= pa_mnl(); if (r) return r;
855 closeerrorfile(); eh.handling= dtoken; return 0;
858 int df_errorstosyslog(int dtoken) {
859 int token, level, facility;
861 facility= DEFUSERLOGFACILITY;
862 level= DEFUSERLOGLEVEL;
864 if (token == tokv_lwsp) {
865 token= yylex(); if (token == tokv_error) return token;
866 if (!(token & tokt_logfacility))
867 return unexpected(token,-1,"syslog facility (or end of line)");
868 facility= lr_logfacility;
871 if (token == tokv_lwsp) {
872 token= yylex(); if (token == tokv_error) return token;
873 if (!(token & tokt_loglevel))
874 return unexpected(token,-1,"syslog level (or end of line) after facility");
878 if (unexpected(token,tokv_newline,"end of line somewhere after errors-to-syslog"))
880 closeerrorfile(); eh.handling= tokv_word_errorstosyslog;
881 eh.logfacility= facility; eh.loglevel= level; return 0;
884 int df_errorstofile(int dtoken) {
889 r= paa_1path(&cp); if (r) return r;
892 parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno));
895 if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) {
896 parseerrprint("unable to set line buffering on errors file: %s",strerror(errno));
897 fclose(file); return tokv_error;
899 closeerrorfile(); eh.handling= tokv_word_errorstofile;
900 eh.file= file; eh.filename= xstrsave(cp); return 0;
903 int dfg_setflag(int dtoken) {
906 r= pa_mnl(); if (r) return r;
907 *lr_flag= lr_flagval; return 0;
910 int df_reset(int dtoken) {
913 r= pa_mnl(); if (r) return r;
914 r= parse_string(RESET_CONFIGURATION,"<builtin reset configuration>",1);
915 assert(!r); return 0;
918 int df_cd(int dtoken) {
922 r= paa_1path(&cp); if (r) return r;
923 if (!chdir(cp)) return 0;
924 parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno));
928 int df_userrcfile(int dtoken) {
932 r= paa_1path(&cp); if (r) return r;
933 free(userrcfile); userrcfile= xstrsave(cp);
937 int dfi_includeuserrcfile(int dtoken) {
940 r= pa_mnl(); if (r) return r;
941 if (userrcfile) return parse_file(userrcfile,0);
942 parseerrprint("_include-user-rcfile (for-internal-use directive) "
943 "found but user-rcfile not set"); return tokv_error;
946 int dfi_includeclientconfig(int dtoken) {
949 r= pa_mnl(); if (r) return r;
951 parseerrprint("_include-client-config (for-internal-use directive) "
952 "found but configuration not overridden");
955 return parse_string(overridedata,"<configuration override data>",0);
958 int df_include(int dtoken) {
962 r= paa_1path(&cp); if (r) return r;
963 r= parse_file(cp,&found); if (r) return r;
964 if (found || dtoken == tokv_word_includeifexist) return 0;
965 parseerrprint(dtoken == tokv_word_includesysconfig ?
966 "system configuration file `%s' does not exist" :
967 "included file `%s' does not exist",
972 static int paa_message(const char **message_r) {
973 static char *buildbuf;
974 static int buildbuflen;
978 r= pa_mwsp(); if (r) return r;
980 makeroom(&buildbuf,&buildbuflen,10);
983 r= yylex(); if (r == tokv_error) return r;
985 parseerrprint("unexpected end of file in message text"); return tokv_error;
987 if (r == tokv_newline) break;
989 makeroom(&buildbuf,&buildbuflen,tl);
990 strcat(buildbuf,yytext);
992 *message_r= buildbuf;
996 int df_message(int dtoken) {
1000 r= paa_message(&mp); if (r) return r;
1001 parseerrprint("`message' directive: %s",mp);
1005 int df_error(int dtoken) {
1009 r= paa_message(&mp); if (r) return r;
1010 parseerrprint("`error' directive: %s",mp); return tokv_error;
1013 int df_includedirectory(int dtoken) {
1014 static char *buildbuf=0;
1015 static int buildbuflen=0;
1017 int r, cpl, tel, c, found;
1022 r= paa_1path(&cp); if (r) return r;
1025 parseerrprint("unable to open directory `%s': %s",cp,strerror(errno));
1029 while ((de= readdir(d))) {
1030 tel= strlen(de->d_name);
1033 if (!isalnum(*p)) continue;
1034 while ((c= *++p)) if (!(isalnum(c) || c=='-')) break;
1036 makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1);
1037 snyprintf(buildbuf,buildbuflen,"%s/%s",cp,de->d_name);
1038 r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; }
1040 parseerrprint("unable to open file `%s' in included directory `%s': %s",
1041 de->d_name,cp,strerror(errno));
1042 closedir(d); return tokv_error;
1046 parseerrprint("error closing directory `%s': %s",cp,strerror(errno));
1052 int df_includelookup(int dtoken) {
1053 static char *buildbuf=0;
1056 char **parmvalues, **pp, *p, *q;
1059 int r, done, thisdone, cpl, c;
1061 r= pa_mwsp(); if (r) return r;
1062 r= pa_parameter(&parmvalues,0); if (r) return r;
1063 r= paa_1path(&cp); if (r) { freecharparray(parmvalues); return r; }
1064 if (stat(cp,&stab)) {
1065 parseerrprint("unable to access directory `%s': %s",cp,strerror(errno));
1066 freecharparray(parmvalues); return tokv_error;
1068 if (!S_ISDIR(stab.st_mode)) {
1069 parseerrprint("object `%s' is not a directory or link to one",cp);
1070 freecharparray(parmvalues); return tokv_error;
1074 if (!parmvalues[0]) {
1075 makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP));
1076 snyprintf(buildbuf,buildbuflen,"%s/" NONEINCLUDELOOKUP,cp);
1077 r= parse_file(buildbuf,&thisdone);
1078 if (r) { freecharparray(parmvalues); return r; }
1079 if (thisdone) done= 1;
1082 *pp && (!done || dtoken == tokv_word_includelookupall);
1084 makeroom(&buildbuf,&buildbuflen,
1085 cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1);
1086 strcpy(buildbuf,cp);
1087 p= *pp; q= buildbuf+cpl;
1089 if (*p=='.') *q++= ':';
1091 if (c=='/') { *q++= ':'; c='-'; }
1092 else if (c==':') { *q++= ':'; }
1096 r= parse_file(buildbuf,&thisdone);
1097 if (r) { freecharparray(parmvalues); return r; }
1098 if (thisdone) done= 1;
1101 freecharparray(parmvalues);
1103 makeroom(&buildbuf,&buildbuflen,
1104 cpl+1+sizeof(DEFAULTINCLUDELOOKUP));
1105 snyprintf(buildbuf,buildbuflen,"%s/" DEFAULTINCLUDELOOKUP,cp);
1106 r= parse_file(buildbuf,0); if (r) return r;
1111 int df_catchquit(int dtoken) {
1114 r= pa_mnl(); if (r) return r;
1115 r= parser(tokv_word_catchquit);
1116 if (r & tokt_controlend) {
1117 assert(r == tokv_word_hctac);
1119 } else if (r == tokv_quit || r == tokv_error) {
1120 if (r == tokv_error) {
1121 r= parse_string(RESET_CONFIGURATION,
1122 "<builtin reset configuration (caught error)>",1);
1125 r= skip(tokv_word_catchquit);
1126 if (r & tokt_controlend) {
1127 assert(r == tokv_word_hctac);
1134 int df_if(int dtoken) {
1139 r= pa_condition(&true); if (r) return r;
1140 if (!done && true) { r= parser(tokv_word_if); done= 1; }
1141 else { r= skip(tokv_word_if); }
1142 if (!(r & tokv_word_if)) return r;
1143 } while (r == tokv_word_elif);
1144 if (r == tokv_word_else) {
1145 r= pa_mnl(); if (r) return r;
1146 cstate->reportlineno= cstate->lineno;
1147 if (done) r= skip(tokv_word_if);
1148 else r= parser(tokv_word_if);
1149 if (!(r & tokv_word_if)) return r;
1151 if (unexpected(r,tokv_word_fi,"`fi' to end `if'")) return tokv_error;
1155 int dfg_fdwant(int dtoken) {
1156 int fdmin, fdmax, r, needreadwrite, havereadwrite, fd;
1158 needreadwrite= lr_fdwant_readwrite;
1159 r= pa_mwsp(); if (r) return r;
1160 r= yylex(); if (r == tokv_error) return r;
1161 if (!(r & tokt_fdrange)) return r;
1162 fdmin= lr_min; fdmax= lr_max;
1163 r= yylex(); if (r == tokv_error) return r;
1164 if (r == tokv_newline) {
1165 if (needreadwrite > 0) {
1166 parseerrprint("read or write is required"); return tokv_error;
1169 } else if (r == tokv_lwsp) {
1170 if (needreadwrite < 0) {
1171 parseerrprint("read or write not allowed"); return tokv_error;
1173 r= yylex(); if (r == tokv_error) return r;
1174 if (!(r & tokt_readwrite))
1175 return unexpected(r,-1,"read or write (or perhaps newline)");
1177 r= pa_mnl(); if (r) return r;
1179 return unexpected(r,-1,"whitespace before read or write or newline");
1181 ensurefdarray(fdmin);
1183 if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd))
1184 parseerrprint("unspecified maximum only allowed with reject-fd and ignore-fd");
1185 fdmax= fdarrayused-1;
1186 restfdwantstate= dtoken;
1187 restfdwantrw= havereadwrite;
1189 for (fd=fdmin; fd<=fdmax; fd++) {
1190 fdarray[fd].wantstate= dtoken;
1191 fdarray[fd].wantrw= havereadwrite;
1196 int df_quit(int dtoken) {
1199 r= pa_mnl(); if (r) return r;
1203 int df_eof(int dtoken) {
1206 r= pa_mnl(); if (r) return r;
1210 int df_errorspush(int dt) {
1211 struct error_handling save;
1214 r= pa_mnl(); if (r) return r;
1219 r= parser(tokv_word_errorspush);
1224 if (r & tokt_controlend) {
1225 assert(r == tokv_word_srorre);