m4: Sync with FreeBSD.
[dragonfly.git] / usr.bin / m4 / main.c
index 7ef646b..969c206 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: main.c,v 1.53 2002/04/26 16:15:16 espie Exp $ */
+/*     $OpenBSD: main.c,v 1.80 2011/09/27 07:24:02 espie Exp $ */
 /*     $NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $    */
 
 /*-
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * @(#) Copyright (c) 1989, 1993 The Regents of the University of California.  All rights reserved.
- * @(#)main.c  8.1 (Berkeley) 6/6/93
- * $OpenBSD: main.c,v 1.53 2002/04/26 16:15:16 espie Exp $
- * $FreeBSD: src/usr.bin/m4/main.c,v 1.26 2004/08/16 14:18:22 tjr Exp $
- * $DragonFly: src/usr.bin/m4/main.c,v 1.4 2007/01/01 00:41:58 pavalos Exp $
+ * $FreeBSD: src/usr.bin/m4/main.c,v 1.33 2012/11/17 01:54:24 svnexp Exp $
  */
 
 /*
  * by: oz
  */
 
-#include <sys/types.h>
 #include <assert.h>
 #include <signal.h>
+#include <err.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <string.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
-#include <err.h>
-#include <locale.h>
+#include <ohash.h>
 #include "mdef.h"
 #include "stdd.h"
 #include "extern.h"
 #include "pathnames.h"
 
-ndptr hashtab[HASHSIZE];       /* hash table for macros etc.  */
-stae *mstack;                  /* stack of m4 machine         */
-char *sstack;                  /* shadow stack, for string space extension */
+stae *mstack;                  /* stack of m4 machine */
+char *sstack;                  /* shadow stack, for string space extension */
 static size_t STACKMAX;                /* current maximum size of stack */
-int sp;                        /* current m4  stack pointer   */
-int fp;                        /* m4 call frame pointer       */
-struct input_file infile[MAXINP];/* input file stack (0=stdin)  */
-char *inname[MAXINP];          /* names of these input files */
-int inlineno[MAXINP];          /* current number in each input file */
-FILE **outfile;                        /* diversion array(0=bitbucket)*/
+int sp;                                /* current m4 stack pointer */
+int fp;                                /* m4 call frame pointer */
+struct input_file infile[MAXINP];/* input file stack (0=stdin) */
+FILE **outfile;                        /* diversion array(0=bitbucket) */
 int maxout;
-FILE *active;                  /* active output file pointer  */
-int ilevel = 0;                /* input file stack pointer    */
-int oindex = 0;                /* diversion index..           */
-char null[] = "";              /* as it says.. just a null..  */
-const char *m4wraps = "";       /* m4wrap string default..     */
-int m4prefix = 0;              /* prefix keywords with m4_    */
-char lquote[MAXCCHARS+1] = {LQUOTE};   /* left quote character  (`)   */
-char rquote[MAXCCHARS+1] = {RQUOTE};   /* right quote character (')   */
-char scommt[MAXCCHARS+1] = {SCOMMT};   /* start character for comment */
-char ecommt[MAXCCHARS+1] = {ECOMMT};   /* end character for comment   */
-int synccpp;                   /* Line synchronisation for C preprocessor */
-
-struct keyblk keywrds[] = {    /* m4 keywords to be installed */
-       { "include",      INCLTYPE },
-       { "sinclude",     SINCTYPE },
-       { "define",       DEFITYPE },
-       { "defn",         DEFNTYPE },
-       { "divert",       DIVRTYPE | NOARGS },
-       { "expr",         EXPRTYPE },
-       { "eval",         EXPRTYPE },
-       { "substr",       SUBSTYPE },
-       { "ifelse",       IFELTYPE },
-       { "ifdef",        IFDFTYPE },
-       { "len",          LENGTYPE },
-       { "incr",         INCRTYPE },
-       { "decr",         DECRTYPE },
-       { "dnl",          DNLNTYPE | NOARGS },
-       { "changequote",  CHNQTYPE | NOARGS },
-       { "changecom",    CHNCTYPE | NOARGS },
-       { "index",        INDXTYPE },
+FILE *active;                  /* active output file pointer */
+int ilevel = 0;                        /* input file stack pointer */
+int oindex = 0;                        /* diversion index.. */
+const char *null = "";         /* as it says.. just a null.. */
+char **m4wraps = NULL;         /* m4wraps array. */
+int maxwraps = 0;              /* size of m4wraps array */
+int wrapindex = 0;             /* current offset in m4wraps */
+char lquote[MAXCCHARS + 1] = {LQUOTE}; /* left quote character  (`) */
+char rquote[MAXCCHARS + 1] = {RQUOTE}; /* right quote character (') */
+char scommt[MAXCCHARS + 1] = {SCOMMT}; /* start character for comment */
+char ecommt[MAXCCHARS + 1] = {ECOMMT}; /* end character for comment */
+int synch_lines = 0;           /* line synchronisation for C preprocessor */
+int prefix_builtins = 0;       /* -P option to prefix builtin keywords */
+
+struct keyblk {
+       const char      *knam;  /* keyword name */
+       int             ktyp;   /* keyword type */
+};
+
+static struct keyblk keywrds[] = {     /* m4 keywords to be installed */
+       { "include",    INCLTYPE },
+       { "sinclude",   SINCTYPE },
+       { "define",     DEFITYPE },
+       { "defn",       DEFNTYPE },
+       { "divert",     DIVRTYPE | NOARGS },
+       { "expr",       EXPRTYPE },
+       { "eval",       EXPRTYPE },
+       { "substr",     SUBSTYPE },
+       { "ifelse",     IFELTYPE },
+       { "ifdef",      IFDFTYPE },
+       { "len",        LENGTYPE },
+       { "incr",       INCRTYPE },
+       { "decr",       DECRTYPE },
+       { "dnl",        DNLNTYPE | NOARGS },
+       { "changequote",CHNQTYPE | NOARGS },
+       { "changecom",  CHNCTYPE | NOARGS },
+       { "index",      INDXTYPE },
 #ifdef EXTENDED
-       { "paste",        PASTTYPE },
-       { "spaste",       SPASTYPE },
-       /* Newer extensions, needed to handle gnu-m4 scripts */
-       { "indir",        INDIRTYPE},
-       { "builtin",      BUILTINTYPE},
-       { "patsubst",     PATSTYPE},
-       { "regexp",       REGEXPTYPE},
-       { "esyscmd",      ESYSCMDTYPE},
-       { "__file__",     FILENAMETYPE | NOARGS},
-       { "__line__",     LINETYPE | NOARGS},
-#endif
-       { "popdef",       POPDTYPE },
-       { "pushdef",      PUSDTYPE },
-       { "dumpdef",      DUMPTYPE | NOARGS },
-       { "shift",        SHIFTYPE | NOARGS },
-       { "translit",     TRNLTYPE },
-       { "undefine",     UNDFTYPE },
-       { "undivert",     UNDVTYPE | NOARGS },
-       { "divnum",       DIVNTYPE | NOARGS },
-       { "maketemp",     MKTMTYPE },
-       { "errprint",     ERRPTYPE | NOARGS },
-       { "m4wrap",       M4WRTYPE | NOARGS },
-       { "m4exit",       EXITTYPE | NOARGS },
-       { "syscmd",       SYSCTYPE },
-       { "sysval",       SYSVTYPE | NOARGS },
-       { "traceon",      TRACEONTYPE | NOARGS },
-       { "traceoff",     TRACEOFFTYPE | NOARGS },
-
-#if defined(unix) || defined(__unix__)
-       { "unix",         SELFTYPE | NOARGS },
-#else
-#ifdef vms
-       { "vms",          SELFTYPE | NOARGS },
-#endif
+       { "paste",      PASTTYPE },
+       { "spaste",     SPASTYPE },
+       /* Newer extensions, needed to handle gnu-m4 scripts */
+       { "indir",      INDIRTYPE},
+       { "builtin",    BUILTINTYPE},
+       { "patsubst",   PATSTYPE},
+       { "regexp",     REGEXPTYPE},
+       { "esyscmd",    ESYSCMDTYPE},
+       { "__file__",   FILENAMETYPE | NOARGS},
+       { "__line__",   LINETYPE | NOARGS},
 #endif
+       { "popdef",     POPDTYPE },
+       { "pushdef",    PUSDTYPE },
+       { "dumpdef",    DUMPTYPE | NOARGS },
+       { "shift",      SHIFTYPE | NOARGS },
+       { "translit",   TRNLTYPE },
+       { "undefine",   UNDFTYPE },
+       { "undivert",   UNDVTYPE | NOARGS },
+       { "divnum",     DIVNTYPE | NOARGS },
+       { "maketemp",   MKTMTYPE },
+       { "mkstemp",    MKTMTYPE },
+       { "errprint",   ERRPTYPE | NOARGS },
+       { "m4wrap",     M4WRTYPE | NOARGS },
+       { "m4exit",     EXITTYPE | NOARGS },
+       { "syscmd",     SYSCTYPE },
+       { "sysval",     SYSVTYPE | NOARGS },
+       { "traceon",    TRACEONTYPE | NOARGS },
+       { "traceoff",   TRACEOFFTYPE | NOARGS },
+       { "unix",       SELFTYPE | NOARGS },
 };
 
-#define MAXKEYS        (sizeof(keywrds)/sizeof(struct keyblk))
+#define        MAXKEYS (sizeof(keywrds) / sizeof(struct keyblk))
 
-#define MAXRECORD 50
+#define        MAXRECORD 50
 static struct position {
        char *name;
        unsigned long line;
@@ -160,52 +150,42 @@ static void macro(void);
 static void initkwds(void);
 static ndptr inspect(int, char *);
 static int do_look_ahead(int, const char *);
+static void reallyoutputstr(const char *);
+static void reallyputchar(int);
 
 static void enlarge_stack(void);
 
+int exit_code = 0;
+
 int
 main(int argc, char *argv[])
 {
        int c;
        int n;
-       int rval;
-       const char *optstr = "D:I:PU:gd:o:st:";
        char *p;
 
-       setlocale(LC_ALL, "");
-
-       traceout = stderr;
-
        if (signal(SIGINT, SIG_IGN) != SIG_IGN)
                signal(SIGINT, onintr);
 
-       /*
-        * We need to know if -P is there before checking -D and -U.
-        */
-       while ((c = getopt(argc, argv, optstr)) != -1)
-               if (c == 'P')
-                       m4prefix = 1;
-       optind = optreset = 1;
-
-       initkwds();
+       init_macros();
        initspaces();
        STACKMAX = INITSTACKMAX;
 
-       mstack = (stae *)xalloc(sizeof(stae) * STACKMAX);
-       sstack = (char *)xalloc(STACKMAX);
+       mstack = (stae *)xalloc(sizeof(stae) * STACKMAX, NULL);
+       sstack = (char *)xalloc(STACKMAX, NULL);
 
        maxout = 0;
        outfile = NULL;
        resizedivs(MAXOUT);
 
-       while ((c = getopt(argc, argv, optstr)) != -1)
+       while ((c = getopt(argc, argv, "gst:d:D:U:o:I:P")) != -1)
                switch(c) {
-               case 'D':               /* define something..*/
-                       for (p = optarg; *p; p++)
+
+               case 'D':       /* define something.. */
+                       for (p = optarg; *p; p++) {
                                if (*p == '=')
                                        break;
-                       if (p == optarg)
-                               errx(1, "null variable cannot be defined");
+                       }
                        if (*p)
                                *p++ = EOS;
                        dodefine(optarg, p);
@@ -214,9 +194,10 @@ main(int argc, char *argv[])
                        addtoincludepath(optarg);
                        break;
                case 'P':
+                       prefix_builtins = 1;
                        break;
-               case 'U':               /* undefine...       */
-                       remhash(optarg, TOP);
+               case 'U':       /* undefine... */
+                       macro_popdef(optarg);
                        break;
                case 'g':
                        mimic_gnu = 1;
@@ -225,73 +206,78 @@ main(int argc, char *argv[])
                        set_trace_flags(optarg);
                        break;
                case 's':
-                       synccpp = 1;
+                       synch_lines = 1;
                        break;
                case 't':
                        mark_traced(optarg, 1);
                        break;
                case 'o':
                        trace_file(optarg);
-                        break;
+                       break;
                case '?':
-               default:
                        usage();
                }
 
-        argc -= optind;
-        argv += optind;
+       argc -= optind;
+       argv += optind;
+
+       initkwds();
+       if (mimic_gnu)
+               setup_builtin("format", FORMATTYPE);
 
-       rval = 0;
        active = stdout;                /* default active output     */
        bbase[0] = bufbase;
-        if (!argc) {
-               sp = -1;                /* stack pointer initialized */
-               fp = 0;                 /* frame pointer initialized */
-               set_input(infile+0, stdin, "stdin");
+       if (argc == 0) {
+               sp = -1;                /* stack pointer initialized */
+               fp = 0;                 /* frame pointer initialized */
+               set_input(infile + 0, stdin, "stdin");
                                        /* default input (naturally) */
-               if ((inname[0] = strdup("-")) == NULL)
-                       err(1, NULL);
-               inlineno[0] = 1;
-               emitline();
                macro();
-       } else
+       } else {
                for (; argc--; ++argv) {
                        p = *argv;
                        if (p[0] == '-' && p[1] == EOS)
                                set_input(infile, stdin, "stdin");
-                       else if (fopen_trypath(infile, p) == NULL) {
-                               warn("%s", p);
-                               rval = 1;
-                               continue;
-                       }
+                       else if (fopen_trypath(infile, p) == NULL)
+                               err(1, "%s", p);
                        sp = -1;
                        fp = 0;
-                       if ((inname[0] = strdup(p)) == NULL)
-                               err(1, NULL);
-                       inlineno[0] = 1;
-                       emitline();
                        macro();
-                       release_input(infile);
+                       release_input(infile);
                }
+       }
+
+       if (wrapindex) {
+               int i;
 
-       if (*m4wraps) {                 /* anything for rundown ??   */
                ilevel = 0;             /* in case m4wrap includes.. */
                bufbase = bp = buf;     /* use the entire buffer   */
-               pbstr(m4wraps);         /* user-defined wrapup act   */
-               macro();                /* last will and testament   */
+               if (mimic_gnu) {
+                       while (wrapindex != 0) {
+                               for (i = 0; i < wrapindex; i++)
+                                       pbstr(m4wraps[i]);
+                               wrapindex =0;
+                               macro();
+                       }
+               } else {
+                       for (i = 0; i < wrapindex; i++) {
+                               pbstr(m4wraps[i]);
+                               macro();
+                       }
+               }
        }
 
        if (active != stdout)
                active = stdout;        /* reset output just in case */
-       for (n = 1; n < maxout; n++)    /* default wrap-up: undivert */
+       for (n = 1; n < maxout; n++) {  /* default wrap-up: undivert */
                if (outfile[n] != NULL)
                        getdiv(n);
-                                       /* remove bitbucket if used  */
-       if (outfile[0] != NULL) {
-               (void) fclose(outfile[0]);
        }
+       /* remove bitbucket if used */
+       if (outfile[0] != NULL)
+               fclose(outfile[0]);
 
-       exit(rval);
+       return exit_code;
 }
 
 /*
@@ -299,7 +285,7 @@ main(int argc, char *argv[])
  * (on input `t == token[0]')
  * Used for comment and quoting delimiters.
  * Returns 1 if `token' present; copied to output.
- *         0 if `token' not found; all characters pushed back
+ *        0 if `token' not found; all characters pushed back
  */
 static int
 do_look_ahead(int t, const char *token)
@@ -311,18 +297,18 @@ do_look_ahead(int t, const char *token)
        for (i = 1; *++token; i++) {
                t = gpbc();
                if (t == EOF || (unsigned char)t != (unsigned char)*token) {
-                       putback(t);
+                       pushback(t);
                        while (--i)
-                               putback(*--token);
+                               pushback(*--token);
                        return 0;
                }
        }
        return 1;
 }
 
-#define LOOK_AHEAD(t, token) (t != EOF &&              \
-    (unsigned char)(t)==(unsigned char)(token)[0] &&   \
-    do_look_ahead(t,token))
+#define        LOOK_AHEAD(t, token) (t != EOF &&               \
+    (unsigned char)(t) == (unsigned char)(token)[0] && \
+    do_look_ahead(t, token))
 
 /*
  * macro - the work horse..
@@ -330,68 +316,15 @@ do_look_ahead(int t, const char *token)
 static void
 macro(void)
 {
-       char token[MAXTOK+1];
+       char token[MAXTOK + 1];
        int t, l;
        ndptr p;
-       int  nlpar;
+       int nlpar;
 
-       cycle {
+       for (;;) {
                t = gpbc();
-               if (t == '_' || isalpha(t)) {
-                       p = inspect(t, token);
-                       if (p != nil)
-                               putback(l = gpbc());
-                       if (p == nil || (l != LPAREN &&
-                           (p->type & NEEDARGS) != 0))
-                               outputstr(token);
-                       else {
-               /*
-                * real thing.. First build a call frame:
-                */
-                               pushf(fp);      /* previous call frm */
-                               pushf(p->type); /* type of the call  */
-                               pushf(0);       /* parenthesis level */
-                               fp = sp;        /* new frame pointer */
-               /*
-                * now push the string arguments:
-                */
-                               pushs1(p->defn);        /* defn string */
-                               pushs1(p->name);        /* macro name  */
-                               pushs(ep);              /* start next..*/
-
-                               if (l != LPAREN && PARLEV == 0)  {
-                                   /* no bracks  */
-                                       chrsave(EOS);
-
-                                       if ((uintptr_t)sp == STACKMAX)
-                                               errx(1, "internal stack overflow");
-                                       eval((const char **) mstack+fp+1, 2,
-                                           CALTYP);
 
-                                       ep = PREVEP;    /* flush strspace */
-                                       sp = PREVSP;    /* previous sp..  */
-                                       fp = PREVFP;    /* rewind stack...*/
-                               }
-                       }
-               } else if (t == EOF) {
-                       if (sp > -1) {
-                               warnx( "unexpected end of input, unclosed parenthesis:");
-                               dump_stack(paren, PARLEV);
-                               exit(1);
-                       }
-                       if (ilevel <= 0)
-                               break;                  /* all done thanks.. */
-                       release_input(infile+ilevel--);
-                       free(inname[ilevel+1]);
-                       bufbase = bbase[ilevel];
-                       emitline();
-                       continue;
-               }
-       /*
-        * non-alpha token possibly seen..
-        * [the order of else if .. stmts is important.]
-        */
-               else if (LOOK_AHEAD(t,lquote)) {        /* strip quotes */
+               if (LOOK_AHEAD(t, lquote)) {    /* strip quotes */
                        nlpar = 0;
                        record(quotes, nlpar++);
                        /*
@@ -399,7 +332,6 @@ macro(void)
                         * closing quote has been found.
                         */
                        do {
-
                                l = gpbc();
                                if (LOOK_AHEAD(l,rquote)) {
                                        if (--nlpar > 0)
@@ -417,32 +349,80 @@ macro(void)
                                } else {
                                        if (nlpar > 0) {
                                                if (sp < 0)
-                                                       putc(l, active);
+                                                       reallyputchar(l);
                                                else
                                                        CHRSAVE(l);
                                        }
                                }
                        }
                        while (nlpar != 0);
-               }
-
-               else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
-                       fputs(scommt, active);
+               } else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
+                       reallyoutputstr(scommt);
 
                        for(;;) {
                                t = gpbc();
                                if (LOOK_AHEAD(t, ecommt)) {
-                                       fputs(ecommt, active);
+                                       reallyoutputstr(ecommt);
                                        break;
                                }
                                if (t == EOF)
                                        break;
-                               putc(t, active);
+                               reallyputchar(t);
                        }
-               }
+               } else if (t == '_' || isalpha(t)) {
+                       p = inspect(t, token);
+                       if (p != NULL)
+                               pushback(l = gpbc());
+                       if (p == NULL || (l != LPAREN &&
+                           (macro_getdef(p)->type & NEEDARGS) != 0))
+                               outputstr(token);
+                       else {
+                               /*
+                                * real thing.. First build a call frame:
+                                */
+                               pushf(fp);      /* previous call frm */
+                               /* type of the call */
+                               pushf(macro_getdef(p)->type);
+                               pushf(is_traced(p));
+                               pushf(0);       /* parenthesis level */
+                               fp = sp;        /* new frame pointer */
+                               /*
+                                * now push the string arguments:
+                                */
+                               pushs1(macro_getdef(p)->defn);  /* defn string */
+                               pushs1((char *)macro_name(p));  /* macro name  */
+                               pushs(ep);                      /* start next..*/
+
+                               if (l != LPAREN && PARLEV == 0) {
+                                       /* no bracks  */
+                                       chrsave(EOS);
 
-               else if (sp < 0) {              /* not in a macro at all */
-                       putc(t, active);        /* output directly..     */
+                                       if (sp == (int)STACKMAX) {
+                                               errx(1,
+                                                   "internal stack overflow");
+                                       }
+                                       eval((const char **) mstack + fp + 1, 2,
+                                           CALTYP, TRACESTATUS);
+
+                                       ep = PREVEP;    /* flush strspace */
+                                       sp = PREVSP;    /* previous sp..  */
+                                       fp = PREVFP;    /* rewind stack...*/
+                               }
+                       }
+               } else if (t == EOF) {
+                       if (sp > -1 && ilevel <= 0) {
+                               warnx( "unexpected end of input, unclosed parenthesis:");
+                               dump_stack(paren, PARLEV);
+                               exit(1);
+                       }
+                       if (ilevel <= 0)
+                               break;                  /* all done thanks.. */
+                       release_input(infile+ilevel--);
+                       emit_synchline();
+                       bufbase = bbase[ilevel];
+                       continue;
+               } else if (sp < 0) {            /* not in a macro at all */
+                       reallyputchar(t);       /* output directly..     */
                }
 
                else switch(t) {
@@ -450,9 +430,10 @@ macro(void)
                case LPAREN:
                        if (PARLEV > 0)
                                chrsave(t);
-                       while (isspace(l = gpbc()))
-                               ;               /* skip blank, tab, nl.. */
-                       putback(l);
+                       while (isspace(l = gpbc())) /* skip blank, tab, nl.. */
+                               if (PARLEV > 0)
+                                       chrsave(l);
+                       pushback(l);
                        record(paren, PARLEV++);
                        break;
 
@@ -462,11 +443,11 @@ macro(void)
                        else {                  /* end of argument list */
                                chrsave(EOS);
 
-                               if ((uintptr_t)sp == STACKMAX)
+                               if (sp == (int)STACKMAX)
                                        errx(1, "internal stack overflow");
 
-                               eval((const char **) mstack+fp+1, sp-fp,
-                                   CALTYP);
+                               eval((const char **) mstack + fp + 1, sp-fp,
+                                   CALTYP, TRACESTATUS);
 
                                ep = PREVEP;    /* flush strspace */
                                sp = PREVSP;    /* previous sp..  */
@@ -479,7 +460,7 @@ macro(void)
                                chrsave(EOS);           /* new argument   */
                                while (isspace(l = gpbc()))
                                        ;
-                               putback(l);
+                               pushback(l);
                                pushs(ep);
                        } else
                                chrsave(t);
@@ -487,18 +468,18 @@ macro(void)
 
                default:
                        if (LOOK_AHEAD(t, scommt)) {
-                               char *pc;
-                               for (pc = scommt; *pc; pc++)
-                                       chrsave(*pc);
+                               char *cp;
+                               for (cp = scommt; *cp; cp++)
+                                       chrsave(*cp);
                                for(;;) {
                                        t = gpbc();
                                        if (LOOK_AHEAD(t, ecommt)) {
-                                               for (pc = ecommt; *pc; pc++)
-                                                       chrsave(*pc);
+                                               for (cp = ecommt; *cp; cp++)
+                                                       chrsave(*cp);
                                                break;
                                        }
                                        if (t == EOF)
-                                           break;
+                                               break;
                                        CHRSAVE(t);
                                }
                        } else
@@ -515,32 +496,57 @@ void
 outputstr(const char *s)
 {
        if (sp < 0)
-               while (*s)
-                       putc(*s++, active);
+               reallyoutputstr(s);
        else
                while (*s)
                        CHRSAVE(*s++);
 }
 
+void
+reallyoutputstr(const char *s)
+{
+       if (synch_lines) {
+               while (*s) {
+                       fputc(*s, active);
+                       if (*s++ == '\n') {
+                               infile[ilevel].synch_lineno++;
+                               if (infile[ilevel].synch_lineno !=
+                                   infile[ilevel].lineno)
+                                       do_emit_synchline();
+                       }
+               }
+       } else
+               fputs(s, active);
+}
+
+void
+reallyputchar(int c)
+{
+       putc(c, active);
+       if (synch_lines && c == '\n') {
+               infile[ilevel].synch_lineno++;
+               if (infile[ilevel].synch_lineno != infile[ilevel].lineno)
+                       do_emit_synchline();
+       }
+}
+
 /*
  * build an input token..
- * consider only those starting with _ or A-Za-z. This is a
- * combo with lookup to speed things up.
+ * consider only those starting with _ or A-Za-z.
  */
 static ndptr
 inspect(int c, char *tp)
 {
        char *name = tp;
-       char *etp = tp+MAXTOK;
+       char *etp = tp + MAXTOK;
        ndptr p;
-       unsigned int h;
 
-       h = *tp++ = c;
+       *tp++ = c;
 
        while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
-               h = (h << 5) + h + (*tp++ = c);
+               *tp++ = c;
        if (c != EOF)
-               PUTBACK(c);
+               PUSHBACK(c);
        *tp = EOS;
        /* token is too long, it won't match anything, but it can still
         * be output. */
@@ -548,22 +554,24 @@ inspect(int c, char *tp)
                outputstr(name);
                while (isalnum(c = gpbc()) || c == '_') {
                        if (sp < 0)
-                               putc(c, active);
+                               reallyputchar(c);
                        else
                                CHRSAVE(c);
                }
                *name = EOS;
-               return nil;
+               return NULL;
        }
 
-       for (p = hashtab[h % HASHSIZE]; p != nil; p = p->nxtptr)
-               if (h == p->hv && STREQ(name, p->name))
-                       break;
+       p = ohash_find(&macros, ohash_qlookupi(&macros, name, (const char **)&tp));
+       if (p == NULL)
+               return NULL;
+       if (macro_getdef(p) == NULL)
+               return NULL;
        return p;
 }
 
 /*
- * initkwds - initialise m4 keywords as fast as possible.
+ * initkwds - initialize m4 keywords as fast as possible.
  * This very similar to install, but without certain overheads,
  * such as calling lookup. Malloc is not used for storing the
  * keyword strings, since we simply use the static pointers
@@ -572,52 +580,15 @@ inspect(int c, char *tp)
 static void
 initkwds(void)
 {
-       size_t i;
-       unsigned int h;
-       ndptr p;
-       char *k;
-
-       for (i = 0; i < MAXKEYS; i++) {
-               k = __DECONST(char *, keywrds[i].knam);
-               if (m4prefix) {
-                       if (asprintf(&k, "m4_%s", k) == -1)
-                               err(1, "asprintf");
-                       keywrds[i].knam = k;
-               }
-               h = hash(k);
-               p = (ndptr) xalloc(sizeof(struct ndblock));
-               p->nxtptr = hashtab[h % HASHSIZE];
-               hashtab[h % HASHSIZE] = p;
-               p->name = xstrdup(keywrds[i].knam);
-               p->defn = null;
-               p->hv = h;
-               p->type = keywrds[i].ktyp & TYPEMASK;
-               if ((keywrds[i].ktyp & NOARGS) == 0)
-                       p->type |= NEEDARGS;
-       }
-}
-
-/* Look up a builtin type, even if overridden by the user */
-int
-builtin_type(const char *key)
-{
+       unsigned int type;
        int i;
 
-       for (i = 0; i != MAXKEYS; i++)
-               if (STREQ(keywrds[i].knam, key))
-                       return keywrds[i].ktyp;
-       return -1;
-}
-
-const char *
-builtin_realname(int n)
-{
-       int i;
-
-       for (i = 0; i != MAXKEYS; i++)
-               if (((keywrds[i].ktyp ^ n) & TYPEMASK) == 0)
-                       return keywrds[i].knam;
-       return NULL;
+       for (i = 0; i < (int)MAXKEYS; i++) {
+               type = keywrds[i].ktyp & TYPEMASK;
+               if ((keywrds[i].ktyp & NOARGS) == 0)
+                       type |= NEEDARGS;
+               setup_builtin(keywrds[i].knam, type);
+       }
 }
 
 static void
@@ -648,20 +619,11 @@ dump_stack(struct position *t, int lev)
 static void
 enlarge_stack(void)
 {
-       STACKMAX *= 2;
-       mstack = realloc(mstack, sizeof(stae) * STACKMAX);
-       sstack = realloc(sstack, STACKMAX);
-       if (mstack == NULL || sstack == NULL)
-               errx(1, "Evaluation stack overflow (%lu)",
-                   (unsigned long)STACKMAX);
-}
-
-/* Emit preprocessor #line directive if -s option used. */
-void
-emitline(void)
-{
-
-       if (synccpp)
-               fprintf(active, "#line %d \"%s\"\n", inlineno[ilevel],
-                       inname[ilevel]);
+       STACKMAX += STACKMAX/2;
+       mstack = xrealloc(mstack, sizeof(stae) * STACKMAX,
+           "Evaluation stack overflow (%lu)",
+           (unsigned long)STACKMAX);
+       sstack = xrealloc(sstack, STACKMAX,
+           "Evaluation stack overflow (%lu)",
+           (unsigned long)STACKMAX);
 }