Print this page
cpp: if a macro with parameters is invoked without any, don't destroy state
Previously, we would parse a macro foo() presuming that we would always
see the opening parenthesis indicating the begining of the parameter
list.  If we saw 'foo' we would consume the token _following_ 'foo'
presuming it would be the parenthesis, and if it was not, would not
paste it to the output.

@@ -74,10 +74,11 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <string.h>
 #include <stdarg.h>
+#include <ctype.h>
 
 #include "cpp.h"
 
 #define SYMLEN  128
 static  int symlen = SYMLEN;

@@ -634,10 +635,15 @@
         
         if (isslo) return(p);
 } /* end of infinite loop */
 }
 
+/*
+ * XXX: This unconditionally consumes one token (presuming it's blank? that we
+ * already consumed it?).  That's pretty terrible, but it's also very fragile,
+ * and I don't want to change it.
+ */
 char *
 skipbl(p) register char *p; {/* get next non-blank token */
         do {
                 outp=inp=p;
                 p=cotoken(p);

@@ -1191,17 +1197,30 @@
                 vp=acttxt; *vp++='\0';
                 sprintf(vp,"\"%s\"",fnames[ifno]); while (*vp++);
         }
         if (0!=(params= *--vp&0xFF)) {/* definition calls for params */
                 register char **pa;
+                char *savp, *savinp, *savoutp;
                 ca=acttxt; pa=actual;
                 if (params==0xFF)
                         params=1;       /* #define foo() ... */
                 sloscan();
                 ++flslvl; /* no expansion during search for actuals */
                 plvl= -1;
-                do p=skipbl(p); while (*inp=='\n');     /* skip \n too */
+                /*
+                 * Skip any blanks (including \n), until we hit the macro
+                 * arguments.
+                 *
+                 * save our state so we can roll back if none are called.
+                 */
+                savp = p;
+                savinp = inp;
+                savoutp = outp;
+                do {
+                        p=skipbl(p);
+                } while (*inp=='\n');   /* skip \n too */
+
                 if (*inp=='(') {
                         maclin=lineno[ifno]; macfil=fnames[ifno];
                         for (plvl=1; plvl!=0; ) {
                                 *ca++='\0';
                                 for (;;) {

@@ -1253,10 +1272,18 @@
                                         ppwarn("%s: argument mismatch" ,
                                             sp->name);
                                 else
                                         *pa++=ca;
                         }
+                } else {
+                        /*
+                         * Didn't find any parameters, rollback our state so
+                         * we don't chew more output than necessary
+                         */
+                        p = savp;
+                        inp = savinp;
+                        outp = savoutp;
                 }
                 if (params!=0)
                         ppwarn("%s: argument mismatch", sp->name);
                 while (--params>=0)
                         *pa++=""+1;     /* null string for missing actuals */