chiark / gitweb /
e65ff7986598dce3e0b043d72ca0c1b3bedca8a9
[userv.git] / parser.c
1 /*
2  * userv - parser.c
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
6  * about m4 quoting &c., but we have to #include it so that the C
7  * objects from the lexer are available.
8  *
9  * userv is
10  * Copyright 1996-2017 Ian Jackson <ian@davenant.greenend.org.uk>.
11  * Copyright 2000      Ben Harris <bjh21@cam.ac.uk>
12  * Copyright 2016-2017 Peter Benie <pjb1008@cam.ac.uk>
13  *
14  * This is free software; you can redistribute it and/or modify it
15  * under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with userv; if not, see <http://www.gnu.org/licenses/>.
26  */
27
28 static int parse_file(const char *string, int *didexist);
29 static int parser(int allowce);
30 int parse_string(const char *string, const char *descrip, int isinternal);
31
32 /*
33  * Error-handling routines
34  */
35
36 static void closeerrorfile(void) {
37   if (eh.file && !eh.filekeep) {
38     if (fclose(eh.file)) {
39       eh.handling= tokv_word_errorstostderr;
40       parseerrprint("error writing to error log file `%s': %s",
41                     eh.filename,strerror(errno));
42     }
43     free(eh.filename);
44   }
45   eh.handling= 0;
46   eh.file= 0; eh.filename= 0; eh.filekeep= 0;
47 }
48
49 static void senderrmsg(const char *errmsg, int useehandling) {
50   char suberrmsg[MAX_ERRMSG_LEN];
51   int e;
52   time_t now;
53   struct tm *lt;
54
55   switch (useehandling) {
56   case tokv_word_errorstostderr:
57     senderrmsgstderr(errmsg);
58     break;
59   case tokv_word_errorstosyslog:
60     ensurelogopen(eh.logfacility);
61     syslog(eh.loglevel,"%s",errmsg);
62     break;
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(eh.file,"%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,
69                 errmsg) == EOF) {
70       e= errno;
71       closeerrorfile(); eh.handling= 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),eh.filename,strerror(e));
76       senderrmsg(suberrmsg,eh.handling);
77       senderrmsg(errmsg,eh.handling);
78     }
79     break;
80   default:
81     abort();
82   }
83 }
84
85 static void errwhere(struct parser_state *tstate, char *bufput, int bufputlen) {
86   static const char suffix[]= "references ...";
87   char errmsg[MAX_ERRMSG_LEN];
88   
89   if (!tstate) {
90     strnycpy(bufput,"<initialisation>: ",bufputlen);
91     return;
92   }
93   if (!tstate->notedreferer && tstate->upstate && !tstate->upstate->isinternal) {
94     errwhere(tstate->upstate,errmsg,sizeof(errmsg)-sizeof(suffix));
95     strcat(errmsg,suffix);
96     senderrmsg(errmsg,eh.handling);
97     tstate->notedreferer= 1;
98   }
99   snyprintf(bufput,bufputlen,"%.*s:%d: ",bufputlen-10,
100             tstate->filename,tstate->reportlineno);
101 }
102
103 int parseerrprint(const char *fmt, ...) {
104   va_list al;
105   char errmsg[MAX_ERRMSG_LEN];
106
107   va_start(al,fmt);
108   errwhere(cstate,errmsg,sizeof(errmsg)>>1);
109   vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al);
110   senderrmsg(errmsg,eh.handling);
111   va_end(al);
112
113   return tokv_error;
114 }
115
116 static int unexpected(int found, int wanted, const char *wantedstr) {
117   /* pass wanted==-1 if you know it's not what you wanted;
118    * otherwise this function will check it for you.
119    */
120   if (found == wanted) return 0;
121   if (found == tokv_error) return found;
122   return parseerrprint("found %s, expected %s",printtoken(found),wantedstr);
123 }
124
125 static int stringoverflow(const char *where) {
126   return parseerrprint("string buffer became far too large building %s",where);
127 }
128
129 /*
130  * General assistance functions
131  */
132
133 static void freecharparray(char **array) {
134   char **pp;
135
136   if (!array) return;
137   for (pp=array; *pp; pp++) free(*pp);
138   free(array);
139 }
140
141 static void countnewlines(void) {
142   char *p;
143   for (p=yytext; *p; p++)
144     if (*p == '\n')
145       cstate->lineno++;
146 }
147
148 static int dequote(char *inplace) {
149   char *p, *q, buf[4], *bep;
150   int v;
151
152   p=q=inplace;
153   assert(*p=='"');  p++;
154   while (*p && *p != '"') {
155     if (*p != '\\') { *q++= *p++; continue; }
156     switch (*++p) {
157     case 'n': *q++= '\n'; p++; continue;
158     case 'r': *q++= '\r'; p++; continue;
159     case 't': *q++= '\t'; p++; continue;
160     case 'x':
161       p++;
162       if (!((buf[0]= *p++) && (buf[1]= *p++)))
163         return parseerrprint("quoted string ends inside \\x<hex> sequence");
164       buf[2]= 0;
165       v= strtoul(buf,&bep,16);
166       if (bep != buf+2)
167         return parseerrprint("invalid \\<hex> sequence \\x%s in quoted string",buf);
168       assert(!(v & ~0xff));
169       *q++= v;
170       continue;
171     default:
172       if (ISCHAR(isalpha,*p))
173         return parseerrprint("unknown \\<letter> sequence \\%c in quoted string",*p);
174       if (ISCHAR(isdigit,*p)) {
175         if (!((buf[0]= *p++) && (buf[1]= *p++) && (buf[2]= *p++))) abort();
176         buf[3]= 0; v= strtoul(buf,&bep,8);
177         if (bep != buf+3 || (v & ~0xff))
178           return parseerrprint("invalid \\<octal> sequence \\%s in quoted string",buf);
179         *q++= v; continue;
180       } else if (ISCHAR(ispunct,*p)) {
181         *q++= *p++; continue;
182       } else {
183         while (*p==' ' || *p=='\t') p++;
184         v= *p++; assert(v=='\n');
185       }
186     }
187   }
188   assert(*p); p++; assert(!*p);
189   *q++= 0;
190   return tokv_quotedstring;
191 }
192
193 const char *printtoken(int token) {
194   /* Returns pointer to static buffer, overwritten by next call. */
195   static const char keywordfmt[]= "keyword `%s'";
196   static const char operatorfmt[]= "operator `%s'";
197   
198   static char buf[250];
199   
200   char *q;
201   const char *p;
202   int i, c, l;
203   
204   if ((token & tokm_repres) == tokr_word) {
205     assert(strlen(yytext)+sizeof(keywordfmt)<sizeof(buf));
206     snyprintf(buf,sizeof(buf),keywordfmt,yytext);
207     return buf;
208   } else if (token & tokt_number) {
209     snyprintf(buf,sizeof(buf),"number %d",lr_min); return buf;
210   } else if (token & tokt_fdrange && token & tokr_word) {
211     snyprintf(buf,sizeof(buf),"fd %s",buf); return buf;
212   } else if (token & tokt_fdrange && lr_max==-1) {
213     snyprintf(buf,sizeof(buf),"fdrange %d-",lr_min); return buf;
214   } else if (token & tokt_fdrange) {
215     snyprintf(buf,sizeof(buf),"fdrange %d-%d",lr_min,lr_max); return buf;
216   } else if ((token & tokm_repres) == tokr_punct) {
217     assert(strlen(yytext)+sizeof(operatorfmt)<sizeof(buf));
218     snyprintf(buf,sizeof(buf),operatorfmt,yytext);
219     return buf;
220   } else if (token & tokt_string) {
221     switch (token) {
222     case tokv_barestring: strcpy(buf,"unquoted string (bare word)"); break;
223     case tokv_quotedstring: strcpy(buf,"quoted string"); break;
224     default: abort();
225     }
226     strcat(buf," `");
227     l= strlen(buf); i= sizeof(buf)-l-2; p= yytext; q= buf+l;
228     while ((c= *p++)) {
229       if (i-- <= 0) { q--; strcpy(q-3,"..."); break; }
230       if (ISCHAR(isspace,c)) c= ' ';
231       else if (!ISCHAR(isprint,c) || ISCHAR(iscntrl,c)) c= '?';
232       else *q++= c;
233     }
234     strcpy(q,"'");
235     return buf;
236   } else {
237     switch (token) {
238     case tokv_lwsp:    return "linear whitespace";
239     case tokv_newline: return "newline (or comment followed by newline)";
240     case tokv_eof:     return "end of input file";
241     case tokv_error:   return "syntax error token";
242     default:
243       sprintf(buf,"token#0%o",token); return buf;
244     }
245   }
246 }
247
248 static const char *string2path(const char *in) {
249   /* Returned pointers become invalid on next call.
250    * May return 0, having printed an error message.
251    */
252   static char *p;
253   static int pl;
254   
255   if (strncmp(in,"~/",2)) return in;
256   if (makeroom(&p,&pl,strlen(serviceuser_dir)+1+strlen(in+2)+1)) {
257     stringoverflow("pathname");
258     return 0;
259   }
260   snyprintf(p,pl,"%s/%s",serviceuser_dir,in+2);
261   return p;
262 }
263
264 /*
265  * Parser component functions for parsing parameters to directives
266  *
267  * Functions pa_... parse just one parameter,
268  *  and return tokv_error or 0, having scanned exactly what they were expecting
269  * Functions paa_... parse complete parameter lists, including the leading lwsp,
270  *  and return tokv_error or 0, having scanned up to and including the newline
271  *
272  * For any function which takes `const char **rv' the
273  * value returned in *rv is overwritten by repeated calls to
274  * any of those functions (whether the same or another).
275  */
276
277 static int pa_mnl(void) {
278   return unexpected(yylex(),tokv_newline,"newline");
279 }
280 static int pa_mwsp(void) {
281   return unexpected(yylex(),tokv_lwsp,"linear whitespace");
282 }
283
284 static int pa_string(const char **rv) {
285   static char *p= 0;
286   static int pl= 0;
287
288   int r, l;
289
290   r= pa_mwsp(); if (r) return r;
291   r= yylex(); if (r == tokv_error) return r;
292   if ((r & tokm_repres) == tokr_nonstring) return unexpected(r,-1,"string");
293   l= strlen(yytext)+1;
294   if (makeroom(&p,&pl,l)) return stringoverflow("string argument");
295   strcpy(p,yytext); *rv= p;
296
297   return 0;
298 }  
299
300 static int pa_numberdollar(int *value_r) {
301   /* Also parses the whitespace beforehand. */
302   int r;
303
304   r= pa_mwsp(); if (r) return r;
305   r= yylex(); if (r == tokv_error || r == tokv_dollar) return r;
306   if (unexpected(r,tokv_ordinal,"expected number or dollar")) return tokv_error;
307   *value_r= lr_min;
308   return r;
309 }
310
311 static int paa_1string(const char **rv) {
312   int r;
313
314   r= pa_string(rv); if (r) return r;
315   return pa_mnl();
316 }  
317
318 static int paa_1path(const char **rv) {
319   const char *cp;
320   int r;
321   
322   r= paa_1string(&cp); if (r) return r;
323   *rv= string2path(cp); if (!*rv) return tokv_error;
324   return 0;
325 }
326
327 static int paa_pathargs(const char **path_r, char ***newargs_r) {
328   /* Repeated calls do _not_ overwrite newargs_r; caller must free.
329    * Repeated calls _do_ overwrite string returned in path_r.
330    * Any call to this function invalidates previous returns in *rv.
331    */
332   char **newargs;
333   const char *string;
334   int used, size, r;
335
336   r= pa_string(&string); if (r == tokv_error) return r;
337   *path_r= string2path(string); if (!*path_r) return tokv_error;
338   
339   used=0; size=0;
340   newargs= xmalloc(sizeof(char*)*(size+1));
341
342   for (;;) {
343     r= yylex(); if (r == tokv_error) goto error;
344     if (r==tokv_newline) break;
345     if (unexpected(r,tokv_lwsp,"newline after or whitespace between arguments")) {
346       r= tokv_error; goto error;
347     }
348     r= yylex(); if (r==tokv_error) goto error;
349     if ((r & tokm_repres) == tokr_nonstring) {
350       r= unexpected(r,-1,"string for command argument");
351       goto error;
352     }
353     if (used>=size) {
354       if (used >= MAX_ARGSDEFVAR) {
355         r= parseerrprint("far too many arguments to service program");
356         goto error;
357       }
358       size= (used+5)<<1;
359       newargs= xrealloc(newargs,sizeof(char*)*(size+1));
360     }
361     newargs[used++]= xstrsave(yytext);
362   }
363   newargs[used]= 0;
364   *newargs_r= newargs;
365   return 0;
366   
367 error:
368   newargs[used]=0;
369   freecharparray(newargs);
370   return r;
371 }
372
373 static int paa_message(const char **message_r) {
374   /* Returned value is invalidated by repeated calls. */
375   static char *buildbuf;
376   static int buildbuflen;
377   const char *usetext;
378
379   int r, tl;
380
381   r= pa_mwsp(); if (r) return r;
382   tl= 1;
383   if (makeroom(&buildbuf,&buildbuflen,50)) return stringoverflow("start of message");
384   buildbuf[0]= 0;
385   for (;;) {
386     r= yylex(); if (r == tokv_error) return r;
387     if (r == tokv_eof)
388       return parseerrprint("unexpected end of file in message text");
389     if (r == tokv_newline) break;
390     usetext= r == tokv_lwsp ? " " : yytext;
391     tl+= strlen(usetext);
392     if (makeroom(&buildbuf,&buildbuflen,tl)) return stringoverflow("message");
393     strcat(buildbuf,usetext);
394   }
395   *message_r= buildbuf;
396   return 0;
397 }
398
399 /*
400  * Skipping routines (used by e.g. the `if' code).
401  */
402
403 static int skiptoeol(void) {
404   /* Returns 0 if OK, having just parsed the newline
405    * or tokv_error if an error occurs, leaving the position at the error
406    * (which may be EOF or a syntax error token).
407    */
408   int token;
409
410   do { token= yylex(); }
411   while (token != tokv_newline && !(token & tokt_exception));
412   if (token == tokv_newline) return 0;
413   if (token == tokv_error) return token;
414   assert(token == tokv_eof);
415   return parseerrprint("unexpected end of file while looking for end of line");
416 }
417
418 static int skip(int allowce) {
419   /* Scans a piece of config without executing it.  Returns
420    * tokv_error (leaving the parsing state at the error),
421    * or the tokt_controlend token with type allowce if one
422    * was found (in which case rest of line, including any whitespace
423    * after tokt_controlend, has not been parsed), or tokv_eof.
424    */
425   int token, r;
426
427   for (;;) { /* loop over lines */
428     cstate->reportlineno= cstate->lineno;
429     do { token= yylex(); } while (token == tokv_lwsp || token == tokv_newline);
430     if (token & tokt_exception) {
431       return token;
432     } else if (token & tokt_controlend) {
433       if (allowce == lr_controlend) return token;
434       else return unexpected(token,-1,"control structure end of a different kind");
435     } else if (token & tokt_controlstart) {
436       r= token;
437       while (r & tokt_controlstart) {
438         r= skiptoeol(); if (r) return r;
439         cstate->reportlineno= cstate->lineno;
440         r= skip(token); if (r & tokt_exception) return r;
441       }
442     } else if (!(token & tokt_directive) && !(token & tokt_condop)) {
443       return parseerrprint("not a directive (or conditional operator) "
444                            "while looking for control structure end");
445     }
446     r= skiptoeol(); if (r) return r;
447   }
448 }
449
450 /*
451  * Routines for parsing and getting the values of parameters
452  */
453
454 static void parm_1string(char ***rvalues, const char *tocopy) {
455   char **a;
456   a= xmalloc(sizeof(char*)*2);
457   a[0]= xstrsave(tocopy);
458   a[1]= 0;
459   *rvalues= a;
460 }
461
462 static char *parm_ulong(unsigned long ul) {
463   char *p;
464   int l;
465
466   l= CHAR_BIT*sizeof(unsigned long)/3+4;
467   p= xmalloc(l);
468   snyprintf(p,l,"%lu",ul);
469   return p;
470 }
471
472 static int parm_usernameuid(char ***rvalues, const char *name, uid_t id) {
473   char **a;
474   
475   a= xmalloc(sizeof(char*)*3);
476   a[0]= xstrsave(name);
477   a[1]= parm_ulong(id);
478   a[2]= 0;
479   *rvalues= a;
480   return 0;
481 }
482
483 static int parm_groups(char ***rvalues, int size,
484                        gid_t *gids, const char *const *groups) {
485   char **a;
486   int i;
487   
488   if (size >= 2 && gids[0] == gids[1]) { size--; gids++; groups++; }
489   a= xmalloc(sizeof(char*)*(size+1)*2);
490   for (i=0; i<size; i++) {
491     a[i]= xstrsave(groups[i]);
492     a[size+i]= parm_ulong(gids[i]);
493   }
494   a[size*2]= 0;
495   *rvalues= a;
496   return 0;
497 }  
498
499 static int pf_service(int ptoken, char ***rvalues) {
500   parm_1string(rvalues,service); return 0;
501 }
502
503 static int pf_callinguser(int ptoken, char ***rvalues) {
504   return parm_usernameuid(rvalues,loginname,request_mbuf.callinguid);
505 }
506
507 static int pf_serviceuser(int ptoken, char ***rvalues) {
508   return parm_usernameuid(rvalues,serviceuser,serviceuser_uid);
509 }
510
511 static int pf_callinggroup(int ptoken, char ***rvalues) {
512   return parm_groups(rvalues,request_mbuf.ngids,calling_gids,calling_groups);
513 }
514
515 static int pf_servicegroup(int ptoken, char ***rvalues) {
516   return parm_groups(rvalues,service_ngids,service_gids,service_groups);
517 }
518
519 static int pf_callingusershell(int ptoken, char ***rvalues) {
520   parm_1string(rvalues,callinguser_shell); return 0;
521 }
522
523 static int pf_serviceusershell(int ptoken, char ***rvalues) {
524   parm_1string(rvalues,serviceuser_shell); return 0;
525 }
526
527 static int pa_parameter(char ***rvalues, char **rname) {
528   /* Scans a single parameter token and calls the appropriate parameter
529    * function, returning tokv_error (leaving the parser state wherever),
530    * or 0 on success (having just parsed the parameter name).
531    * If rname is non-null then the name of the parameter (malloc'd) will
532    * be stored in it.
533    *
534    * Caller must free *rvalues using freecharparray.
535    */
536   int token, r, i;
537   char *name;
538
539   token= yylex(); if (token == tokv_error) return token;
540   name= xstrsave(yytext);
541   if ((token & tokm_repres) != tokr_nonstring &&
542       !memcmp(yytext,"u-",2) && strlen(yytext)>=3) {
543     for (i=0;
544          i<request_mbuf.nvars && strcmp(yytext+2,defvararray[i].key);
545          i++);
546     if (i>=request_mbuf.nvars) {
547       *rvalues= xmalloc(sizeof(char*));
548       **rvalues= 0;
549     } else {
550       parm_1string(rvalues,defvararray[i].value);
551     }
552   } else if (token & tokt_parameter) {
553     r= (lr_parameter)(token,rvalues);
554     if (r) { free(name); return r; }
555   } else {
556     free(name);
557     return unexpected(token,-1,"parameter name");
558   }
559   debug_dumpparameter(name,*rvalues);
560   if (rname) *rname= name;
561   else free(name);
562   return 0;
563 }
564
565 /*
566  * Routines for parsing conditions, including
567  * parameter-based conditional functions (parmcondition's).
568  */
569
570 int pcf_glob(int ctoken, char *const *pv, int *rtrue) {
571   int token, actrue, r;
572   char *const *pp;
573
574   actrue= 0;
575   r= pa_mwsp(); if (r) return r;
576   for (;;) {
577     token= yylex();
578     if ((token & tokm_repres) == tokr_nonstring)
579       return unexpected(token,-1,"glob pattern");
580     for (pp= pv; !actrue && *pp; pp++) if (!fnmatch(yytext,*pp,0)) actrue= 1;
581     token= yylex(); 
582     if (token == tokv_newline) break;
583     if (unexpected(token,tokv_lwsp,"newline after or whitespace between glob patterns"))
584       return tokv_error;
585   }
586   *rtrue= actrue;
587   return 0;
588 }
589
590 int pcf_range(int ctoken, char *const *pv, int *rtrue) {
591   int mintoken, min, maxtoken, max, r;
592   char *const *pp;
593   char *ep;
594   unsigned long v;
595
596   mintoken= pa_numberdollar(&min); if (mintoken == tokv_error) return mintoken;
597   maxtoken= pa_numberdollar(&max); if (maxtoken == tokv_error) return maxtoken;
598   r= pa_mnl(); if (r) return r;
599   for (pp= pv; *pp; pp++) {
600     v= strtoul(*pp,&ep,10); if (*ep) continue;
601     if (mintoken != tokv_dollar && v < min) continue;
602     if (maxtoken != tokv_dollar && v > max) continue;
603     *rtrue= 1; return 0;
604   }
605   *rtrue= 0; return 0;
606 }
607
608 int pcf_grep(int ctoken, char *const *pv, int *rtrue) {
609   FILE *file;
610   const char *cp;
611   char *const *pp;
612   char *buf, *p;
613   int r, maxlen, l, c, actrue, posstrue;
614   
615   r= paa_1path(&cp); if (r) return r;
616   file= fopen(cp,"r");
617   if (!file)
618     return parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno));
619   maxlen= 0;
620   for (pp= pv; *pp; pp++) { l= strlen(*pp); if (l > maxlen) maxlen= l; }
621   buf= xmalloc(maxlen+2); actrue= 0; c= 0;
622   while (!actrue && c!=EOF) {
623     c= getc(file); if (c==EOF) break;
624     if (ISCHAR(isspace,c)) continue;
625     l= maxlen+1; p= buf;
626     while (l>0 && c!='\n' && c!=EOF) { *p++= c; l--; c= getc(file); } 
627     if (c=='\n' || c==EOF || ISCHAR(isspace,c)) {
628       while (p>buf && ISCHAR(isspace,p[-1])) --p;
629       *p= 0; posstrue= 0;
630       for (pp= pv; !posstrue && *pp; pp++)
631         if (!strcmp(*pp,buf)) posstrue= 1;
632     } else {
633       posstrue= 0;
634     }
635     if (c!='\n' && c!=EOF) {
636       for (;;) {
637         c= getc(file);
638         if (c==EOF || c=='\n') break;
639         if (!ISCHAR(isspace,c)) posstrue= 0;
640       }
641     }
642     if (posstrue) actrue= 1;
643   }
644   if (ferror(file)) {
645     parseerrprint("error while reading `%s' for grep: %s",cp,strerror(errno));
646     fclose(file); free(buf); return tokv_error;
647   }
648   assert(actrue || feof(file));
649   fclose(file); free(buf); *rtrue= actrue;
650   return 0;
651
652
653 static int pa_condition(int *rtrue) {
654   /* Scans up to and including the newline following the condition;
655    * may scan more than one line if the condition is a multi-line
656    * one.  Returns 0 (setting *rtrue, and parsing up to and including
657    * the last newline) or tokv_error.
658    * Expects to scan the whitespace before its condition.
659    */
660   int r, token, andor, ctrue, actrue;
661   char **parmvalues;
662
663   r= pa_mwsp(); if (r) return r;
664   token= yylex();
665   if (token == tokv_error) {
666     return token;
667   } else if (token == tokv_not) {
668     r= pa_condition(&ctrue); if (r) return r;
669     *rtrue= !ctrue; return 0;
670   } else if (token == tokv_openparen) {
671     andor= 0; actrue= -1;
672     for (;;) {
673       cstate->reportlineno= cstate->lineno;
674       r= pa_condition(&ctrue); if (r) return r;
675       switch (andor) {
676       case 0:         assert(actrue==-1); actrue= ctrue;           break;
677       case tokv_and:  assert(actrue>=0); actrue= actrue && ctrue;  break;
678       case tokv_or:   assert(actrue>=0); actrue= actrue || ctrue;  break;
679       default:        abort();
680       }
681       do { token= yylex(); } while (token == tokv_lwsp);
682       if (token == tokv_error) return token;
683       if (token == tokv_closeparen) break;
684       if (andor) {
685         r= unexpected(token,andor,"same conjunction as before"); if (r) return r;
686       } else {
687         if (token != tokv_and && token != tokv_or)
688           return unexpected(token,-1,"first conjunction inside connective");
689         andor= token;
690       }
691     }
692     r= pa_mnl(); if (r) return r;
693     *rtrue= actrue; return 0;
694   } else if (token & tokt_parmcondition) {
695     r= pa_mwsp(); if (r) return r;
696     r= pa_parameter(&parmvalues,0); if (r) return r;
697     r= (lr_parmcond)(token,parmvalues,rtrue); freecharparray(parmvalues);
698     return r;
699   } else {
700     return unexpected(token,-1,"condition");
701   }
702 }
703
704 /*
705  * Directive functions and associated `common code' functions
706  */
707
708 /* Directives specifying the service program (execute-... and reset) */
709
710 static void execreset(void) {
711   execute= 0;
712   execbuiltin= 0;
713   free(execpath); execpath= 0;
714   freecharparray(execargs); execargs= 0;
715 }
716
717 int df_reject(int dtoken) {
718   int r;
719   
720   r= pa_mnl(); if (r) return r;
721   execreset();
722   execute= tokv_word_reject;
723   return 0;
724 }
725
726 int df_executefrompath(int dtoken) {
727   int r;
728   
729   r= pa_mnl(); if (r) return r;
730   execreset();
731   execute= tokv_word_executefrompath;
732   return 0;
733 }
734
735 int df_execute(int dtoken) {
736   const char *rv;
737   char **newargs;
738   int r;
739
740   r= paa_pathargs(&rv,&newargs); if (r) return r;
741   execreset();
742   execute= tokv_word_execute;
743   execargs= newargs;
744   execpath= xstrsave(rv);
745   return 0;
746 }
747
748 int df_executefromdirectory(int dtoken) {
749   const char *p, *q, *rv;
750   struct stat stab;
751   char *fn, **newargs;
752   int l, r;
753
754   r= paa_pathargs(&rv,&newargs); if (r) return r;
755   p= strrchr(service,'/'); if (p) p++; else p= service;
756   if (!*p || !ISCHAR(isalnum,*p)) {
757     parseerrprint("execute-from-directory requires initial char of service "
758                   "portion to be alphanumeric (service portion was `%s')",
759                   p);
760     freecharparray(newargs);
761     return tokv_error;
762   }
763   for (q=p+1; *q; q++) {
764     if (!ISCHAR(isalnum,*q) && *q != '-') {
765       parseerrprint("execute-from-directory requires service portion to "
766                     "contain only alphanumerics and hyphens (was `%s')",
767                     p);
768       freecharparray(newargs);
769       return tokv_error;
770     }
771   }
772   l= strlen(rv)+1+strlen(p)+1;
773   fn= xmalloc(l);
774   snyprintf(fn,l,"%s/%s",rv,p);
775   if (stat(fn,&stab)) {
776     if (errno == ENOENT) { free(fn); freecharparray(newargs); return 0; }
777     parseerrprint("failed to stat `%s' for execute-from-directory: %s",
778                   fn,strerror(errno));
779     free(fn); freecharparray(newargs); return tokv_error;
780   }
781   if (!S_ISREG(stab.st_mode)) {
782     parseerrprint("file `%s' in execute-from-directory is not an ordinary file"
783                   " or link to one (mode=0%lo)",fn,(unsigned long)stab.st_mode);
784     free(fn); freecharparray(newargs); return tokv_error;
785   }
786   execreset();
787   execute= tokv_word_executefromdirectory;
788   execargs= newargs;
789   execpath= fn;
790   return 0;
791 }
792
793 /* Parsing builtin service requests (execute-builtin) */
794
795 static int bispa_none(char ***rnewargs) {
796   return pa_mnl();
797 }
798
799 static int bispa_parameter(char ***rnewargs) {
800   int r, i;
801   char **parmvalues, *name, **newargs;
802   
803   r= pa_mwsp(); if (r) return r;
804   r= pa_parameter(&parmvalues,&name); if (r) return r;
805   for (i=0; parmvalues[i]; i++);
806   newargs= xmalloc(sizeof(char*)*(i+2));
807   newargs[0]= name;
808   memcpy(newargs+1,parmvalues,sizeof(char*)*(i+1));
809   free(parmvalues);
810   r= pa_mnl(); if (r) { free(newargs); return r; }
811   *rnewargs= newargs;
812   return 0;
813 }
814
815 int df_executebuiltin(int dtoken) {
816   int r;
817   builtinserviceexec_fnt *bisexec;
818   char *newpath, **newargs;
819
820   r= pa_mwsp(); if (r) return r;
821   r= yylex(); if (r == tokv_error) return r;
822   if (!(r & tokt_builtinservice)) return unexpected(r,-1,"builtin service name");
823   bisexec= lr_bisexec;
824   newpath= xstrsave(yytext);
825   newargs= 0;
826   r= lr_bispa(&newargs); if (r) { free(newpath); return r; }
827
828   execreset();
829   execute= tokv_word_executebuiltin;
830   execbuiltin= bisexec;
831   execpath= newpath;
832   execargs= newargs;
833   return 0;
834 }
835
836 /* Directives for changing other execution parameters */
837
838 int dfg_setflag(int dtoken) {
839   int r;
840
841   r= pa_mnl(); if (r) return r;
842   *lr_flag= lr_flagval;
843   return 0;
844 }
845
846 int df_reset(int dtoken) {
847   int r;
848
849   r= pa_mnl(); if (r) return r;
850   r= parse_string(RESET_CONFIGURATION,"<builtin reset configuration>",1);
851   return r;
852 }
853
854 int dfg_fdwant(int dtoken) {
855   int fdmin, fdmax, r, needreadwrite, havereadwrite, fd;
856
857   needreadwrite= lr_fdwant_readwrite;
858   r= pa_mwsp(); if (r) return r;
859   r= yylex(); if (r == tokv_error) return r;
860   if (!(r & tokt_fdrange)) return unexpected(r,-1,"file descriptor range");
861   fdmin= lr_min; fdmax= lr_max;
862   if (fdmin<0 || fdmin>MAX_ALLOW_FD ||
863       (fdmax != -1 && fdmax<0) || fdmax>MAX_ALLOW_FD)
864     return parseerrprint("file descriptor in range is negative or far too large");
865   r= yylex(); if (r == tokv_error) return r;
866   if (r == tokv_newline) {
867     if (needreadwrite > 0)
868       return parseerrprint("read or write is required");
869     havereadwrite= 0;
870   } else if (r == tokv_lwsp) {
871     if (needreadwrite < 0)
872       return parseerrprint("read or write not allowed");
873     r= yylex(); if (r == tokv_error) return r;
874     if (!(r & tokt_readwrite))
875       return unexpected(r,-1,"read or write (or perhaps newline)");
876     havereadwrite= r;
877     r= pa_mnl(); if (r) return r;
878   } else {
879     return unexpected(r,-1,"whitespace before read or write or newline");
880   }
881   ensurefdarray(fdmin);
882   if (fdmax == -1) {
883     if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd))
884       return parseerrprint("unspecified maximum only allowed"
885                            " with reject-fd and ignore-fd");
886     fdmax= fdarrayused-1;
887     restfdwantstate= dtoken;
888     restfdwantrw= havereadwrite;
889   }
890   ensurefdarray(fdmax);
891   for (fd=fdmin; fd<=fdmax; fd++) {
892     fdarray[fd].wantstate= dtoken;
893     fdarray[fd].wantrw= havereadwrite;
894   }
895   return 0;
896 }
897
898 /* Directives for changing error handling */
899
900 int df_errorstostderr(int dtoken) {
901   int r;
902   
903   r= pa_mnl(); if (r) return r;
904   closeerrorfile(); eh.handling= dtoken;
905   return 0;
906 }
907
908 int df_errorstosyslog(int dtoken) {
909   int token, level, facility;
910
911   facility= DEFUSERLOGFACILITY;
912   level= DEFUSERLOGLEVEL;
913   token= yylex();
914   if (token == tokv_lwsp) {
915     token= yylex(); if (token == tokv_error) return token;
916     if (!(token & tokt_logfacility))
917       return unexpected(token,-1,"syslog facility (or end of line)");
918     facility= lr_logfacility;
919     token= yylex();
920   }    
921   if (token == tokv_lwsp) {
922     token= yylex(); if (token == tokv_error) return token;
923     if (!(token & tokt_loglevel))
924       return unexpected(token,-1,"syslog level (or end of line) after facility");
925     level= lr_loglevel;
926     token= yylex();
927   }
928   if (unexpected(token,tokv_newline,"end of line after errors-to-syslog"))
929     return tokv_error;
930   closeerrorfile(); eh.handling= tokv_word_errorstosyslog;
931   eh.logfacility= facility; eh.loglevel= level;
932   return 0;
933 }
934
935 int df_errorstofile(int dtoken) {
936   const char *cp;
937   FILE *file;
938   int r;
939   
940   r= paa_1path(&cp); if (r) return r;
941   file= fopen(cp,"a");
942   if (!file)
943     return parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno));
944   if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) {
945     parseerrprint("unable to set line buffering on errors file: %s",strerror(errno));
946     fclose(file); return tokv_error;
947   }
948   closeerrorfile(); eh.handling= tokv_word_errorstofile;
949   eh.file= file; eh.filename= xstrsave(cp);
950   return 0;
951 }
952
953 /* Directives for including other files or configuration data */
954
955 int dfi_includeuserrcfile(int dtoken) {
956   int r;
957
958   r= pa_mnl(); if (r) return r;
959   assert(userrcfile);
960   return parse_file(userrcfile,0);
961 }
962
963 int dfi_includeclientconfig(int dtoken) {
964   int r;
965
966   r= pa_mnl(); if (r) return r;
967   assert(overridedata);
968   return parse_string(overridedata,"<configuration override data>",0);
969 }
970
971 int df_include(int dtoken) {
972   const char *cp;
973   int r, found;
974
975   r= paa_1path(&cp); if (r) return r;
976   r= parse_file(cp,&found); if (r) return r;
977   if (found || dtoken == tokv_word_includeifexist) return 0;
978   return parseerrprint(dtoken == tokv_word_includesysconfig ?
979                        "system configuration file `%s' does not exist" :
980                        "included file `%s' does not exist",
981                        cp);
982 }
983
984 int df_includedirectory(int dtoken) {
985   static char *buildbuf=0;
986   static int buildbuflen=0;
987   
988   int r, cpl, tel, c, found;
989   DIR *d;
990   struct dirent *de;
991   const char *p, *cpget;
992   char *cp;
993   
994   r= paa_1path(&cpget); if (r) return r;
995   d= opendir(cpget);
996   if (!d)
997     return parseerrprint("unable to open directory `%s': %s",cpget,strerror(errno));
998   cp= xstrsave(cpget);
999   cpl= strlen(cp);
1000   while ((errno=0, de= readdir(d))) {
1001     tel= strlen(de->d_name);
1002     if (!tel) continue;
1003     p= de->d_name;
1004     if (!*p || !ISCHAR(isalnum,*p)) continue;
1005     while ((c= *++p)) if (!(ISCHAR(isalnum,c) || c=='-')) break;
1006     if (c) continue;
1007     if (makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1)) {
1008       stringoverflow("pathname in directory");
1009       r= tokv_error; goto x_err;
1010     }
1011     snyprintf(buildbuf,buildbuflen,"%s/%s",cp,de->d_name);
1012     r= parse_file(buildbuf,&found); if (r) goto x_err;
1013     if (!found) {
1014       r= parseerrprint("unable to open file `%s' in included directory `%s': %s",
1015                        de->d_name,cp,strerror(errno));
1016       goto x_err;
1017     }
1018   }
1019   if (errno) {
1020     parseerrprint("error reading directory `%s': %s",cp,strerror(errno));
1021     closedir(d);
1022     free(cp);
1023     return tokv_error;
1024   }
1025   if (closedir(d)) {
1026     parseerrprint("error closing directory `%s': %s",cp,strerror(errno));
1027     free(cp);
1028     return tokv_error;
1029   }
1030   free(cp);
1031   return 0;
1032
1033 x_err:
1034   closedir(d);
1035   free(cp);
1036   return r;
1037 }
1038
1039 static int oldquote = 0;
1040
1041 int dfg_lookupquotemode(int dtoken) {
1042   int r;
1043   r= pa_mnl(); if (r) return r;
1044   oldquote = dtoken == tokv_word_includelookupquoteold;
1045   return r;
1046 }
1047
1048 int df_includelookup(int dtoken) {
1049   static char *buildbuf=0;
1050   int buildbuflen=0;
1051   
1052   char **parmvalues, **pp, *p, *q, *cp;
1053   const char *cpget;
1054   struct stat stab;
1055   int r, done, thisdone, cpl, c;
1056
1057   r= pa_mwsp(); if (r) return r;
1058   r= pa_parameter(&parmvalues,0); if (r) return r;
1059   r= paa_1path(&cpget); if (r) { freecharparray(parmvalues); return r; }
1060   if (stat(cpget,&stab)) {
1061     parseerrprint("unable to access directory `%s': %s",cpget,strerror(errno));
1062     freecharparray(parmvalues); return tokv_error;
1063   }
1064   if (!S_ISDIR(stab.st_mode)) {
1065     parseerrprint("object `%s' is not a directory or link to one",cpget);
1066     freecharparray(parmvalues); return tokv_error;
1067   }
1068   done= 0;
1069   cp= xstrsave(cpget);
1070   cpl= strlen(cp);
1071   if (!parmvalues[0]) {
1072     if (makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP))) {
1073       stringoverflow("pathname in directory for lookup of undefined parameter");
1074       r= tokv_error; goto x_err;
1075     }
1076     snyprintf(buildbuf,buildbuflen,"%s/" NONEINCLUDELOOKUP,cp);
1077     r= parse_file(buildbuf,&thisdone); if (r) goto x_err;
1078     if (thisdone) done= 1;
1079   } else {
1080     for (pp=parmvalues;
1081          *pp && (!done || dtoken == tokv_word_includelookupall);
1082          pp++) {
1083       if (makeroom(&buildbuf,&buildbuflen,
1084                    cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1)) {
1085         stringoverflow("pathname in directory for lookup");
1086         r= tokv_error; goto x_err;
1087       }
1088       strcpy(buildbuf,cp);
1089       p= *pp; q= buildbuf+cpl;
1090       *q++= '/';
1091       if (!*p) {
1092         strcpy(q,EMPTYINCLUDELOOKUP);
1093       } else {
1094         if (*p=='.') *q++= ':';
1095         while ((c= *p++)) {
1096           if (c=='/') {
1097             *q++= ':';
1098             c= '-';
1099           } else if (oldquote ?
1100                      !((c >= '0' && c <= '9') ||
1101                        (c >= 'a' && c <= 'z') ||
1102                        c == '-' || c == '_') :
1103                      (c==':')) {
1104             *q++= ':';
1105           }
1106           *q++= c;
1107         }
1108         *q++= 0;
1109       }
1110       r= parse_file(buildbuf,&thisdone);
1111       if (r) goto x_err;
1112       if (thisdone) done= 1;
1113     }
1114   }
1115   if (!done) {
1116     if (makeroom(&buildbuf,&buildbuflen,
1117                  cpl+1+sizeof(DEFAULTINCLUDELOOKUP))) {
1118       stringoverflow("pathname in directory for lookup of default");
1119       r= tokv_error; goto x_err;
1120     }
1121     snyprintf(buildbuf,buildbuflen,"%s/" DEFAULTINCLUDELOOKUP,cp);
1122     r= parse_file(buildbuf,0); if (r) goto x_err;
1123   }
1124   r= 0;
1125   
1126 x_err:
1127   freecharparray(parmvalues);
1128   free(cp);
1129   return r;
1130 }
1131
1132 /* Control constructs */
1133
1134 int df_catchquit(int dtoken) {
1135   int r;
1136
1137   r= pa_mnl(); if (r) return r;
1138   r= parser(tokv_word_catchquit);
1139   if (r == tokv_quit || r == tokv_error) {
1140     if (r == tokv_error) {
1141       r= parse_string(RESET_CONFIGURATION,
1142                       "<builtin reset configuration (caught error)>",1);
1143       assert(!r);
1144     }
1145     r= skip(tokv_word_catchquit);
1146   }
1147   if (r & tokt_controlend) {
1148     assert(r == tokv_word_hctac);
1149     r= pa_mnl();
1150   }
1151   return r;
1152 }
1153
1154 int df_if(int dtoken) {
1155   int r, true, done;
1156   
1157   done= 0;
1158   do {
1159     r= pa_condition(&true); if (r) return r;
1160     if (!done && true) { r= parser(tokv_word_if); done= 1; }
1161     else { r= skip(tokv_word_if); }
1162     if (!(r & tokt_controlend)) return r;
1163   } while (r == tokv_word_elif);
1164   if (r == tokv_word_else) {
1165     r= pa_mnl(); if (r) return r;
1166     cstate->reportlineno= cstate->lineno;
1167     if (done) r= skip(tokv_word_if);
1168     else r= parser(tokv_word_if);
1169     if (!(r & tokt_controlend)) return r;
1170   }
1171   if (unexpected(r,tokv_word_fi,"`fi' to end `if'")) return tokv_error;
1172   return pa_mnl();
1173 }
1174
1175 int df_errorspush(int dt) {
1176   struct error_handling save;
1177   int r;
1178
1179   r= pa_mnl(); if (r) return r;
1180
1181   save= eh;
1182   eh.filekeep= 1;
1183
1184   r= parser(tokv_word_errorspush);
1185
1186   closeerrorfile();
1187   eh= save;
1188
1189   if (r & tokt_controlend) {
1190     assert(r == tokv_word_srorre);
1191     r= pa_mnl();
1192   }
1193   return r;
1194 }
1195
1196 /* Miscelleanous directives */
1197
1198 int df_cd(int dtoken) {
1199   const char *cp;
1200   int r;
1201
1202   r= paa_1path(&cp); if (r) return r;
1203   if (!chdir(cp)) return 0;
1204   return parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno));
1205 }
1206
1207 int df_userrcfile(int dtoken) {
1208   const char *cp;
1209   int r;
1210
1211   r= paa_1path(&cp); if (r) return r;
1212   free(userrcfile); userrcfile= xstrsave(cp);
1213   return 0;
1214 }
1215
1216 int df_message(int dtoken) {
1217   const char *mp;
1218   int r;
1219
1220   r= paa_message(&mp); if (r) return r;
1221   parseerrprint("`message' directive: %s",mp);
1222   return 0;
1223 }
1224
1225 int df_error(int dtoken) {
1226   const char *mp;
1227   int r;
1228
1229   r= paa_message(&mp); if (r) return r;
1230   return parseerrprint("`error' directive: %s",mp);
1231 }
1232
1233 int df_eof(int dtoken) {
1234   int r;
1235
1236   r= pa_mnl(); if (r) return r;
1237   return tokv_eof;
1238 }
1239
1240 int df_quit(int dtoken) {
1241   int r;
1242
1243   r= pa_mnl(); if (r) return r;
1244   return tokv_quit;
1245 }
1246
1247 /*
1248  * Main parser routines
1249  */
1250       
1251 static void parser_push(struct parser_state *usestate,
1252                         const char *newfile,
1253                         const struct stat *newfilestab,
1254                         YY_BUFFER_STATE ybuf,
1255                         int isinternal) {
1256   usestate->lineno= 1;
1257   usestate->reportlineno= 1;
1258   usestate->filename= newfile;
1259   usestate->filestab= *newfilestab;
1260   usestate->notedreferer= 0;
1261   usestate->isinternal= isinternal;
1262   usestate->ybuf= ybuf;
1263   usestate->upstate= cstate;
1264
1265   cstate= usestate;
1266   yy_switch_to_buffer(ybuf);
1267 }
1268
1269 static void parser_pop(void) {
1270   struct parser_state *oldstate;
1271
1272   oldstate= cstate;
1273   cstate= cstate->upstate;
1274   if (cstate) yy_switch_to_buffer(cstate->ybuf);
1275   yy_delete_buffer(oldstate->ybuf);
1276 }
1277
1278 int parse_string(const char *string, const char *descrip, int isinternal) {
1279   /* Returns the same things as parser, except that tokv_eof is turned
1280    * into 0.  *string must be statically allocated or copied, so that
1281    * it is not overwritten while the parsing takes place (unlike with
1282    * parse_file).
1283    */
1284   static const struct stat blankstab;
1285   
1286   struct parser_state usestate;
1287   YY_BUFFER_STATE ybuf;
1288   int r;
1289
1290   ybuf= yy_scan_string(string);
1291   if (!ybuf) syscallerror("unable to create flex buffer for internal string");
1292   parser_push(&usestate,descrip,&blankstab,ybuf,isinternal);
1293   
1294   r= parser(0);
1295
1296   parser_pop();
1297   if (r == tokv_eof) r= 0;
1298   return r;
1299 }
1300
1301 static int parse_file(const char *string, int *didexist) {
1302   /* Returns the same things as parser, except that tokv_eof is turned
1303    * into 0.  If *didexist is 0 then errno will have been set.
1304    * *string will be copied by parse_file so it may be be overwritten
1305    * during the parsing (so, for example, yytext need not be copied).
1306    */
1307   static int fileparselevel= 0;
1308   
1309   struct parser_state usestate, *checkrecurse;
1310   YY_BUFFER_STATE ybuf;
1311   int r;
1312   FILE *file;
1313   char *filename;
1314   struct stat newstab;
1315
1316   if (fileparselevel >= MAX_INCLUDE_NEST)
1317     return parseerrprint("too many nested levels of included files");
1318   file= fopen(string,"r");
1319   if (!file) {
1320     if (errno == ENOENT) {
1321       if (didexist) *didexist= 0;
1322       return 0;
1323     }
1324     return parseerrprint("unable to open config file `%s': %s",string,strerror(errno));
1325   }
1326   r= fstat(fileno(file),&newstab); if (r) syscallerror("unable to fstat new file");
1327   for (checkrecurse= cstate; checkrecurse; checkrecurse= checkrecurse->upstate) {
1328     if (!checkrecurse->filestab.st_mode) continue;
1329     if (newstab.st_dev==checkrecurse->filestab.st_dev &&
1330         newstab.st_ino==checkrecurse->filestab.st_ino) {
1331       fclose(file);
1332       return parseerrprint("recursion detected - config file `%s' calls itself",string);
1333     }
1334   }
1335   
1336   if (didexist) *didexist= 1;
1337
1338   ybuf= yy_create_buffer(file,YY_BUF_SIZE);
1339   if (!ybuf) syscallerror("unable to create flex buffer for file");
1340   filename= xstrsave(string);
1341   parser_push(&usestate,filename,&newstab,ybuf,0);
1342   fileparselevel++;
1343   
1344   r= parser(0);
1345   if (ferror(file))
1346     r= parseerrprint("error reading configuration file `%s'",string);
1347
1348   fileparselevel--;
1349   parser_pop();
1350   free(filename);
1351   fclose(file);
1352   if (r == tokv_eof) r= 0;
1353   return r;
1354 }
1355
1356 static int parser(int allowce) {
1357   /* Returns:
1358    *  an exception (error, eof or quit)
1359    *   then rest of `file' is uninteresting
1360    * or
1361    *  token if allowce was !0 and equal to token's controlend
1362    *   then rest of `file' (including rest of line with the
1363    *   controlend - even the whitespace) not scanned yet
1364    */
1365   int token, r;
1366
1367   for (;;) { /* loop over lines */
1368     cstate->reportlineno= cstate->lineno;
1369     do { token= yylex(); } while (token == tokv_lwsp);
1370     if (token & tokt_exception) {
1371       return token;
1372     } else if (token & tokt_controlend) {
1373       if (lr_controlend == allowce) return token;
1374       else return unexpected(token,-1,"directive (not this kind of"
1375                              " control structure end)");
1376     } else if (token & tokt_directive) {
1377       if ((token & tokt_internal) && !cstate->isinternal)
1378         return unexpected(token,-1,"published directive, not internal-use-only one");
1379       r= (lr_dir)(token); if (r) { assert(r & tokt_exception); return r; }
1380     } else if (token == tokv_newline) {
1381       /* ignore blank lines (and comment-only lines) */
1382     } else {
1383       return unexpected(token,-1,"directive");
1384     }
1385   }
1386 }