chiark / gitweb /
wip fix clean
[userv.git] / parser.c
index e49b5ca6895b86e6a3782bf9cc9c5b78ff2e0226..037e6f9fe19a49eef2b4a88fac87e80645acb964 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -6,7 +6,7 @@
  * about m4 quoting &c., but we have to #include it so that the C
  * objects from the lexer are available.
  *
- * Copyright (C)1996-1997 Ian Jackson
+ * Copyright (C)1996-1999,2001 Ian Jackson
  *
  * This is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by
@@ -98,7 +98,7 @@ static void errwhere(struct parser_state *tstate, char *bufput, int bufputlen) {
            tstate->filename,tstate->reportlineno);
 }
 
-void parseerrprint(const char *fmt, ...) {
+int parseerrprint(const char *fmt, ...) {
   va_list al;
   char errmsg[MAX_ERRMSG_LEN];
 
@@ -107,6 +107,8 @@ void parseerrprint(const char *fmt, ...) {
   vsnytprintfcat(errmsg,sizeof(errmsg),fmt,al);
   senderrmsg(errmsg,eh.handling);
   va_end(al);
+
+  return tokv_error;
 }
 
 static int unexpected(int found, int wanted, const char *wantedstr) {
@@ -115,13 +117,11 @@ static int unexpected(int found, int wanted, const char *wantedstr) {
    */
   if (found == wanted) return 0;
   if (found == tokv_error) return found;
-  parseerrprint("found %s, expected %s",printtoken(found),wantedstr);
-  return tokv_error;
+  return parseerrprint("found %s, expected %s",printtoken(found),wantedstr);
 }
 
 static int stringoverflow(const char *where) {
-  parseerrprint("string buffer became far too large building %s",where);
-  return tokv_error;
+  return parseerrprint("string buffer became far too large building %s",where);
 }
 
 /*
@@ -136,43 +136,54 @@ static void freecharparray(char **array) {
   free(array);
 }
 
+static void countnewlines(void) {
+  char *p;
+  for (p=yytext; *p; p++)
+    if (*p == '\n')
+      cstate->lineno++;
+}
+
 static int dequote(char *inplace) {
   char *p, *q, buf[4], *bep;
   int v;
-  
+
   p=q=inplace;
-  assert(*p++ = '"');
+  assert(*p=='"');  p++;
   while (*p && *p != '"') {
     if (*p != '\\') { *q++= *p++; continue; }
     switch (*++p) {
-    case 'n': *q++= '\n'; continue;
-    case 'r': *q++= '\r'; continue;
-    case 't': *q++= '\t'; continue;
+    case 'n': *q++= '\n'; p++; continue;
+    case 'r': *q++= '\r'; p++; continue;
+    case 't': *q++= '\t'; p++; continue;
     case 'x':
-      assert(buf[0]= *++p); assert(buf[1]= *++p); buf[2]= 0;
-      v= strtoul(buf,&bep,16); assert(bep == buf+2);
-      assert(!(v & ~0xff)); *q++= v; p++; continue;
+      p++;
+      if (!((buf[0]= *p++) && (buf[1]= *p++)))
+       return parseerrprint("quoted string ends inside \\x<hex> sequence");
+      buf[2]= 0;
+      v= strtoul(buf,&bep,16);
+      if (bep != buf+2)
+       return parseerrprint("invalid \\<hex> sequence \\x%s in quoted string",buf);
+      assert(!(v & ~0xff));
+      *q++= v;
+      continue;
     default:
-      if (isalpha(*p)) {
-        parseerrprint("unknown \\<letter> sequence \\%c in quoted string",*p);
-        return tokv_error;
-      } else if (isdigit(*p)) {
-        assert(buf[0]= *++p); assert(buf[1]= *++p); assert(buf[2]= *++p);
+      if (ISCHAR(isalpha,*p))
+        return parseerrprint("unknown \\<letter> sequence \\%c in quoted string",*p);
+      if (ISCHAR(isdigit,*p)) {
+        if (!((buf[0]= *p++) && (buf[1]= *p++) && (buf[2]= *p++))) abort();
         buf[3]= 0; v= strtoul(buf,&bep,8);
-        if (bep != buf+3 || (v & ~0xff)); {
-          parseerrprint("invalid \\<octal> sequence \\%s in quoted string",buf);
-          return tokv_error;
-        }
-        *q++= v; p++; continue;
-      } else if (ispunct(*p)) {
+        if (bep != buf+3 || (v & ~0xff))
+          return parseerrprint("invalid \\<octal> sequence \\%s in quoted string",buf);
+        *q++= v; continue;
+      } else if (ISCHAR(ispunct,*p)) {
         *q++= *p++; continue;
       } else {
        while (*p==' ' || *p=='\t') p++;
-       assert(*p=='\n');
+       v= *p++; assert(v=='\n');
       }
     }
   }
-  assert(*p); assert(!*++p);
+  assert(*p); p++; assert(!*p);
   *q++= 0;
   return tokv_quotedstring;
 }
@@ -212,8 +223,8 @@ const char *printtoken(int token) {
     l= strlen(buf); i= sizeof(buf)-l-2; p= yytext; q= buf+l;
     while ((c= *p++)) {
       if (i-- <= 0) { q--; strcpy(q-3,"..."); break; }
-      if (isspace(c)) c= ' ';
-      else if (!isprint(c) || iscntrl(c)) c= '?';
+      if (ISCHAR(isspace,c)) c= ' ';
+      else if (!ISCHAR(isprint,c) || ISCHAR(iscntrl,c)) c= '?';
       else *q++= c;
     }
     strcpy(q,"'");
@@ -337,8 +348,8 @@ static int paa_pathargs(const char **path_r, char ***newargs_r) {
     }
     if (used>=size) {
       if (used >= MAX_ARGSDEFVAR) {
-       parseerrprint("far too many arguments to service program");
-       r= tokv_error; goto error;
+       r= parseerrprint("far too many arguments to service program");
+       goto error;
       }
       size= (used+5)<<1;
       newargs= xrealloc(newargs,sizeof(char*)*(size+1));
@@ -359,6 +370,7 @@ static int paa_message(const char **message_r) {
   /* Returned value is invalidated by repeated calls. */
   static char *buildbuf;
   static int buildbuflen;
+  const char *usetext;
 
   int r, tl;
 
@@ -368,14 +380,13 @@ static int paa_message(const char **message_r) {
   buildbuf[0]= 0;
   for (;;) {
     r= yylex(); if (r == tokv_error) return r;
-    if (r == tokv_eof) {
-      parseerrprint("unexpected end of file in message text");
-      return tokv_error;
-    }
+    if (r == tokv_eof)
+      return parseerrprint("unexpected end of file in message text");
     if (r == tokv_newline) break;
-    tl+= strlen(yytext);
+    usetext= r == tokv_lwsp ? " " : yytext;
+    tl+= strlen(usetext);
     if (makeroom(&buildbuf,&buildbuflen,tl)) return stringoverflow("message");
-    strcat(buildbuf,yytext);
+    strcat(buildbuf,usetext);
   }
   *message_r= buildbuf;
   return 0;
@@ -397,8 +408,7 @@ static int skiptoeol(void) {
   if (token == tokv_newline) return 0;
   if (token == tokv_error) return token;
   assert(token == tokv_eof);
-  parseerrprint("unexpected end of file while looking for end of line");
-  return tokv_error;
+  return parseerrprint("unexpected end of file while looking for end of line");
 }
 
 static int skip(int allowce) {
@@ -412,7 +422,7 @@ static int skip(int allowce) {
 
   for (;;) { /* loop over lines */
     cstate->reportlineno= cstate->lineno;
-    do { token= yylex(); } while (token == tokv_lwsp);
+    do { token= yylex(); } while (token == tokv_lwsp || token == tokv_newline);
     if (token & tokt_exception) {
       return token;
     } else if (token & tokt_controlend) {
@@ -426,9 +436,8 @@ static int skip(int allowce) {
         r= skip(token); if (r & tokt_exception) return r;
       }
     } else if (!(token & tokt_directive) && !(token & tokt_condop)) {
-      parseerrprint("not a directive (or conditional operator) "
-                   "while looking for control structure end");
-      return tokv_error;
+      return parseerrprint("not a directive (or conditional operator) "
+                          "while looking for control structure end");
     }
     r= skiptoeol(); if (r) return r;
   }
@@ -488,7 +497,7 @@ static int pf_service(int ptoken, char ***rvalues) {
 }
 
 static int pf_callinguser(int ptoken, char ***rvalues) {
-  return parm_usernameuid(rvalues,logname,request_mbuf.callinguid);
+  return parm_usernameuid(rvalues,loginname,request_mbuf.callinguid);
 }
 
 static int pf_serviceuser(int ptoken, char ***rvalues) {
@@ -603,20 +612,18 @@ int pcf_grep(int ctoken, char *const *pv, int *rtrue) {
   
   r= paa_1path(&cp); if (r) return r;
   file= fopen(cp,"r");
-  if (!file) {
-    parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno));
-    return tokv_error;
-  }
+  if (!file)
+    return parseerrprint("unable to open file `%s' for grep: %s",cp,strerror(errno));
   maxlen= 0;
   for (pp= pv; *pp; pp++) { l= strlen(*pp); if (l > maxlen) maxlen= l; }
   buf= xmalloc(maxlen+2); actrue= 0; c= 0;
   while (!actrue && c!=EOF) {
     c= getc(file); if (c==EOF) break;
-    if (isspace(c)) continue;
+    if (ISCHAR(isspace,c)) continue;
     l= maxlen+1; p= buf;
     while (l>0 && c!='\n' && c!=EOF) { *p++= c; l--; c= getc(file); } 
-    if (c=='\n' || c==EOF || isspace(c)) {
-      while (p>buf && isspace(p[-1])) --p;
+    if (c=='\n' || c==EOF || ISCHAR(isspace,c)) {
+      while (p>buf && ISCHAR(isspace,p[-1])) --p;
       *p= 0; posstrue= 0;
       for (pp= pv; !posstrue && *pp; pp++)
        if (!strcmp(*pp,buf)) posstrue= 1;
@@ -627,7 +634,7 @@ int pcf_grep(int ctoken, char *const *pv, int *rtrue) {
       for (;;) {
         c= getc(file);
         if (c==EOF || c=='\n') break;
-        if (!isspace(c)) posstrue= 0;
+        if (!ISCHAR(isspace,c)) posstrue= 0;
       }
     }
     if (posstrue) actrue= 1;
@@ -744,7 +751,7 @@ int df_executefromdirectory(int dtoken) {
 
   r= paa_pathargs(&rv,&newargs); if (r) return r;
   p= strrchr(service,'/'); if (p) p++; else p= service;
-  if (!*p || !isalnum(*p)) {
+  if (!*p || !ISCHAR(isalnum,*p)) {
     parseerrprint("execute-from-directory requires initial char of service "
                  "portion to be alphanumeric (service portion was `%s')",
                  p);
@@ -752,7 +759,7 @@ int df_executefromdirectory(int dtoken) {
     return tokv_error;
   }
   for (q=p+1; *q; q++) {
-    if (!isalnum(*q) && *q != '-') {
+    if (!ISCHAR(isalnum,*q) && *q != '-') {
       parseerrprint("execute-from-directory requires service portion to "
                    "contain only alphanumerics and hyphens (was `%s')",
                    p);
@@ -851,21 +858,16 @@ int dfg_fdwant(int dtoken) {
   if (!(r & tokt_fdrange)) return unexpected(r,-1,"file descriptor range");
   fdmin= lr_min; fdmax= lr_max;
   if (fdmin<0 || fdmin>MAX_ALLOW_FD ||
-      (fdmax != -1 && fdmax<0) || fdmax>MAX_ALLOW_FD) {
-    parseerrprint("file descriptor in range is negative or far too large");
-    return tokv_error;
-  }
+      (fdmax != -1 && fdmax<0) || fdmax>MAX_ALLOW_FD)
+    return parseerrprint("file descriptor in range is negative or far too large");
   r= yylex(); if (r == tokv_error) return r;
   if (r == tokv_newline) {
-    if (needreadwrite > 0) {
-      parseerrprint("read or write is required");
-      return tokv_error;
-    }
+    if (needreadwrite > 0)
+      return parseerrprint("read or write is required");
     havereadwrite= 0;
   } else if (r == tokv_lwsp) {
-    if (needreadwrite < 0) {
-      parseerrprint("read or write not allowed"); return tokv_error;
-    }
+    if (needreadwrite < 0)
+      return parseerrprint("read or write not allowed");
     r= yylex(); if (r == tokv_error) return r;
     if (!(r & tokt_readwrite))
       return unexpected(r,-1,"read or write (or perhaps newline)");
@@ -877,7 +879,8 @@ int dfg_fdwant(int dtoken) {
   ensurefdarray(fdmin);
   if (fdmax == -1) {
     if (!(dtoken == tokv_word_rejectfd || dtoken == tokv_word_ignorefd))
-      parseerrprint("unspecified maximum only allowed with reject-fd and ignore-fd");
+      return parseerrprint("unspecified maximum only allowed"
+                          " with reject-fd and ignore-fd");
     fdmax= fdarrayused-1;
     restfdwantstate= dtoken;
     restfdwantrw= havereadwrite;
@@ -934,10 +937,8 @@ int df_errorstofile(int dtoken) {
   
   r= paa_1path(&cp); if (r) return r;
   file= fopen(cp,"a");
-  if (!file) {
-    parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno));
-    return tokv_error;
-  }
+  if (!file)
+    return parseerrprint("unable to open error log file `%s': %s",cp,strerror(errno));
   if (setvbuf(file,0,_IOLBF,MAX_ERRMSG_LEN)) {
     parseerrprint("unable to set line buffering on errors file: %s",strerror(errno));
     fclose(file); return tokv_error;
@@ -972,11 +973,10 @@ int df_include(int dtoken) {
   r= paa_1path(&cp); if (r) return r;
   r= parse_file(cp,&found); if (r) return r;
   if (found || dtoken == tokv_word_includeifexist) return 0;
-  parseerrprint(dtoken == tokv_word_includesysconfig ?
-               "system configuration file `%s' does not exist" :
-               "included file `%s' does not exist",
-               cp);
-  return tokv_error;
+  return parseerrprint(dtoken == tokv_word_includesysconfig ?
+                      "system configuration file `%s' does not exist" :
+                      "included file `%s' does not exist",
+                      cp);
 }
 
 int df_includedirectory(int dtoken) {
@@ -986,76 +986,88 @@ int df_includedirectory(int dtoken) {
   int r, cpl, tel, c, found;
   DIR *d;
   struct dirent *de;
-  const char *p, *cp;
+  const char *p, *cpget;
+  char *cp;
   
-  r= paa_1path(&cp); if (r) return r;
-  d= opendir(cp);
-  if (!d) {
-    parseerrprint("unable to open directory `%s': %s",cp,strerror(errno));
-    return tokv_error;
-  }
+  r= paa_1path(&cpget); if (r) return r;
+  d= opendir(cpget);
+  if (!d)
+    return parseerrprint("unable to open directory `%s': %s",cpget,strerror(errno));
+  cp= xstrsave(cpget);
   cpl= strlen(cp);
   while ((de= readdir(d))) {
     tel= strlen(de->d_name);
     if (!tel) continue;
     p= de->d_name;
-    if (!*p || !isalnum(*p)) continue;
-    while ((c= *++p)) if (!(isalnum(c) || c=='-')) break;
+    if (!*p || !ISCHAR(isalnum,*p)) continue;
+    while ((c= *++p)) if (!(ISCHAR(isalnum,c) || c=='-')) break;
     if (c) continue;
-    if (makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1))
-      return stringoverflow("pathname in directory");
+    if (makeroom(&buildbuf,&buildbuflen,cpl+1+tel+1)) {
+      stringoverflow("pathname in directory");
+      r= tokv_error; goto x_err;
+    }
     snyprintf(buildbuf,buildbuflen,"%s/%s",cp,de->d_name);
-    r= parse_file(buildbuf,&found); if (r) { closedir(d); return r; }
+    r= parse_file(buildbuf,&found); if (r) goto x_err;
     if (!found) {
-      parseerrprint("unable to open file `%s' in included directory `%s': %s",
-                   de->d_name,cp,strerror(errno));
-      closedir(d);
-      return tokv_error;
+      r= parseerrprint("unable to open file `%s' in included directory `%s': %s",
+                      de->d_name,cp,strerror(errno));
+      goto x_err;
     }
   }
   if (closedir(d)) {
     parseerrprint("error closing directory `%s': %s",cp,strerror(errno));
+    free(cp);
     return tokv_error;
   }
+  free(cp);
   return 0;
+
+x_err:
+  closedir(d);
+  free(cp);
+  return r;
 }
 
 int df_includelookup(int dtoken) {
   static char *buildbuf=0;
   int buildbuflen=0;
   
-  char **parmvalues, **pp, *p, *q;
-  const char *cp;
+  char **parmvalues, **pp, *p, *q, *cp;
+  const char *cpget;
   struct stat stab;
   int r, done, thisdone, cpl, c;
 
   r= pa_mwsp(); if (r) return r;
   r= pa_parameter(&parmvalues,0); if (r) return r;
-  r= paa_1path(&cp); if (r) { freecharparray(parmvalues); return r; }
-  if (stat(cp,&stab)) {
-    parseerrprint("unable to access directory `%s': %s",cp,strerror(errno));
+  r= paa_1path(&cpget); if (r) { freecharparray(parmvalues); return r; }
+  if (stat(cpget,&stab)) {
+    parseerrprint("unable to access directory `%s': %s",cpget,strerror(errno));
     freecharparray(parmvalues); return tokv_error;
   }
   if (!S_ISDIR(stab.st_mode)) {
-    parseerrprint("object `%s' is not a directory or link to one",cp);
+    parseerrprint("object `%s' is not a directory or link to one",cpget);
     freecharparray(parmvalues); return tokv_error;
   }
   done= 0;
+  cp= xstrsave(cpget);
   cpl= strlen(cp);
   if (!parmvalues[0]) {
-    if (makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP)))
-      return stringoverflow("pathname in directory for lookup of undefined parameter");
+    if (makeroom(&buildbuf,&buildbuflen,cpl+1+sizeof(NONEINCLUDELOOKUP))) {
+      stringoverflow("pathname in directory for lookup of undefined parameter");
+      r= tokv_error; goto x_err;
+    }
     snyprintf(buildbuf,buildbuflen,"%s/" NONEINCLUDELOOKUP,cp);
-    r= parse_file(buildbuf,&thisdone);
-    if (r) { freecharparray(parmvalues); return r; }
+    r= parse_file(buildbuf,&thisdone); if (r) goto x_err;
     if (thisdone) done= 1;
   } else {
     for (pp=parmvalues;
         *pp && (!done || dtoken == tokv_word_includelookupall);
         pp++) {
       if (makeroom(&buildbuf,&buildbuflen,
-                  cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1))
-       return stringoverflow("pathname in directory for lookup");
+                  cpl+1+strlen(*pp)*2+3+sizeof(EMPTYINCLUDELOOKUP)+1)) {
+       stringoverflow("pathname in directory for lookup");
+       r= tokv_error; goto x_err;
+      }
       strcpy(buildbuf,cp);
       p= *pp; q= buildbuf+cpl;
       *q++= '/';
@@ -1064,26 +1076,38 @@ int df_includelookup(int dtoken) {
       } else {
        if (*p=='.') *q++= ':';
        while ((c= *p++)) {
-         if (c=='/') { *q++= ':'; c='-'; }
-         else if (c==':') { *q++= ':'; }
+         if (c=='/') {
+           *q++= ':';
+           c= '-';
+         } else if (!((c >= '0' && c <= '9') ||
+                      (c >= 'a' && c <= 'z') ||
+                      c == '-' || c == '_')) {
+           *q++= ':';
+         }
          *q++= c;
        }
        *q++= 0;
       }
       r= parse_file(buildbuf,&thisdone);
-      if (r) { freecharparray(parmvalues); return r; }
+      if (r) goto x_err;
       if (thisdone) done= 1;
     }
   }
-  freecharparray(parmvalues);
   if (!done) {
     if (makeroom(&buildbuf,&buildbuflen,
-                cpl+1+sizeof(DEFAULTINCLUDELOOKUP)))
-      return stringoverflow("pathname in directory for lookup of default");
+                cpl+1+sizeof(DEFAULTINCLUDELOOKUP))) {
+      stringoverflow("pathname in directory for lookup of default");
+      r= tokv_error; goto x_err;
+    }
     snyprintf(buildbuf,buildbuflen,"%s/" DEFAULTINCLUDELOOKUP,cp);
-    r= parse_file(buildbuf,0); if (r) return r;
+    r= parse_file(buildbuf,0); if (r) goto x_err;
   }
-  return 0;
+  r= 0;
+  
+x_err:
+  freecharparray(parmvalues);
+  free(cp);
+  return r;
 }
 
 /* Control constructs */
@@ -1158,8 +1182,7 @@ int df_cd(int dtoken) {
 
   r= paa_1path(&cp); if (r) return r;
   if (!chdir(cp)) return 0;
-  parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno));
-  return tokv_error;
+  return parseerrprint("unable to change directory to `%s': %s",cp,strerror(errno));
 }
 
 int df_userrcfile(int dtoken) {
@@ -1185,8 +1208,7 @@ int df_error(int dtoken) {
   int r;
 
   r= paa_message(&mp); if (r) return r;
-  parseerrprint("`error' directive: %s",mp);
-  return tokv_error;
+  return parseerrprint("`error' directive: %s",mp);
 }
 
 int df_eof(int dtoken) {
@@ -1272,27 +1294,23 @@ static int parse_file(const char *string, int *didexist) {
   char *filename;
   struct stat newstab;
 
-  if (fileparselevel >= MAX_INCLUDE_NEST) {
-    parseerrprint("too many nested levels of included files");
-    return tokv_error;
-  }
+  if (fileparselevel >= MAX_INCLUDE_NEST)
+    return parseerrprint("too many nested levels of included files");
   file= fopen(string,"r");
   if (!file) {
     if (errno == ENOENT) {
       if (didexist) *didexist= 0;
       return 0;
     }
-    parseerrprint("unable to open config file `%s': %s",string,strerror(errno));
-    return tokv_error;
+    return parseerrprint("unable to open config file `%s': %s",string,strerror(errno));
   }
   r= fstat(fileno(file),&newstab); if (r) syscallerror("unable to fstat new file");
   for (checkrecurse= cstate; checkrecurse; checkrecurse= checkrecurse->upstate) {
     if (!checkrecurse->filestab.st_mode) continue;
     if (newstab.st_dev==checkrecurse->filestab.st_dev &&
        newstab.st_ino==checkrecurse->filestab.st_ino) {
-      parseerrprint("recursion detected - config file `%s' calls itself",string);
       fclose(file);
-      return tokv_error;
+      return parseerrprint("recursion detected - config file `%s' calls itself",string);
     }
   }
   
@@ -1305,10 +1323,8 @@ static int parse_file(const char *string, int *didexist) {
   fileparselevel++;
   
   r= parser(0);
-  if (ferror(file)) {
-    parseerrprint("error reading configuration file `%s'",string);
-    r= tokv_error;
-  }
+  if (ferror(file))
+    r= parseerrprint("error reading configuration file `%s'",string);
 
   fileparselevel--;
   parser_pop();