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.
26 int lineno, atnewline, notedreferer;
29 struct parser_state *upstate;
32 static const char *currentfile= 0;
33 static struct parser_state *parser_topstate= 0;
35 static directive_fnt *lr_dir=0;
36 static parmcondition_fnt *lr_parmcond=0;
37 static parameter_fnt *lr_parameter=0;
38 static int lr_loglevel, lr_logfacility, lr_min, lr_max, *lr_flag;
39 static int lr_flagval, lr_controlend;
40 static int lr_fdwant_readwrite;
42 static void useless(void) { (void)yyunput; (void)useless; /* to shut up GCC ! */ }
44 static void closeerrorfile(void) {
45 if (ehfile && !ehfilekeep) { fclose(ehfile); free(ehfilename); }
46 ehfile= 0; ehfilename= 0; ehfilekeep= 0;
49 static void senderrmsg(const char *errmsg, int useehandling) {
50 char suberrmsg[MAX_ERRMSG_LEN];
55 switch (useehandling) {
56 case tokv_word_errorstostderr:
57 senderrmsgstderr(errmsg);
59 case tokv_word_errorstosyslog:
60 ensurelogopen(ehlogfacility);
61 syslog(ehloglevel,"%s",errmsg);
63 case tokv_word_errorstofile:
64 if (time(&now)==-1) syscallerror("get current time");
65 lt= localtime(&now); if (!lt) syscallerror("convert current time");
66 if (fprintf(ehfile,"%d-%02d-%02d %02d:%02d:%02d: uservd: %s\n",
67 lt->tm_year+1900, lt->tm_mon+1, lt->tm_mday,
68 lt->tm_hour, lt->tm_min, lt->tm_sec,
71 closeerrorfile(); ehandling= tokv_word_errorstofile;
72 snyprintf(suberrmsg,sizeof(suberrmsg),
73 "error writing to error log file `%.*s': %s;"
74 " reverting to errors-to-stderr",
75 (int)(sizeof(suberrmsg)>>1),ehfilename,strerror(e));
76 senderrmsg(suberrmsg,ehandling);
77 senderrmsg(errmsg,ehandling);
85 static void errwhere(int iflineno, const char *filename, int *ifnotedreferer,
86 struct parser_state *upstate, char *bufput, int bufputlen) {
87 static const char suffix[]= "references ...";
88 char errmsg[MAX_ERRMSG_LEN];
91 filename= "<initialisation>";
92 } else if (!*ifnotedreferer && upstate->upstate && upstate->upstate->upstate) {
93 errwhere(upstate->lineno-upstate->atnewline,upstate->filename,
94 &upstate->notedreferer,upstate->upstate,
95 errmsg,sizeof(errmsg)-sizeof(suffix));
96 strcat(errmsg,suffix);
97 senderrmsg(errmsg,ehandling);
100 snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,filename,iflineno);
103 void parseerrprint(const char *fmt, ...) {
105 char errmsg[MAX_ERRMSG_LEN];
108 errwhere(lineno-atnewline,currentfile,¬edreferer,parser_topstate,
109 errmsg,sizeof(errmsg)>>1);
110 vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al);
111 senderrmsg(errmsg,ehandling);
115 static void freecharparray(char **array) {
119 for (pp=array; *pp; pp++) free(*pp);
123 static int dequote(char *inplace) {
124 char *p, *q, buf[4], *bep;
129 while (*p && *p != '"') {
130 if (*p != '\\') { *q++= *p++; continue; }
132 case 'n': *q++= '\n'; continue;
133 case 'r': *q++= '\r'; continue;
134 case 't': *q++= '\t'; continue;
136 assert(buf[0]= *++p); assert(buf[1]= *++p); buf[2]= 0;
137 v= strtoul(buf,&bep,16); assert(bep == buf+2);
138 assert(!(v & ~0xff)); *q++= v; p++; continue;
141 parseerrprint("unknown \\<letter> sequence \\%c in quoted string",*p);
143 } else if (isdigit(*p)) {
144 assert(buf[0]= *++p); assert(buf[1]= *++p); assert(buf[2]= *++p);
145 buf[3]= 0; v= strtoul(buf,&bep,8);
146 if (bep != buf+3 || (v & ~0xff)); {
147 parseerrprint("invalid \\<octal> sequence \\%s in quoted string",buf);
150 *q++= v; p++; continue;
151 } else if (ispunct(*p)) {
152 *q++= *p++; continue;
154 while (*p==' ' || *p=='\t') p++;
159 assert(*p); assert(!*++p); return tokv_quotedstring;
162 const char *printtoken(int token) {
163 static char buf[250];
169 if ((token & tokm_repres) == tokr_word) {
170 assert(strlen(yytext)+50<sizeof(buf));
171 sprintf(buf,"word `%s'",yytext);
173 } else if (token & tokt_number) {
174 sprintf(buf,"number %d",lr_min); return buf;
175 } else if (token & tokt_fdrange) {
176 sprintf(buf,"fdrange %d..%d",lr_min,lr_max); return buf;
177 } else if ((token & tokm_repres) == tokr_punct) {
178 assert(strlen(yytext)+50<sizeof(buf));
179 sprintf(buf,"operator `%s'",yytext);
181 } else if (token & tokt_string) {
183 case tokv_barestring: strcpy(buf,"unquoted string (bare word)"); break;
184 case tokv_quotedstring: strcpy(buf,"quoted string"); break;
188 i= sizeof(buf)-50; p= yytext; q= buf+strlen(buf);
189 while (i-- >0 && (c= *p++)) {
190 if (isspace(c)) c= ' ';
191 else if (!isprint(c) || iscntrl(c)) c= '?';
198 case tokv_lwsp: return "linear whitespace";
199 case tokv_newline: return "newline (or comment followed by newline)";
200 case tokv_eof: return "end of input file";
201 case tokv_error: return "syntax error token";
203 sprintf(buf,"token#0%o",token); return buf;
208 static const char *string2path(const char *in) {
214 if (strncmp(in,USERDIRPREFIX,sizeof(USERDIRPREFIX)-1)) return in;
215 l= strlen(serviceuser_dir)+strlen(in)+1-(sizeof(USERDIRPREFIX)-1)+
217 if (l>pl) { p= realloc(p,l); pl=l; }
218 strcpy(p,serviceuser_dir); strcat(p,DIRSEP);
219 strcat(p,in+(sizeof(USERDIRPREFIX)-1));
223 static void parser_push(struct parser_state *saveinto,
224 const char *newfile) {
225 saveinto->lineno= lineno;
226 saveinto->atnewline= atnewline;
227 saveinto->filename= currentfile;
228 saveinto->notedreferer= notedreferer;
229 saveinto->ybuf= YY_CURRENT_BUFFER;
230 saveinto->upstate= parser_topstate;
234 currentfile= newfile;
235 parser_topstate= saveinto;
238 static void parser_pop(void) {
239 YY_BUFFER_STATE ybuf;
240 ybuf= YY_CURRENT_BUFFER;
242 lineno= parser_topstate->lineno;
243 atnewline= parser_topstate->atnewline;
244 currentfile= parser_topstate->filename;
245 notedreferer= parser_topstate->notedreferer;
246 if (parser_topstate->ybuf) yy_switch_to_buffer(parser_topstate->ybuf);
247 parser_topstate= parser_topstate->upstate;
249 yy_delete_buffer(ybuf);
252 /* parser component functions pa_ return tokv_error or 0,
253 * having scanned exactly what they were expecting
254 * complete argument list parsers paa_ return tokv_error or 0,
255 * having scanned up to and including the newline
258 static int unexpected(int found, int wanted, const char *wantedstr) {
259 /* pass wanted==-1 if you know it's not what you wanted;
260 * otherwise this function will check it for you.
262 if (found == wanted) return 0;
263 if (found != tokv_error) {
264 parseerrprint("found %s, expected %s",printtoken(found),wantedstr);
269 static int pa_mnl(void) {
270 return unexpected(yylex(),tokv_newline,"newline");
272 static int pa_mwsp(void) {
273 return unexpected(yylex(),tokv_lwsp,"linear whitespace");
276 static void parm_1string(char ***rvalues, const char *tocopy) {
278 a= xmalloc(sizeof(char*)*2);
279 a[0]= xstrdup(tocopy);
284 static int pa_string(const char **rv) {
285 /* Value returned in *rv is overwritten by repeated calls */
291 r= pa_mwsp(); if (r) return r;
292 r= yylex(); if (r == tokv_error) return r;
293 if ((r & tokm_repres) == tokr_nonstring) return unexpected(r,-1,"string");
296 strcpy(p,yytext); *rv= p;
301 static int paa_1string(const char **rv) {
302 /* Value returned in *rv is overwritten by repeated calls */
305 r= pa_string(rv); if (r) return r;
309 static int paa_1path(const char **rv) {
310 /* Value returned in *rv is overwritten by repeated calls */
314 r= paa_1string(&cp); if (r) return r;
315 *rv= string2path(cp); return 0;
318 static int pa_parameter(char ***rvalues) {
319 /* Scans a single parameter token and calls the appropriate parameter
320 * function, returning tokv_error or 0 just like the parameter function.
324 token= yylex(); if (token == tokv_error) return token;
325 if ((token & tokm_repres) != tokr_nonstring &&
326 !memcmp(yytext,"u-",2) && strlen(yytext)>=3) {
328 i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i][0]);
330 if (i>=request_mbuf.nvars) {
331 *rvalues= xmalloc(sizeof(char*));
334 parm_1string(rvalues,defvararray[i][1]);
337 if (!(token & tokt_parameter)) return unexpected(token,-1,"parameter name");
338 r= (lr_parameter)(token,rvalues);
341 debug_dumpparameter(yytext,*rvalues);
345 static int pa_condition(int *rtrue) {
346 /* Scans up to and including the newline following the condition;
347 * may scan more than one line if the condition is a multi-line
348 * one. Returns 0 (setting *rtrue) or tokv_error. Expects to
349 * scan the whitespace before its condition.
351 int r, token, andor, ctrue, actrue;
354 r= pa_mwsp(); if (r) return r;
356 if (token == tokv_error) {
358 } else if (token == tokv_not) {
359 r= pa_condition(&ctrue); if (r) return r;
360 *rtrue= !ctrue; return 0;
361 } else if (token == tokv_openparen) {
362 andor= 0; actrue= -1;
364 r= pa_condition(&ctrue); if (r) return r;
366 case 0: assert(actrue==-1); actrue= ctrue; break;
367 case tokv_and: assert(actrue>=0); actrue= actrue && ctrue; break;
368 case tokv_or: assert(actrue>=0); actrue= actrue || ctrue; break;
371 do { token= yylex(); } while (token == tokv_lwsp);
372 if (token == tokv_error) return token;
373 if (token == tokv_closeparen) break;
375 r= unexpected(token,andor,"same conjunction as before"); if (r) return r;
377 if (token != tokv_and && token != tokv_or)
378 return unexpected(token,-1,"first conjunction inside connective");
382 r= pa_mnl(); if (r) return r;
383 *rtrue= actrue; return 0;
384 } else if (token & tokt_parmcondition) {
385 r= pa_mwsp(); if (r) return r;
386 r= pa_parameter(&parmvalues); if (r) return r;
387 r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues);
390 return unexpected(token,-1,"condition");
393 static int pa_numberdollar(int *rv) {
394 /* Also parses the whitespace beforehand. */
397 r= pa_mwsp(); if (r) return r;
398 r= yylex(); if (r == tokv_error || r == tokv_dollar) return r;
399 if (unexpected(r,tokv_ordinal,"expected number or dollar")) return tokv_error;
400 *rv= lr_min; return r;
403 /* Main parser routines */
405 static int skiptoeol(void) {
408 do { token= yylex(); }
409 while (token != tokv_newline && !(token & tokt_exception));
410 if (token == tokv_newline) return 0;
411 if (token == tokv_error) return token;
412 assert(token == tokv_eof);
413 parseerrprint("unexpected end of file while looking for end of line");
417 static int skip(int allowce) {
418 /* Scans a piece of config without executing it.
419 * Returns tokv_error, or the tokt_controlend token
420 * with type allowce if one was found.
424 for (;;) { /* loop over lines */
425 do { token= yylex(); } while (token == tokv_lwsp);
426 if (token & tokt_exception) {
428 } else if (token & tokt_controlend) {
429 if (allowce == lr_controlend) return token;
430 return unexpected(token,-1,"control structure end of a different kind");
431 } else if (token & tokt_controlstart) {
433 while (r & tokt_controlstart) {
434 r= skiptoeol(); if (r) return r;
435 r= skip(token); if (r & tokt_exception) return r;
437 } else if (!(token & tokt_directive) && !(token & tokt_condop)) {
438 parseerrprint("not a directive (or conditional operator) "
439 "while looking for control structure end");
442 r= skiptoeol(); if (r) return r;
446 static int parser(int allowce) {
448 * an exception (error, eof or quit)
449 * then rest of `file' is uninteresting
451 * token if allowce was !0 and equal to token's controlend
452 * then rest of `file' not scanned yet
456 for (;;) { /* loop over lines */
457 do { token= yylex(); } while (token == tokv_lwsp);
458 if (token & tokt_exception) {
460 } else if (token & tokt_controlend) {
461 if (lr_controlend == allowce) return token;
462 return unexpected(token,-1,"directive (not this kind of control structure end)");
463 } else if (token & tokt_directive) {
464 r= (lr_dir)(token); if (r) { assert(r & tokt_exception); return r; }
465 } else if (token == tokv_newline) {
466 /* ignore blank links (and comment-only lines) */
468 return unexpected(token,-1,"directive");
473 int parse_string(const char *string, const char *descrip) {
474 /* Returns the same things as parser, except that tokv_eof
475 * is turned into 0. */
476 struct parser_state save;
477 YY_BUFFER_STATE ybuf;
480 parser_push(&save,descrip);
481 ybuf= yy_scan_string(string);
482 if (!ybuf) syscallerror("unable to create flex buffer for internal string");
483 yy_switch_to_buffer(ybuf);
488 if (r == tokv_eof) r= 0;
492 static int parse_file(const char *string, int *didexist) {
493 /* Returns the same things as parser, except that tokv_eof
494 * is turned into 0. */
495 static int fileparselevel= 0;
497 struct parser_state save;
498 YY_BUFFER_STATE ybuf;
502 if (fileparselevel >= MAX_INCLUDE_NEST) {
503 parseerrprint("too many nested levels of included files");
506 file= fopen(string,"r"); if (!file) {
507 if (errno == ENOENT) {
508 if (didexist) *didexist= 0;
511 parseerrprint("unable to open config file `%s': %s",string,strerror(errno));
514 if (didexist) *didexist= 1;
516 parser_push(&save,string);
517 ybuf= yy_create_buffer(file,YY_BUF_SIZE);
518 if (!ybuf) syscallerror("unable to create flex buffer for file");
519 yy_switch_to_buffer(ybuf);
524 parseerrprint("error reading configuration file `%s'",string);
531 if (r == tokv_eof) r= 0;
535 /* Parameter-based conditional functions (parmcondition's) */
537 int pcf_glob(int ctoken, char **pv, int *rtrue) {
538 int token, actrue, r;
542 r= pa_mwsp(); if (r) return r;
545 if ((token & tokm_repres) == tokr_nonstring)
546 return unexpected(token,-1,"glob pattern");
547 for (pp= pv; !actrue && *pp; pp++) if (!fnmatch(yytext,*pp,0)) actrue= 1;
549 if (token == tokv_newline) break;
550 if (unexpected(token,tokv_lwsp,"newline after or whitespace between glob patterns"))
557 int pcf_range(int ctoken, char **pv, int *rtrue) {
558 int mintoken, min, maxtoken, max, r;
562 r= pa_mwsp(); if (r) return r;
563 mintoken= pa_numberdollar(&min); if (mintoken == tokv_error) return mintoken;
564 r= pa_mwsp(); if (r) return r;
565 maxtoken= pa_numberdollar(&max); if (maxtoken == tokv_error) return maxtoken;
566 r= pa_mnl(); if (r) return r;
567 for (pp= pv; *pp; pp++) {
568 v= strtoul(*pp,&ep,10); if (*ep) continue;
569 if (mintoken != tokv_dollar && v < min) continue;
570 if (maxtoken != tokv_dollar && v > max) continue;
576 int pcf_grep(int ctoken, char **pv, int *rtrue) {
580 int r, maxlen, l, c, actrue, posstrue;
582 r= paa_1path(&cp); if (r) return r;
583 file= fopen(cp,"r"); if (!file) {
584 parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno));
588 for (pp= pv; *pp; pp++) { l= strlen(*pp); if (l > maxlen) maxlen= l; }
589 buf= xmalloc(maxlen+2); actrue= 0; c= 0;
590 while (!actrue && c!=EOF) {
591 c= getc(file); if (c==EOF) break;
592 if (isspace(c)) continue;
594 while (l>0 && c!='\n' && c!=EOF) { *p++= c; l--; c= getc(file); }
595 if (c=='\n' || c==EOF || isspace(c)) {
596 while (p>buf && isspace(p[-1])) --p;
598 for (pp= pv; *pp; pp++) if (!strcmp(*pp,buf)) { posstrue= 1; break; }
602 if (c!='\n' && c!=EOF) {
605 if (c==EOF || c=='\n') break;
606 if (!isspace(c)) posstrue= 0;
609 if (posstrue) actrue= 1;
612 parseerrprint("error while reading `%s' for grep: %s",cp,strerror(errno));
613 fclose(file); free(buf); return tokv_error;
615 assert(actrue || feof(file));
616 fclose(file); free(buf); *rtrue= actrue; return 0;
619 /* Parameter functions and associated `common code' functions */
621 int pf_service(int ptoken, char ***rvalues) {
622 parm_1string(rvalues,service); return 0;
625 static char *parm_ulong(unsigned long ul) {
629 l= CHAR_BIT*sizeof(unsigned long)/3+4;
631 snyprintf(p,l,"%lu",ul);
635 static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) {
637 a= xmalloc(sizeof(char*)*3);
639 a[1]= parm_ulong(id);
641 *rvalues= a; return 0;
644 int pf_callinguser(int ptoken, char ***rvalues) {
645 return parm_usernameuid(rvalues,logname,request_mbuf.callinguid);
648 int pf_serviceuser(int ptoken, char ***rvalues) {
649 return parm_usernameuid(rvalues,serviceuser,serviceuser_uid);
652 static char *parm_gidname(gid_t id) {
653 static char ebuf[200];
659 sprintf(ebuf,"look up group with id %lu",(unsigned long)id);
662 return xstrdup(ge->gr_name);
665 static int parm_gids(char ***rvalues, int size, gid_t *list) {
669 if (size >= 2 && list[0] == list[1]) { size--; list++; }
670 a= xmalloc(sizeof(char*)*(size+1)*2);
671 for (i=0; i<size; i++) {
672 a[i]= parm_gidname(list[i]);
673 a[size+i]= parm_ulong(list[i]);
676 *rvalues= a; return 0;
679 int pf_callinggroup(int ptoken, char ***rvalues) {
680 return parm_gids(rvalues,request_mbuf.ngids,gidarray);
683 int pf_servicegroup(int ptoken, char ***rvalues) {
688 size= getgroups(0,0); if (size == -1) syscallerror("getgroups(0,0)");
689 list= xmalloc(sizeof(gid_t)*(size+1));
690 if (getgroups(size,list+1) != size) syscallerror("getgroups(size,list)");
691 list[0]= serviceuser_gid;
693 return parm_gids(rvalues,size,list);
696 int pf_callingusershell(int ptoken, char ***rvalues) {
699 pw= getpwnam(logname); if (!pw) syscallerror("looking up calling user");
700 parm_1string(rvalues,pw->pw_shell); return 0;
703 int pf_serviceusershell(int ptoken, char ***rvalues) {
704 parm_1string(rvalues,serviceuser_shell); return 0;
707 /* Directive functions and associated `common code' functions */
709 int df_reject(int dtoken) {
712 r= pa_mnl(); if (r) return r;
713 execute= tokv_word_reject;
714 free(execpath); execpath= 0;
715 freecharparray(execargs); execargs= 0;
719 int df_executefrompath(int dtoken) {
722 r= pa_mnl(); if (r) return r;
723 execute= tokv_word_executefrompath;
724 free(execpath); execpath= 0;
725 freecharparray(execargs); execargs= 0;
729 static int paa_pathargs(const char **rv, char ***newargs_r) {
730 /* Repeated calls do _not_ overwrite newargs_r; caller must free.
731 * Repeated calls _do_ overwrite string returned in rv.
737 newargs= xmalloc(sizeof(char*)*(size+1));
738 r= pa_string(rv); if (r == tokv_error) return r;
740 r= yylex(); if (r == tokv_error) goto error;
741 if (r==tokv_newline) break;
742 if (unexpected(r,tokv_lwsp,"newline after or whitespace between arguments")) {
746 r= yylex(); if (r==tokv_error) goto error;
747 if ((r & tokm_repres) == tokr_nonstring) {
748 r= unexpected(r,-1,"string for command argument");
753 newargs= xrealloc(newargs,sizeof(char*)*(size+1));
755 newargs[used++]= xmstrsave(yytext);
763 freecharparray(newargs);
767 int df_execute(int dtoken) {
772 r= paa_pathargs(&rv,&newargs); if (r) return r;
773 execute= tokv_word_execute;
774 freecharparray(execargs); execargs= newargs;
775 free(execpath); execpath= xmstrsave(rv);
779 int df_executefromdirectory(int dtoken) {
780 const char *p, *q, *rv;
785 r= paa_pathargs(&rv,&newargs); if (r) return r;
786 p= strrchr(service,'/'); if (p) p++; else p= service;
787 if (!*p || !isalnum(*p)) {
788 parseerrprint("execute-from-directory requires initial char of service "
789 "portion to be alphanumeric (service portion was `%s')",
791 freecharparray(newargs);
794 for (q=p+1; *q; q++) {
795 if (!isalnum(*q) && *q != '-') {
796 parseerrprint("execute-from-directory requires service portion to "
797 "contain only alphanumerics and hyphens (was `%s')",
799 freecharparray(newargs);
803 l= strlen(rv)+sizeof(DIRSEP)+strlen(p);
804 fn= xmalloc(l); strcpy(fn,rv); strcat(fn,DIRSEP); strcat(fn,p);
805 if (stat(fn,&stab)) {
806 if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; }
807 parseerrprint("failed to stat `%s' for execute-from-directory: %s",
809 free(fn); freecharparray(newargs); return tokv_error;
811 if (!S_ISREG(stab.st_mode)) {
812 parseerrprint("file `%s' in execute-from-directory is not an ordinary file"
813 " or link to one (mode=0%o)",fn,stab.st_mode);
814 free(fn); freecharparray(newargs); return tokv_error;
816 execute= tokv_word_executefromdirectory;
817 freecharparray(execargs); execargs= newargs;
818 free(execpath); execpath= fn;
822 int df_errorstostderr(int dtoken) {
825 r= pa_mnl(); if (r) return r;
826 closeerrorfile(); ehandling= dtoken; return 0;
829 int df_errorstosyslog(int dtoken) {
830 int token, level, facility;
832 facility= DEFUSERLOGFACILITY;
833 level= DEFUSERLOGLEVEL;
835 if (token == tokv_lwsp) {
836 token= yylex(); if (token == tokv_error) return token;
837 if (!(token & tokt_logfacility))
838 return unexpected(token,-1,"syslog facility (or end of line)");
839 facility= lr_logfacility;
842 if (token == tokv_lwsp) {
843 token= yylex(); if (token == tokv_error) return token;
844 if (!(token & tokt_loglevel))
845 return unexpected(token,-1,"syslog level (or end of line) after facility");
849 if (unexpected(token,tokv_newline,"end of line somewhere after errors-to-syslog"))
851 closeerrorfile(); ehandling= tokv_word_errorstosyslog;
852 ehlogfacility= facility; ehloglevel= level; return 0;
855 int df_errorstofile(int dtoken) {
860 r= paa_1path(&cp); if (r) return r;
863 parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno));
866 if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) {
867 parseerrprint("unable to set line buffering on errors file: %s",strerror(errno));
868 fclose(file); return tokv_error;
870 closeerrorfile(); ehandling= tokv_word_errorstofile;
871 ehfile= file; ehfilename= xmstrsave(cp); ehfilekeep= 0; return 0;
874 int dfg_setflag(int dtoken) {
877 r= pa_mnl(); if (r) return r;
878 *lr_flag= lr_flagval; return 0;
881 int df_reset(int dtoken) {
884 r= pa_mnl(); if (r) return r;
885 r= parse_string(RESET_CONFIGURATION,"<builtin reset configuration>");
886 assert(!r); return 0;
889 int df_cd(int dtoken) {
893 r= paa_1path(&cp); if (r) return r;
894 if (!chdir(cp)) return 0;
895 parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno));
899 int df_userrcfile(int dtoken) {
903 r= paa_1path(&cp); if (r) return r;
904 free(userrcfile); userrcfile= xstrdup(cp);
908 int dfi_includeuserrcfile(int dtoken) {
911 r= pa_mnl(); if (r) return r;
912 if (userrcfile) return parse_file(userrcfile,0);
913 parseerrprint("_include-user-rcfile (for-internal-use directive) "
914 "found but user-rcfile not set"); return tokv_error;
917 int dfi_includeclientconfig(int dtoken) {
920 r= pa_mnl(); if (r) return r;
922 parseerrprint("_include-client-config (for-internal-use directive) "
923 "found but configuration not overridden");
926 return parse_string(overridedata,"<configuration override data>");
929 int df_include(int dtoken) {
933 r= paa_1path(&cp); if (r) return r;
934 r= parse_file(cp,&found); if (r) return r;
935 if (found || dtoken == tokv_word_includeifexist) return 0;
936 parseerrprint(dtoken == tokv_word_includesysconfig ?
937 "system configuration file `%s' does not exist" :
938 "included file `%s' does not exist",
943 static int paa_message(const char **message_r) {
944 static char *buildbuf;
945 static int buildbuflen;
949 r= pa_mwsp(); if (r) return r;
951 makeroom(&buildbuf,&buildbuflen,10);
954 r= yylex(); if (r == tokv_error) return r;
956 parseerrprint("unexpected end of file in message text"); return tokv_error;
958 if (r == tokv_newline) break;
960 makeroom(&buildbuf,&buildbuflen,tl);
961 strcat(buildbuf,yytext);
963 *message_r= buildbuf;
967 int df_message(int dtoken) {
971 r= paa_message(&mp); if (r) return r;
972 parseerrprint("`message' directive: %s",mp);
976 int df_error(int dtoken) {
980 r= paa_message(&mp); if (r) return r;
981 parseerrprint("`error' directive: %s",mp); return tokv_error;
984 int df_includedirectory(int dtoken) {
985 static char *buildbuf=0;
986 static int buildbuflen=0;
988 int r, cpl, tel, c, found;
993 r= paa_1path(&cp); if (r) return r;
996 parseerrprint("unable to open directory `%s': %s",cp,strerror(errno));
1000 while ((de= readdir(d))) {
1001 tel= strlen(de->d_name);
1004 if (!isalnum(*p)) continue;
1005 while ((c= *++p)) if (!(isalnum(c) || c=='-')) break;
1007 makeroom(&buildbuf,&buildbuflen,cpl+tel+sizeof(DIRSEP));
1008 strcpy(buildbuf,cp);
1009 strcat(buildbuf,DIRSEP);
1010 strcat(buildbuf,de->d_name);
1011 r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; }
1013 parseerrprint("unable to open file `%s' in included directory `%s': %s",
1014 de->d_name,cp,strerror(errno));
1015 closedir(d); return tokv_error;
1019 parseerrprint("error closing directory `%s': %s",cp,strerror(errno));
1025 int df_includelookup(int dtoken) {
1026 static char *buildbuf=0;
1029 char **parmvalues, **pp, *p, *q;
1032 int r, done, thisdone, cpl, c;
1034 r= pa_mwsp(); if (r) return r;
1035 r= pa_parameter(&parmvalues); if (r) return r;
1036 r= paa_1path(&cp); if (r) { freecharparray(parmvalues); return r; }
1037 if (stat(cp,&stab)) {
1038 parseerrprint("unable to access directory `%s': %s",cp,strerror(errno));
1039 freecharparray(parmvalues); return tokv_error;
1041 if (!S_ISDIR(stab.st_mode)) {
1042 parseerrprint("object `%s' is not a directory or link to one",cp);
1043 freecharparray(parmvalues); return tokv_error;
1047 if (!parmvalues[0]) {
1048 makeroom(&buildbuf,&buildbuflen,cpl+sizeof(DIRSEP NONEINCLUDELOOKUP));
1049 strcpy(buildbuf,cp);
1050 strcat(buildbuf,DIRSEP NONEINCLUDELOOKUP);
1051 r= parse_file(buildbuf,&thisdone);
1052 if (r) { freecharparray(parmvalues); return r; }
1053 if (thisdone) done= 1;
1056 *pp && (!done || dtoken == tokv_word_includelookupall);
1058 makeroom(&buildbuf,&buildbuflen,
1059 cpl+sizeof(DIRSEP)+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP));
1060 strcpy(buildbuf,cp);
1061 strcat(buildbuf,DIRSEP);
1062 p= *pp; q= buildbuf+cpl+sizeof(DIRSEP)-1;
1063 if (*p=='.') *q++= ':';
1065 if (c=='/') { *q++= ':'; c='-'; }
1066 else if (c==':') { *q++= ':'; }
1070 r= parse_file(buildbuf,&thisdone);
1071 if (r) { freecharparray(parmvalues); return r; }
1072 if (thisdone) done= 1;
1075 freecharparray(parmvalues);
1077 makeroom(&buildbuf,&buildbuflen,
1078 cpl+sizeof(DIRSEP)+sizeof(DEFAULTINCLUDELOOKUP));
1079 strcpy(buildbuf,cp);
1080 strcat(buildbuf,DIRSEP DEFAULTINCLUDELOOKUP);
1081 r= parse_file(buildbuf,0); if (r) return r;
1086 int df_catchquit(int dtoken) {
1089 r= pa_mnl(); if (r) return r;
1090 r= parser(tokv_word_catchquit);
1091 if (r & tokt_controlend) {
1092 assert(r == tokv_word_hctac);
1094 } else if (r == tokv_quit || r == tokv_error) {
1095 if (r == tokv_error) {
1096 r= parse_string(RESET_CONFIGURATION,
1097 "<builtin reset configuration (caught error)>");
1099 while (!atnewline) {
1100 r= yylex(); if (r == tokv_error) return r;
1103 r= skip(tokv_word_catchquit);
1104 if (r & tokt_controlend) {
1105 assert(r == tokv_word_hctac);
1112 int df_if(int dtoken) {
1117 r= pa_condition(&true); if (r) return r;
1118 if (!done && true) { r= parser(tokv_word_if); done= 1; }
1119 else { r= skip(tokv_word_if); }
1120 if (!(r & tokv_word_if)) return r;
1121 } while (r == tokv_word_elif);
1122 if (r == tokv_word_else) {
1123 r= pa_mnl(); if (r) return r;
1124 if (done) r= skip(tokv_word_if);
1125 else r= parser(tokv_word_if);
1126 if (!(r & tokv_word_if)) return r;
1128 if (unexpected(r,tokv_word_fi,"`fi' to end `if'")) return tokv_error;
1132 int dfg_fdwant(int dtoken) {
1133 int fdmin, fdmax, r, needreadwrite, havereadwrite, fd;
1135 needreadwrite= lr_fdwant_readwrite;
1136 r= pa_mwsp(); if (r) return r;
1137 r= yylex(); if (r == tokv_error) return r;
1138 if (!(r & tokt_fdrange)) return r;
1139 fdmin= lr_min; fdmax= lr_max;
1140 r= yylex(); if (r == tokv_error) return r;
1141 if (r == tokv_newline) {
1142 if (needreadwrite > 0) {
1143 parseerrprint("read or write is required"); return tokv_error;
1146 } else if (r == tokv_lwsp) {
1147 if (needreadwrite < 0) {
1148 parseerrprint("read or write not allowed"); return tokv_error;
1150 r= yylex(); if (r == tokv_error) return r;
1151 if (!(r & tokt_readwrite))
1152 return unexpected(r,-1,"read or write (or perhaps newline)");
1154 r= pa_mnl(); if (r) return r;
1156 return unexpected(r,-1,"whitespace before read or write or newline");
1158 ensurefdarray(fdmin);
1160 if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd))
1161 parseerrprint("unspecified maximum only allowed with reject-fd and ignore-fd");
1162 fdmax= fdarrayused-1;
1163 restfdwantstate= dtoken;
1164 restfdwantrw= havereadwrite;
1166 for (fd=fdmin; fd<=fdmax; fd++) {
1167 fdarray[fd].wantstate= dtoken;
1168 fdarray[fd].wantrw= havereadwrite;
1173 int df_quit(int dtoken) {
1176 r= pa_mnl(); if (r) return r;
1180 int df_eof(int dtoken) {
1183 r= pa_mnl(); if (r) return r;
1187 int df_errorspush(int dt) {
1188 int saveehandling, saveehlogfacility, saveehloglevel, saveehfilekeep;
1190 char *saveehfilename;
1193 r= pa_mnl(); if (r) return r;
1195 saveehandling= ehandling;
1196 saveehlogfacility= ehlogfacility;
1197 saveehloglevel= ehloglevel;
1199 saveehfilekeep= ehfilekeep;
1200 saveehfilename= ehfilename;
1201 if (ehandling == tokv_word_errorstofile) ehfilekeep= 1;
1203 r= parser(tokv_word_errorspush);
1205 ehandling= saveehandling;
1206 ehlogfacility= saveehlogfacility;
1207 ehloglevel= saveehloglevel;
1209 ehfilekeep= saveehfilekeep;
1210 ehfilename= saveehfilename;
1212 if (r & tokt_controlend) {
1213 assert(r == tokv_word_srorre);