Index: trace.c
===================================================================
--- trace.c	(revision 198625)
+++ trace.c	(working copy)
@@ -1,4 +1,4 @@
-/* $OpenBSD: trace.c,v 1.6 2002/04/26 16:15:16 espie Exp $ */
+/* $OpenBSD: trace.c,v 1.15 2006/03/24 08:03:44 espie Exp $ */
 /*
  * Copyright (c) 2001 Marc Espie.
  *
@@ -24,23 +24,17 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/types.h>
+#include <err.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
-#include <err.h>
 #include <stdlib.h>
-#include <string.h>
 #include "mdef.h"
 #include "stdd.h"
 #include "extern.h"
 
 FILE *traceout;
 
-int traced_macros = 0;
-
 #define TRACE_ARGS 	1
 #define TRACE_EXPANSION 2
 #define TRACE_QUOTE	4
@@ -50,81 +44,19 @@
 #define TRACE_ID	64
 #define TRACE_NEWFILE	128	/* not implemented yet */
 #define TRACE_INPUT	256	/* not implemented yet */
-#define TRACE_ALL	512
 
-static struct t {
-	struct t *next;
-	char 	 *name;
-	int	  on;
-} *l;
-
 static unsigned int letter_to_flag(int);
 static void print_header(struct input_file *);
-static struct t *find_trace_entry(const char *);
 static int frame_level(void);
 
-static unsigned int flags = TRACE_QUOTE | TRACE_EXPANSION;
 
-static struct t *
-find_trace_entry(const char *name)
-{
-	struct t *n;
+unsigned int trace_flags = TRACE_QUOTE | TRACE_EXPANSION;
 
-	for (n = l; n != NULL; n = n->next)
-		if (STREQ(n->name, name))
-			return n;
-	return NULL;
-}
-
-
 void
-mark_traced(const char *name, int on)
-{
-	struct t *n, *n2;
-
-	traced_macros = 1;
-
-	if (name == NULL) {
-		if (on)
-			flags |= TRACE_ALL;
-		else {
-			flags &= ~TRACE_ALL;
-			traced_macros = 0;
-		}
-		for (n = l; n != NULL; n = n2) {
-			n2 = n->next;
-			free(n->name);
-			free(n);
-		}
-		l = NULL;
-	} else {
-	    n = find_trace_entry(name);
-	    if (n == NULL) {
-		    n = xalloc(sizeof(struct t));
-		    n->name = xstrdup(name);
-		    n->next = l;
-		    l = n;
-	    }
-	    n->on = on;
-	}
-}
-
-int
-is_traced(const char *name)
-{
-	struct t *n;
-
-	for (n = l; n != NULL; n = n->next)
-		if (STREQ(n->name, name))
-			return n->on;
-	return (flags & TRACE_ALL) ? 1 : 0;
-}
-
-void
 trace_file(const char *name)
 {
 
-	if (traceout != stderr)
+	if (traceout && traceout != stderr)
 		fclose(traceout);
 	traceout = fopen(name, "w");
 	if (!traceout)
@@ -168,33 +100,31 @@
 	char mode = 0;
 	unsigned int f = 0;
 
-	traced_macros = 1;
-
 	if (*s == '+' || *s == '-')
 		mode = *s++;
 	while (*s)
 		f |= letter_to_flag(*s++);
 	switch(mode) {
 	case 0:
-		flags = f;
+		trace_flags = f;
 		break;
 	case '+':
-		flags |= f;
+		trace_flags |= f;
 		break;
 	case '-':
-		flags &= ~f;
+		trace_flags &= ~f;
 		break;
 	}
 }
 
 static int
-frame_level(void)
+frame_level()
 {
 	int level;
 	int framep;
 
-	for (framep = fp, level = 0; framep != 0;
-		level++,framep = mstack[framep-2].sfra)
+	for (framep = fp, level = 0; framep != 0; 
+		level++,framep = mstack[framep-3].sfra)
 		;
 	return level;
 }
@@ -203,62 +133,64 @@
 print_header(struct input_file *inp)
 {
 	fprintf(traceout, "m4trace:");
-	if (flags & TRACE_FILENAME)
+	if (trace_flags & TRACE_FILENAME)
 		fprintf(traceout, "%s:", inp->name);
-	if (flags & TRACE_LINENO)
+	if (trace_flags & TRACE_LINENO)
 		fprintf(traceout, "%lu:", inp->lineno);
 	fprintf(traceout, " -%d- ", frame_level());
-	if (flags & TRACE_ID)
+	if (trace_flags & TRACE_ID)
 		fprintf(traceout, "id %lu: ", expansion_id);
 }
 
-ssize_t
+size_t 
 trace(const char *argv[], int argc, struct input_file *inp)
 {
+	if (!traceout)
+		traceout = stderr;
 	print_header(inp);
-	if (flags & TRACE_CONT) {
+	if (trace_flags & TRACE_CONT) {
 		fprintf(traceout, "%s ...\n", argv[1]);
 		print_header(inp);
 	}
 	fprintf(traceout, "%s", argv[1]);
-	if ((flags & TRACE_ARGS) && argc > 2) {
+	if ((trace_flags & TRACE_ARGS) && argc > 2) {
 		char delim[3];
 		int i;
 
 		delim[0] = LPAREN;
 		delim[1] = EOS;
 		for (i = 2; i < argc; i++) {
-			fprintf(traceout, "%s%s%s%s", delim,
-			    (flags & TRACE_QUOTE) ? lquote : "",
-			    argv[i],
-			    (flags & TRACE_QUOTE) ? rquote : "");
+			fprintf(traceout, "%s%s%s%s", delim, 
+			    (trace_flags & TRACE_QUOTE) ? lquote : "", 
+			    argv[i], 
+			    (trace_flags & TRACE_QUOTE) ? rquote : "");
 			delim[0] = COMMA;
 			delim[1] = ' ';
 			delim[2] = EOS;
 		}
 		fprintf(traceout, "%c", RPAREN);
 	}
-	if (flags & TRACE_CONT) {
+	if (trace_flags & TRACE_CONT) {
 		fprintf(traceout, " -> ???\n");
 		print_header(inp);
 		fprintf(traceout, argc > 2 ? "%s(...)" : "%s", argv[1]);
 	}
-	if (flags & TRACE_EXPANSION)
+	if (trace_flags & TRACE_EXPANSION)
 		return buffer_mark();
 	else {
 		fprintf(traceout, "\n");
-		return -1;
+		return SIZE_MAX;
 	}
 }
 
-void
+void 
 finish_trace(size_t mark)
 {
 	fprintf(traceout, " -> ");
-	if (flags & TRACE_QUOTE)
+	if (trace_flags & TRACE_QUOTE)
 		fprintf(traceout, "%s", lquote);
 	dump_buffer(traceout, mark);
-	if (flags & TRACE_QUOTE)
+	if (trace_flags & TRACE_QUOTE)
 		fprintf(traceout, "%s", rquote);
 	fprintf(traceout, "\n");
 }
Index: ohash_init.3
===================================================================
--- ohash_init.3	(revision 0)
+++ ohash_init.3	(revision 0)
@@ -0,0 +1,229 @@
+.\"	$OpenBSD: ohash_init.3,v 1.14 2007/05/31 19:19:30 jmc Exp $
+.\" Copyright (c) 1999 Marc Espie <espie@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt OPEN_HASH 3
+.Os
+.Sh NAME
+.Nm ohash_init ,
+.Nm ohash_delete ,
+.Nm ohash_lookup_interval ,
+.Nm ohash_lookup_memory ,
+.Nm ohash_find ,
+.Nm ohash_remove ,
+.Nm ohash_insert ,
+.Nm ohash_first ,
+.Nm ohash_next ,
+.Nm ohash_entries
+.Nd light-weight open hashing
+.Sh SYNOPSIS
+.Fd #include <stdint.h>
+.Fd #include <stddef.h>
+.Fd #include <ohash.h>
+.Ft void
+.Fn ohash_init "struct ohash *h" "unsigned int size" "struct ohash_info *info"
+.Ft void
+.Fn ohash_delete "struct ohash *h"
+.Ft "unsigned int"
+.Fn ohash_lookup_interval "struct ohash *h" "const char *start" "const char *end" "uint32_t hv"
+.Ft "unsigned int"
+.Fn ohash_lookup_memory "struct ohash *h" "const char *k" "size_t s" "uint32_t hv"
+.Ft void *
+.Fn ohash_find "struct ohash *h" "unsigned int i"
+.Ft void *
+.Fn ohash_remove "struct ohash *h" "unsigned int i"
+.Ft void *
+.Fn ohash_insert "struct ohash *h" "unsigned int i" "void *p"
+.Ft void *
+.Fn ohash_first "struct ohash *h" "unsigned int *i"
+.Ft void *
+.Fn ohash_next "struct ohash *h" "unsigned int *i"
+.Ft "unsigned int"
+.Fn ohash_entries "struct ohash *h"
+.Sh DESCRIPTION
+These functions have been designed as a fast, extensible alternative to
+the usual hash table functions.
+They provide storage and retrieval of records indexed by keys,
+where a key is a contiguous sequence of bytes at a fixed position in
+each record.
+Keys can either be NUL-terminated strings or fixed-size memory areas.
+All functions take a pointer to an ohash structure as the
+.Fa h
+function argument.
+Storage for this structure should be provided by user code.
+.Pp
+.Fn ohash_init
+initializes the table to store roughly 2 to the power
+.Fa size
+elements.
+.Fa info
+holds the position of the key in each record, and two pointers to
+.Xr calloc 3
+and
+.Xr free 3 Ns -like
+functions, to use for managing the table internal storage.
+.Pp
+.Fn ohash_delete
+frees storage internal to
+.Fa h .
+Elements themselves should be freed by the user first, using for instance
+.Fn ohash_first
+and
+.Fn ohash_next .
+.Pp
+.Fn ohash_lookup_interval
+and
+.Fn ohash_lookup_memory
+are the basic look-up element functions.
+The hashing function result is provided by the user as
+.Fa hv .
+These return a
+.Qq slot
+in the ohash table
+.Fa h ,
+to be used with
+.Fn ohash_find ,
+.Fn ohash_insert ,
+or
+.Fn ohash_remove .
+This slot is only valid up to the next call to
+.Fn ohash_insert
+or
+.Fn ohash_remove .
+.Pp
+.Fn ohash_lookup_interval
+handles string-like keys.
+.Fn ohash_lookup_interval
+assumes the key is the interval between
+.Fa start
+and
+.Fa end ,
+exclusive,
+though the actual elements stored in the table should only contain
+NUL-terminated keys.
+.Pp
+.Fn ohash_lookup_memory
+assumes the key is the memory area starting at
+.Fa k
+of size
+.Fa s .
+All bytes are significant in key comparison.
+.Pp
+.Fn ohash_find
+retrieves an element from a slot
+.Fa i
+returned by the
+.Fn ohash_lookup*
+functions.
+It returns
+.Dv NULL
+if the slot is empty.
+.Pp
+.Fn ohash_insert
+inserts a new element
+.Fa p
+at slot
+.Fa i .
+Slot
+.Fa i
+must be empty and element
+.Fa p
+must have a key corresponding to the
+.Fn ohash_lookup*
+call.
+.Pp
+.Fn ohash_remove
+removes the element at slot
+.Fa i .
+It returns the removed element, for user code to dispose of, or
+.Dv NULL
+if the slot was empty.
+.Pp
+.Fn ohash_first
+and
+.Fn ohash_next
+can be used to access all elements in an ohash table, like this:
+.Bd -literal -offset indent
+for (n = ohash_first(h, &i); n != NULL; n = ohash_next(h, &i))
+	do_something_with(n);
+.Ed
+.Pp
+.Fa i
+points to an auxiliary unsigned integer used to record the current position
+in the ohash table.
+Those functions are safe to use even while entries are added to/removed
+from the table, but in such a case they don't guarantee that new entries
+will be returned.
+As a special case, they can safely be used to free elements in the table.
+.Pp
+.Fn ohash_entries
+returns the number of elements in the hash table.
+.Sh STORAGE HANDLING
+Only
+.Fn ohash_init ,
+.Fn ohash_insert ,
+.Fn ohash_remove
+and
+.Fn ohash_delete
+may call the user-supplied memory functions.
+It is the responsibility of the user memory allocation code to verify
+that those calls did not fail.
+.Pp
+If memory allocation fails,
+.Fn ohash_init
+returns a useless hash table.
+.Fn ohash_insert
+and
+.Fn ohash_remove
+still perform the requested operation, but the returned table should be
+considered read-only.
+It can still be accessed by
+.Fn ohash_lookup* ,
+.Fn ohash_find ,
+.Fn ohash_first
+and
+.Fn ohash_next
+to dump relevant information to disk before aborting.
+.Sh THREAD SAFETY
+The open hashing functions are not thread-safe by design.
+In particular, in a threaded environment, there is no guarantee that a
+.Qq slot
+will not move between a
+.Fn ohash_lookup*
+and a
+.Fn ohash_find ,
+.Fn ohash_insert
+or
+.Fn ohash_remove
+call.
+.Pp
+Multi-threaded applications should explicitly protect ohash table access.
+.Sh SEE ALSO
+.Xr ohash_interval 3
+.Rs
+.%A Donald E. Knuth
+.%B The Art of Computer Programming
+.%V Vol. 3
+.%P pp 506-550
+.%D 1973
+.Re
+.Sh STANDARDS
+Those functions are completely non-standard and should be avoided in
+portable programs.
+.Sh HISTORY
+Those functions were designed and written for
+.Ox
+.Xr make 1
+by Marc Espie in 1999.
Index: ohash.h
===================================================================
--- ohash.h	(revision 0)
+++ ohash.h	(revision 0)
@@ -0,0 +1,74 @@
+#ifndef OHASH_H
+#define OHASH_H
+/* $OpenBSD: ohash.h,v 1.9 2006/01/16 15:52:25 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Open hashing support. 
+ * Open hashing was chosen because it is much lighter than other hash
+ * techniques, and more efficient in most cases.
+ */
+
+#include <sys/types.h>
+
+struct ohash_info {
+	ptrdiff_t key_offset;
+	void *data;	/* user data */
+	void *(*halloc)(size_t, void *);
+	void (*hfree)(void *, size_t, void *);
+	void *(*alloc)(size_t, void *);
+};
+
+struct _ohash_record;
+
+struct ohash {
+	struct _ohash_record 	*t;
+	struct ohash_info 	info;
+	unsigned int 		size;
+	unsigned int 		total;
+	unsigned int 		deleted;
+};
+
+/* For this to be tweakable, we use small primitives, and leave part of the
+ * logic to the client application.  e.g., hashing is left to the client
+ * application.  We also provide a simple table entry lookup that yields
+ * a hashing table index (opaque) to be used in find/insert/remove.
+ * The keys are stored at a known position in the client data.
+ */
+__BEGIN_DECLS
+void ohash_init(struct ohash *, unsigned, struct ohash_info *);
+void ohash_delete(struct ohash *);
+
+unsigned int ohash_lookup_interval(struct ohash *, const char *,
+	    const char *, uint32_t);
+unsigned int ohash_lookup_memory(struct ohash *, const char *,
+	    size_t, uint32_t);
+void *ohash_find(struct ohash *, unsigned int);
+void *ohash_remove(struct ohash *, unsigned int);
+void *ohash_insert(struct ohash *, unsigned int, void *);
+void *ohash_first(struct ohash *, unsigned int *);
+void *ohash_next(struct ohash *, unsigned int *);
+unsigned int ohash_entries(struct ohash *);
+
+void *ohash_create_entry(struct ohash_info *, const char *, const char **);
+u_int32_t ohash_interval(const char *, const char **);
+
+unsigned int ohash_qlookupi(struct ohash *, const char *, const char **);
+unsigned int ohash_qlookup(struct ohash *, const char *);
+__END_DECLS
+#endif
Index: PSD.doc/Makefile
===================================================================
--- PSD.doc/Makefile	(revision 0)
+++ PSD.doc/Makefile	(revision 0)
@@ -0,0 +1,11 @@
+#	$OpenBSD: Makefile,v 1.4 2004/02/01 14:43:10 jmc Exp $
+
+
+DIR=	psd/17.m4
+SRCS=	m4.ms
+MACROS=	-ms
+
+paper.txt: ${SRCS}
+	${ROFF} -Tascii ${SRCS} > ${.TARGET}
+
+.include <bsd.doc.mk>
Index: PSD.doc/m4.ms
===================================================================
--- PSD.doc/m4.ms	(revision 0)
+++ PSD.doc/m4.ms	(revision 0)
@@ -0,0 +1,967 @@
+.\"	$OpenBSD: m4.ms,v 1.2 2003/06/26 16:18:48 mickey Exp $
+.\"
+.\" Copyright (C) Caldera International Inc.  2001-2002.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code and documentation must retain the above
+.\"    copyright notice, this list of conditions and the following disclaimer.
+.\" 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 or owned by Caldera
+.\"	International, Inc.
+.\" 4. Neither the name of Caldera International, Inc. nor the names of other
+.\"    contributors may be used to endorse or promote products derived from
+.\"    this software without specific prior written permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\"	@(#)m4.ms	6.3 (Berkeley) 6/5/93
+.\"
+.EH 'PSD:17-%''The M4 Macro Processor'
+.OH 'The M4 Macro Processor''PSD:17-%'
+.if n .ls 2
+.tr _\(em
+.tr *\(**
+.de UC
+\&\\$3\s-1\\$1\\s0\&\\$2
+..
+.de IT
+.if n .ul
+\&\\$3\f2\\$1\fP\&\\$2
+..
+.de UL
+.if n .ul
+\&\\$3\f3\\$1\fP\&\\$2
+..
+.de P1
+.DS I 3n
+.if n .ls 2
+.nf
+.if n .ta 5 10 15 20 25 30 35 40 45 50 55 60
+.if t .ta .4i .8i 1.2i 1.6i 2i 2.4i 2.8i 3.2i 3.6i 4i 4.4i 4.8i 5.2i 5.6i
+.if t .tr -\(mi|\(bv'\(fm^\(no*\(**
+.tr `\(ga'\(aa
+.if t .tr _\(ul
+.ft 3
+.lg 0
+..
+.de P2
+.ps \\n(PS
+.vs \\n(VSp
+.ft R
+.if n .ls 2
+.tr --||''^^!!
+.if t .tr _\(em
+.fi
+.lg
+.DE
+.if t .tr _\(em
+..
+.hw semi-colon
+.hw estab-lished
+.hy 14
+.		\"2=not last lines; 4= no -xx; 8=no xx-
+.		\"special chars in programs
+.	\" start of text
+.\".RP
+.....TR 59
+.....TM 77-1273-6 39199 39199-11
+.ND "July 1, 1977"
+.TL
+The M4 Macro Processor
+.AU "MH 2C-518" 6021
+Brian W. Kernighan
+.AU "MH 2C-517" 3770
+Dennis M. Ritchie
+.AI
+.MH
+.AB
+.PP
+M4 is a macro processor available on
+.UX
+and
+.UC GCOS .
+Its primary use has been as a
+front end for Ratfor for those
+cases where parameterless macros 
+are not adequately powerful.
+It has also been used for languages as disparate as C and Cobol.
+M4 is particularly suited for functional languages like Fortran, PL/I and C
+since macros are specified in a functional notation.
+.PP
+M4 provides features seldom found even in much larger
+macro processors, 
+including
+.IP "  \(bu"
+arguments
+.IP "  \(bu"
+condition testing
+.IP "  \(bu"
+arithmetic capabilities
+.IP "  \(bu"
+string and substring functions
+.IP "  \(bu"
+file manipulation
+.LP
+.PP
+This paper is a user's manual for M4.
+.AE
+.CS 6 0 6 0 0 1
+.if t .2C
+.SH
+Introduction
+.PP
+A macro processor is a useful way to enhance a programming language,
+to make it more palatable
+or more readable,
+or to tailor it to a particular application.
+The
+.UL #define
+statement in C
+and the analogous
+.UL define
+in Ratfor
+are examples of the basic facility provided by
+any macro processor _
+replacement of text by other text.
+.PP
+The M4 macro processor is an extension of a macro processor called M3
+which was written by D. M. Ritchie
+for the AP-3 minicomputer;
+M3 was in turn based on a macro processor implemented for [1].
+Readers unfamiliar with the basic ideas of macro processing
+may wish to read some of the discussion there.
+.PP
+M4 is a suitable front end for Ratfor and C,
+and has also been used successfully with Cobol.
+Besides the straightforward replacement of one string of text by another,
+it provides
+macros with arguments,
+conditional macro expansion,
+arithmetic,
+file manipulation,
+and some specialized string processing functions.
+.PP
+The basic operation of M4
+is to copy its input to its output.
+As the input is read, however, each alphanumeric ``token''
+(that is, string of letters and digits) is checked.
+If it is the name of a macro,
+then the name of the macro is replaced by its defining text,
+and the resulting string is pushed back onto the
+input to be rescanned.
+Macros may be called with arguments, in which case the arguments are collected
+and substituted into the right places in the defining text
+before it is rescanned.
+.PP
+M4 provides a collection of about twenty built-in
+macros
+which perform various useful operations;
+in addition, the user can define new macros.
+Built-ins and user-defined macros work exactly the same way, except that
+some of the built-in macros have side effects
+on the state of the process.
+.SH
+Usage
+.PP
+On
+.UC UNIX ,
+use
+.P1
+m4 [files]
+.P2
+Each argument file is processed in order;
+if there are no arguments, or if an argument
+is `\-',
+the standard input is read at that point.
+The processed text is written on the standard output,
+which may be captured for subsequent processing with
+.P1
+m4 [files] >outputfile
+.P2
+On
+.UC GCOS ,
+usage is identical, but the program is called
+.UL \&./m4 .
+.SH
+Defining Macros
+.PP
+The primary built-in function of M4
+is
+.UL define ,
+which is used to define new macros.
+The input
+.P1
+define(name, stuff)
+.P2
+causes the string
+.UL name
+to be defined as
+.UL stuff .
+All subsequent occurrences of
+.UL name
+will be replaced by
+.UL stuff .
+.UL name
+must be alphanumeric and must begin with a letter
+(the underscore \(ul counts as a letter).
+.UL stuff
+is any text that contains balanced parentheses;
+it may stretch over multiple lines.
+.PP
+Thus, as a typical example,
+.P1
+define(N, 100)
+ ...
+if (i > N)
+.P2
+defines
+.UL N
+to be 100, and uses this ``symbolic constant'' in a later
+.UL if
+statement.
+.PP
+The left parenthesis must immediately follow the word
+.UL define ,
+to signal that
+.UL define
+has arguments.
+If a macro or built-in name is not followed immediately by `(',
+it is assumed to have no arguments.
+This is the situation for
+.UL N
+above;
+it is actually a macro with no arguments,
+and thus when it is used there need be no (...) following it.
+.PP
+You should also notice that a macro name is only recognized as such
+if it appears surrounded by non-alphanumerics.
+For example, in
+.P1
+define(N, 100)
+ ...
+if (NNN > 100)
+.P2
+the variable 
+.UL NNN
+is absolutely unrelated to the defined macro
+.UL N ,
+even though it contains a lot of
+.UL N 's.
+.PP
+Things may be defined in terms of other things.
+For example,
+.P1
+define(N, 100)
+define(M, N)
+.P2
+defines both M and N to be 100.
+.PP
+What happens if
+.UL N
+is redefined?
+Or, to say it another way, is
+.UL M 
+defined as
+.UL N
+or as 100?
+In M4,
+the latter is true _
+.UL M
+is 100, so even if
+.UL N 
+subsequently changes,
+.UL M
+does not.
+.PP
+This behavior arises because
+M4 expands macro names into their defining text as soon as it possibly can.
+Here, that means that when the string
+.UL N
+is seen as the arguments of
+.UL define
+are being collected, it is immediately replaced by 100;
+it's just as if you had said
+.P1
+define(M, 100)
+.P2
+in the first place.
+.PP
+If this isn't what you really want, there are two ways out of it.
+The first, which is specific to this situation,
+is to interchange the order of the definitions:
+.P1
+define(M, N)
+define(N, 100)
+.P2
+Now
+.UL M
+is defined to be the string
+.UL N ,
+so when you ask for 
+.UL M
+later, you'll always get the value of
+.UL N 
+at that time
+(because the
+.UL M
+will be replaced by
+.UL N
+which will be replaced by 100).
+.SH
+Quoting
+.PP
+The more general solution is to delay the expansion of
+the arguments of
+.UL define 
+by
+.ul
+quoting
+them.
+Any text surrounded by the single quotes \(ga and \(aa
+is not expanded immediately, but has the quotes stripped off.
+If you say
+.P1
+define(N, 100)
+define(M, `N')
+.P2
+the quotes around the
+.UL N
+are stripped off as the argument is being collected,
+but they have served their purpose, and 
+.UL M
+is defined as
+the string
+.UL N ,
+not 100.
+The general rule is that M4 always strips off
+one level of single quotes whenever it evaluates
+something.
+This is true even outside of
+macros.
+If you want the word
+.UL define
+to appear in the output,
+you have to quote it in the input,
+as in
+.P1
+	`define' = 1;
+.P2
+.PP
+As another instance of the same thing, which is a bit more surprising,
+consider redefining
+.UL N :
+.P1
+define(N, 100)
+ ...
+define(N, 200)
+.P2
+Perhaps regrettably, the
+.UL N
+in the second definition is
+evaluated as soon as it's seen;
+that is, it is
+replaced by
+100, so it's as if you had written
+.P1
+define(100, 200)
+.P2
+This statement is ignored by M4, since you can only define things that look
+like names, but it obviously doesn't have the effect you wanted.
+To really redefine 
+.UL N ,
+you must delay the evaluation by quoting:
+.P1
+define(N, 100)
+ ...
+define(`N', 200)
+.P2
+In M4,
+it is often wise to quote the first argument of a macro.
+.PP
+If \` and \' are not convenient for some reason,
+the quote characters can be changed with the built-in
+.UL changequote :
+.P1
+changequote([, ])
+.P2
+makes the new quote characters the left and right brackets.
+You can restore the original characters with just
+.P1
+changequote
+.P2
+.PP
+There are two additional built-ins related to
+.UL define .
+.UL undefine
+removes the definition of some macro or built-in:
+.P1
+undefine(`N')
+.P2
+removes the definition of
+.UL N .
+(Why are the quotes absolutely necessary?)
+Built-ins can be removed with 
+.UL undefine ,
+as in
+.P1
+undefine(`define')
+.P2
+but once you remove one, you can never get it back.
+.PP
+The built-in 
+.UL ifdef
+provides a way to determine if a macro is currently defined.
+In particular, M4 has pre-defined the names
+.UL unix
+and
+.UL gcos
+on the corresponding systems, so you can
+tell which one you're using:
+.P1
+ifdef(`unix', `define(wordsize,16)' )
+ifdef(`gcos', `define(wordsize,36)' )
+.P2
+makes a definition appropriate for the particular machine.
+Don't forget the quotes!
+.PP
+.UL ifdef
+actually permits three arguments;
+if the name is undefined, the value of
+.UL ifdef
+is then the third argument, as in
+.P1
+ifdef(`unix', on UNIX, not on UNIX)
+.P2
+.SH
+Arguments
+.PP
+So far we have discussed the simplest form of macro processing _
+replacing one string by another (fixed) string.
+User-defined macros may also have arguments, so different invocations
+can have different results.
+Within the replacement text for a macro
+(the second argument of its
+.UL define )
+any occurrence of
+.UL $n
+will be replaced by the 
+.UL n th
+argument when the macro
+is actually used.
+Thus, the macro
+.UL bump ,
+defined as
+.P1
+define(bump, $1 = $1 + 1)
+.P2
+generates code to increment its argument by 1:
+.P1
+bump(x)
+.P2
+is
+.P1
+x = x + 1
+.P2
+.PP
+A macro can have as many arguments as you want,
+but only the first nine are accessible,
+through
+.UL $1
+to
+.UL $9 .
+(The macro name itself is
+.UL $0 ,
+although that is less commonly used.)
+Arguments that are not supplied are replaced by null strings,
+so
+we can define a macro
+.UL cat
+which simply concatenates its arguments, like this:
+.P1
+define(cat, $1$2$3$4$5$6$7$8$9)
+.P2
+Thus
+.P1
+cat(x, y, z)
+.P2
+is equivalent to
+.P1
+xyz
+.P2
+.UL $4
+through
+.UL $9
+are null, since no corresponding arguments were provided.
+.PP
+.PP
+Leading unquoted blanks, tabs, or newlines that occur during argument collection
+are discarded.
+All other white space is retained.
+Thus
+.P1
+define(a,   b   c)
+.P2
+defines
+.UL a
+to be
+.UL b\ \ \ c .
+.PP
+Arguments are separated by commas, but parentheses are counted properly,
+so a comma ``protected'' by parentheses does not terminate an argument.
+That is, in
+.P1
+define(a, (b,c))
+.P2
+there are only two arguments;
+the second is literally
+.UL (b,c) .
+And of course a bare comma or parenthesis can be inserted by quoting it.
+.SH
+Arithmetic Built-ins
+.PP
+M4 provides two built-in functions for doing arithmetic
+on integers (only).
+The simplest is
+.UL incr ,
+which increments its numeric argument by 1.
+Thus to handle the common programming situation
+where you want a variable to be defined as ``one more than N'',
+write
+.P1
+define(N, 100)
+define(N1, `incr(N)')
+.P2
+Then
+.UL N1
+is defined as one more than the current value of
+.UL N .
+.PP
+The more general mechanism for arithmetic is a built-in
+called
+.UL eval ,
+which is capable of arbitrary arithmetic on integers.
+It provides the operators
+(in decreasing order of precedence)
+.DS
+unary + and \(mi
+** or ^	(exponentiation)
+*  /  % (modulus)
++  \(mi
+==  !=  <  <=  >  >=
+!		(not)
+& or &&	(logical and)
+\(or or \(or\(or		(logical or)
+.DE
+Parentheses may be used to group operations where needed.
+All the operands of
+an expression given to
+.UL eval
+must ultimately be numeric.
+The numeric value of a true relation
+(like 1>0)
+is 1, and false is 0.
+The precision in
+.UL eval
+is
+32 bits on
+.UC UNIX
+and 36 bits on
+.UC GCOS .
+.PP
+As a simple example, suppose we want 
+.UL M
+to be 
+.UL 2**N+1 .
+Then
+.P1
+define(N, 3)	
+define(M, `eval(2**N+1)')
+.P2
+As a matter of principle, it is advisable
+to quote the defining text for a macro
+unless it is very simple indeed
+(say just a number);
+it usually gives the result you want,
+and is a good habit to get into.
+.SH
+File Manipulation
+.PP
+You can include a new file in the input at any time by
+the built-in function
+.UL include :
+.P1
+include(filename)
+.P2
+inserts the contents of
+.UL filename
+in place of the
+.UL include
+command.
+The contents of the file is often a set of definitions.
+The value
+of
+.UL include
+(that is, its replacement text)
+is the contents of the file;
+this can be captured in definitions, etc.
+.PP
+It is a fatal error if the file named in
+.UL include
+cannot be accessed.
+To get some control over this situation, the alternate form
+.UL sinclude
+can be used;
+.UL sinclude 
+(``silent include'')
+says nothing and continues if it can't access the file.
+.PP
+It is also possible to divert the output of M4 to temporary files during processing,
+and output the collected material upon command.
+M4 maintains nine of these diversions, numbered 1 through 9.
+If you say
+.P1
+divert(n)
+.P2
+all subsequent output is put onto the end of a temporary file
+referred to as
+.UL n .
+Diverting to this file is stopped by another 
+.UL divert 
+command;
+in particular,
+.UL divert
+or
+.UL divert(0)
+resumes the normal output process.
+.PP
+Diverted text is normally output all at once
+at the end of processing,
+with the diversions output in numeric order.
+It is possible, however, to bring back diversions
+at any time,
+that is, to append them to the current diversion.
+.P1
+undivert
+.P2
+brings back all diversions in numeric order, and
+.UL undivert
+with arguments brings back the selected diversions
+in the order given.
+The act of undiverting discards the diverted stuff,
+as does diverting into a diversion 
+whose number is not between 0 and 9 inclusive.
+.PP
+The value of
+.UL undivert
+is
+.ul
+not
+the diverted stuff.
+Furthermore, the diverted material is
+.ul
+not
+rescanned for macros.
+.PP
+The built-in
+.UL divnum
+returns the number of the currently active diversion.
+This is zero during normal processing.
+.SH
+System Command
+.PP
+You can run any program in the local operating system
+with the
+.UL syscmd
+built-in.
+For example,
+.P1
+syscmd(date)
+.P2
+on
+.UC UNIX
+runs the
+.UL date
+command.
+Normally
+.UL syscmd
+would be used to create a file
+for a subsequent
+.UL include .
+.PP
+To facilitate making unique file names, the built-in
+.UL maketemp
+is provided, with specifications identical to the system function
+.ul
+mktemp:
+a string of XXXXX in the argument is replaced
+by the process id of the current process.
+.SH
+Conditionals
+.PP
+There is a built-in called
+.UL ifelse
+which enables you to perform arbitrary conditional testing.
+In the simplest form,
+.P1
+ifelse(a, b, c, d)
+.P2
+compares the two strings
+.UL a
+and
+.UL b .
+If these are identical, 
+.UL ifelse
+returns
+the string
+.UL c ;
+otherwise it returns
+.UL d .
+Thus we might define a macro called
+.UL compare
+which compares two strings and returns ``yes'' or ``no''
+if they are the same or different.
+.P1
+define(compare, `ifelse($1, $2, yes, no)')
+.P2
+Note the quotes,
+which prevent too-early evaluation of
+.UL ifelse .
+.PP
+If the fourth argument is missing, it is treated as empty.
+.PP
+.UL ifelse
+can actually have any number of arguments,
+and thus provides a limited form of multi-way decision capability.
+In the input
+.P1
+ifelse(a, b, c, d, e, f, g)
+.P2
+if the string
+.UL a
+matches the string
+.UL b ,
+the result is
+.UL c .
+Otherwise, if
+.UL d
+is the same as
+.UL e ,
+the result is
+.UL f .
+Otherwise the result is
+.UL g .
+If the final argument
+is omitted, the result is null,
+so
+.P1
+ifelse(a, b, c)
+.P2
+is
+.UL c
+if 
+.UL a
+matches
+.UL b ,
+and null otherwise.
+.SH
+String Manipulation
+.PP
+The built-in
+.UL len
+returns the length of the string that makes up its argument.
+Thus
+.P1
+len(abcdef)
+.P2
+is 6, and
+.UL len((a,b))
+is 5.
+.PP
+The built-in
+.UL substr
+can be used to produce substrings of strings.
+.UL substr(s,\ i,\ n)
+returns the substring of
+.UL s
+that starts at the
+.UL i th
+position
+(origin zero),
+and is
+.UL n
+characters long.
+If 
+.UL n
+is omitted, the rest of the string is returned,
+so
+.P1
+substr(`now is the time', 1)
+.P2
+is
+.P1
+ow is the time
+.P2
+If 
+.UL i
+or
+.UL n
+are out of range, various sensible things happen.
+.PP
+.UL index(s1,\ s2)
+returns the index (position) in
+.UL s1
+where the string
+.UL s2
+occurs, or \-1
+if it doesn't occur.
+As with
+.UL substr ,
+the origin for strings is 0.
+.PP
+The built-in
+.UL translit
+performs character transliteration.
+.P1
+translit(s, f, t)
+.P2
+modifies
+.UL s
+by replacing any character found in
+.UL f
+by the corresponding character of
+.UL t .
+That is,
+.P1
+translit(s, aeiou, 12345)
+.P2
+replaces the vowels by the corresponding digits.
+If
+.UL t
+is shorter than
+.UL f ,
+characters which don't have an entry in
+.UL t
+are deleted; as a limiting case,
+if
+.UL t
+is not present at all,
+characters from 
+.UL f
+are deleted from 
+.UL s .
+So
+.P1
+translit(s, aeiou)
+.P2
+deletes vowels from 
+.UL s .
+.PP
+There is also a built-in called
+.UL dnl
+which deletes all characters that follow it up to
+and including the next newline;
+it is useful mainly for throwing away 
+empty lines that otherwise tend to clutter up M4 output.
+For example, if you say
+.P1
+define(N, 100)
+define(M, 200)
+define(L, 300)
+.P2
+the newline at the end of each line is not part of the definition,
+so it is copied into the output, where it may not be wanted.
+If you add
+.UL dnl
+to each of these lines, the newlines will disappear.
+.PP
+Another way to achieve this, due to J. E. Weythman,
+is
+.P1
+divert(-1)
+	define(...)
+	...
+divert
+.P2
+.SH
+Printing
+.PP
+The built-in
+.UL errprint
+writes its arguments out on the standard error file.
+Thus you can say
+.P1
+errprint(`fatal error')
+.P2
+.PP
+.UL dumpdef
+is a debugging aid which
+dumps the current definitions of defined terms.
+If there are no arguments, you get everything;
+otherwise you get the ones you name as arguments.
+Don't forget to quote the names!
+.SH
+Summary of Built-ins
+.PP
+Each entry is preceded by the
+page number where it is described.
+.DS
+.tr '\'`\`
+.ta .25i
+3	changequote(L, R)
+1	define(name, replacement)
+4	divert(number)
+4	divnum
+5	dnl
+5	dumpdef(`name', `name', ...)
+5	errprint(s, s, ...)
+4	eval(numeric expression)
+3	ifdef(`name', this if true, this if false)
+5	ifelse(a, b, c, d)
+4	include(file)
+3	incr(number)
+5	index(s1, s2)
+5	len(string)
+4	maketemp(...XXXXX...)
+4	sinclude(file)
+5	substr(string, position, number)
+4	syscmd(s)
+5	translit(str, from, to)
+3	undefine(`name')
+4	undivert(number,number,...)
+.DE
+.SH
+Acknowledgements
+.PP
+We are indebted to Rick Becker, John Chambers,
+Doug McIlroy,
+and especially Jim Weythman,
+whose pioneering use of M4 has led to several valuable improvements.
+We are also deeply grateful to Weythman for several substantial contributions
+to the code.
+.SG
+.SH
+References
+.LP
+.IP [1]
+B. W. Kernighan and P. J. Plauger,
+.ul
+Software Tools,
+Addison-Wesley, Inc., 1976.
Index: extern.h
===================================================================
--- extern.h	(revision 198625)
+++ extern.h	(working copy)
@@ -1,4 +1,4 @@
-/*	$OpenBSD: extern.h,v 1.29 2002/02/16 21:27:48 millert Exp $	*/
+/*	$OpenBSD: extern.h,v 1.49 2009/10/14 17:19:47 sthen Exp $	*/
 /*	$NetBSD: extern.h,v 1.3 1996/01/13 23:25:24 pk Exp $	*/
 
 /*-
@@ -16,11 +16,7 @@
  * 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.
  *
@@ -37,11 +33,10 @@
  * SUCH DAMAGE.
  *
  *	@(#)extern.h	8.1 (Berkeley) 6/6/93
- * $FreeBSD$
  */
 
 /* eval.c */
-extern void	eval(const char *[], int, int);
+extern void	eval(const char *[], int, int, int);
 extern void	dodefine(const char *, const char *);
 extern unsigned long expansion_id;
 
@@ -60,19 +55,39 @@
 extern void doprintfilename(struct input_file *);
 
 extern void doesyscmd(const char *);
+extern void getdivfile(const char *);
+extern void doformat(const char *[], int);
+ 
 
+/* look.c */
 
-/* look.c */
-extern ndptr	addent(const char *);
-extern unsigned	hash(const char *);
+#define FLAG_UNTRACED 0
+#define FLAG_TRACED 1
+#define FLAG_NO_TRACE 2
+
+extern void	init_macros(void);
 extern ndptr	lookup(const char *);
-extern void	remhash(const char *, int);
+extern void mark_traced(const char *, int);
+extern struct ohash macros;
 
+extern struct macro_definition *lookup_macro_definition(const char *);
+extern void 	macro_define(const char *, const char *);
+extern void 	macro_pushdef(const char *, const char *);
+extern void 	macro_popdef(const char *);
+extern void 	macro_undefine(const char *);
+extern void 	setup_builtin(const char *, unsigned int);
+extern void 	macro_for_all(void (*)(const char *, struct macro_definition *));
+#define macro_getdef(p) 	((p)->d)
+#define macro_name(p)		((p)->name)
+#define macro_builtin_type(p)	((p)->builtin_type)
+#define is_traced(p) ((p)->trace_flags == FLAG_NO_TRACE ? (trace_flags & TRACE_ALL) : (p)->trace_flags)
+
+extern ndptr macro_getbuiltin(const char *);
+
 /* main.c */
 extern void outputstr(const char *);
-extern int builtin_type(const char *);
-extern const char *builtin_realname(int);
-extern void emitline(void);
+extern void do_emit_synchline(void);
+#define emit_synchline() do { if (synch_lines) do_emit_synchline(); } while(0)
 
 /* misc.c */
 extern void	chrsave(int);
@@ -83,28 +98,31 @@
 extern void	killdiv(void);
 extern void	onintr(int);
 extern void	pbnum(int);
+extern void	pbnumbase(int, int, int);
 extern void	pbunsigned(unsigned long);
 extern void	pbstr(const char *);
-extern void	putback(int);
-extern void	*xalloc(size_t);
+extern void	pushback(int);
+extern void	*xalloc(size_t, const char *fmt, ...);
+extern void	*xrealloc(void *, size_t, const char *fmt, ...);
 extern char	*xstrdup(const char *);
 extern void	usage(void);
 extern void	resizedivs(int);
 extern size_t	buffer_mark(void);
 extern void	dump_buffer(FILE *, size_t);
+extern void	m4errx(int, const char *, ...);
 
 extern int 	obtain_char(struct input_file *);
 extern void	set_input(struct input_file *, FILE *, const char *);
 extern void	release_input(struct input_file *);
 
-/* speeded-up versions of chrsave/putback */
-#define PUTBACK(c)				\
+/* speeded-up versions of chrsave/pushback */
+#define PUSHBACK(c)				\
 	do {					\
 		if (bp >= endpbb)		\
 			enlarge_bufspace();	\
 		*bp++ = (c);			\
 	} while(0)
-
+	
 #define CHRSAVE(c)				\
 	do {					\
 		if (ep >= endest)		\
@@ -115,16 +133,15 @@
 /* and corresponding exposure for local symbols */
 extern void enlarge_bufspace(void);
 extern void enlarge_strspace(void);
-extern char *endpbb;
+extern unsigned char *endpbb;
 extern char *endest;
 
 /* trace.c */
-extern void mark_traced(const char *, int);
-extern int is_traced(const char *);
+extern unsigned int trace_flags;
+#define TRACE_ALL	512
 extern void trace_file(const char *);
-extern ssize_t trace(const char **, int, struct input_file *);
+extern size_t trace(const char **, int, struct input_file *);
 extern void finish_trace(size_t);
-extern int traced_macros;
 extern void set_trace_flags(const char *);
 extern FILE *traceout;
 
@@ -133,42 +150,28 @@
 extern char *sstack;		/* shadow stack, for string space extension */
 extern FILE *active;		/* active output file pointer */
 extern struct input_file infile[];/* input file stack (0=stdin) */
-extern char *inname[];		/* names of these input files */
-extern int inlineno[];		/* current number in each input file */
 extern FILE **outfile;		/* diversion array(0=bitbucket) */
 extern int maxout;		/* maximum number of diversions */
 extern int fp; 			/* m4 call frame pointer */
 extern int ilevel;		/* input file stack pointer */
 extern int oindex;		/* diversion index. */
 extern int sp;			/* current m4 stack pointer */
-extern char *bp;		/* first available character */
-extern char *buf;		/* push-back buffer */
-extern char *bufbase;		/* buffer base for this ilevel */
-extern char *bbase[];		/* buffer base per ilevel */
+extern unsigned char *bp;	/* first available character */
+extern unsigned char *buf;	/* push-back buffer */
+extern unsigned char *bufbase;	/* buffer base for this ilevel */
+extern unsigned char *bbase[];	/* buffer base per ilevel */
 extern char ecommt[MAXCCHARS+1];/* end character for comment */
 extern char *ep;		/* first free char in strspace */
 extern char lquote[MAXCCHARS+1];/* left quote character (`) */
-extern const char *m4wraps;	/* m4wrap string default. */
-extern char null[];		/* as it says.. just a null. */
+extern char **m4wraps;		/* m4wrap string default. */
+extern int maxwraps;		/* size of m4wraps array */
+extern int wrapindex;		/* current index in m4wraps */
+
+extern char *null;		/* as it says.. just a null. */
 extern char rquote[MAXCCHARS+1];/* right quote character (') */
 extern char scommt[MAXCCHARS+1];/* start character for comment */
-extern int synccpp;		/* Line synchronisation for C preprocessor */
+extern int  synch_lines;	/* line synchronisation directives */
 
 extern int mimic_gnu;		/* behaves like gnu-m4 */
+extern int prefix_builtins;	/* prefix builtin macros with m4_ */
 
-/* get a possibly pushed-back-character, increment lineno if need be */
-static __inline int gpbc(void)
-{
-	int chscratch;		/* Scratch space. */
-
-	if (bp > bufbase) {
-		if (*--bp)
-			return ((unsigned char)*bp);
-		else
-			return (EOF);
-	}
-	chscratch = obtain_char(infile+ilevel);
-	if (chscratch == '\n')
-		++inlineno[ilevel];
-	return (chscratch);
-}
Index: ohash_qlookup.c
===================================================================
--- ohash_qlookup.c	(revision 0)
+++ ohash_qlookup.c	(revision 0)
@@ -0,0 +1,27 @@
+/* $OpenBSD: ohash_qlookup.c,v 1.2 2004/06/22 20:00:17 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+
+unsigned int
+ohash_qlookup(struct ohash *h, const char *s)
+{
+	const char *e = NULL;
+	return ohash_qlookupi(h, s, &e);
+}
Index: ohash_int.h
===================================================================
--- ohash_int.h	(revision 0)
+++ ohash_int.h	(revision 0)
@@ -0,0 +1,19 @@
+/*	$OpenBSD: ohash_int.h,v 1.3 2006/01/16 15:52:25 espie Exp $	*/
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ohash.h"
+
+struct _ohash_record {
+	u_int32_t	hv;
+	const char 	*p;
+};
+
+#define DELETED		((const char *)h)
+#define NONE		(h->size)
+
+/* Don't bother changing the hash table if the change is small enough.  */
+#define MINSIZE		(1UL << 4)
+#define MINDELETED	4
Index: parser.y
===================================================================
--- parser.y	(revision 0)
+++ parser.y	(revision 0)
@@ -0,0 +1,80 @@
+%{
+/* $OpenBSD: parser.y,v 1.6 2008/08/21 21:00:14 espie Exp $ */
+/*
+ * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <stdint.h>
+#define YYSTYPE	int32_t
+extern int32_t end_result;
+extern int yylex(void);
+extern int yyerror(const char *);
+%}
+%token NUMBER
+%token ERROR
+%left LOR
+%left LAND
+%left '|'
+%left '^'
+%left '&'
+%left EQ NE
+%left '<' LE '>' GE
+%left LSHIFT RSHIFT
+%left '+' '-'
+%left '*' '/' '%'
+%right UMINUS UPLUS '!' '~'
+
+%%
+
+top	: expr { end_result = $1; }
+	;
+expr 	: expr '+' expr { $$ = $1 + $3; }
+     	| expr '-' expr { $$ = $1 - $3; }
+     	| expr '*' expr { $$ = $1 * $3; }
+	| expr '/' expr {
+		if ($3 == 0) {
+			yyerror("division by zero");
+			exit(1);
+		}
+		$$ = $1 / $3;
+	}
+	| expr '%' expr { 
+		if ($3 == 0) {
+			yyerror("modulo zero");
+			exit(1);
+		}
+		$$ = $1 % $3;
+	}
+	| expr LSHIFT expr { $$ = $1 << $3; }
+	| expr RSHIFT expr { $$ = $1 >> $3; }
+	| expr '<' expr { $$ = $1 < $3; }
+	| expr '>' expr { $$ = $1 > $3; }
+	| expr LE expr { $$ = $1 <= $3; }
+	| expr GE expr { $$ = $1 >= $3; }
+	| expr EQ expr { $$ = $1 == $3; }
+	| expr NE expr { $$ = $1 != $3; }
+	| expr '&' expr { $$ = $1 & $3; }
+	| expr '^' expr { $$ = $1 ^ $3; }
+	| expr '|' expr { $$ = $1 | $3; }
+	| expr LAND expr { $$ = $1 && $3; }
+	| expr LOR expr { $$ = $1 || $3; }
+	| '(' expr ')' { $$ = $2; }
+	| '-' expr %prec UMINUS { $$ = -$2; }
+	| '+' expr %prec UPLUS  { $$ = $2; }
+	| '!' expr { $$ = !$2; }
+	| '~' expr { $$ = ~$2; }
+	| NUMBER
+	;
+%%
+
Index: ohash_lookup_memory.c
===================================================================
--- ohash_lookup_memory.c	(revision 0)
+++ ohash_lookup_memory.c	(revision 0)
@@ -0,0 +1,64 @@
+/* $OpenBSD: ohash_lookup_memory.c,v 1.3 2006/01/16 15:52:25 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+
+unsigned int
+ohash_lookup_memory(struct ohash *h, const char *k, size_t size, uint32_t hv)
+{
+	unsigned int	i, incr;
+	unsigned int	empty;
+	
+#ifdef STATS_HASH
+	STAT_HASH_LOOKUP++;
+#endif
+	empty = NONE;
+	i = hv % h->size;
+	incr = ((hv % (h->size-2)) & ~1) + 1;
+	while (h->t[i].p != NULL) {
+#ifdef STATS_HASH
+		STAT_HASH_LENGTH++;
+#endif
+		if (h->t[i].p == DELETED) {
+			if (empty == NONE)
+				empty = i;
+		} else if (h->t[i].hv == hv && 
+		    memcmp(h->t[i].p+h->info.key_offset, k, size) == 0) {
+		    	if (empty != NONE) {
+				h->t[empty].hv = hv;
+				h->t[empty].p = h->t[i].p;
+				h->t[i].p = DELETED;
+				return empty;
+			} else {
+#ifdef STATS_HASH
+				STAT_HASH_POSITIVE++;
+#endif
+			}	return i;
+		}
+		i += incr;
+		if (i >= h->size) 
+			i -= h->size;
+	}
+
+	/* Found an empty position.  */
+	if (empty != NONE) 
+		i = empty;
+	h->t[i].hv = hv;
+	return i;
+}
Index: ohash_do.c
===================================================================
--- ohash_do.c	(revision 0)
+++ ohash_do.c	(revision 0)
@@ -0,0 +1,111 @@
+/* $OpenBSD: ohash_do.c,v 1.4 2004/06/22 20:00:16 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+
+static void ohash_resize(struct ohash *);
+
+static void 
+ohash_resize(struct ohash *h)
+{
+	struct _ohash_record *n;
+	unsigned int 	ns, j;
+	unsigned int	i, incr;
+
+	if (4 * h->deleted < h->total)
+		ns = h->size << 1;
+	else if (3 * h->deleted > 2 * h->total)
+		ns = h->size >> 1;
+	else
+		ns = h->size;
+	if (ns < MINSIZE)
+		ns = MINSIZE;
+#ifdef STATS_HASH
+	STAT_HASH_EXPAND++;
+	STAT_HASH_SIZE += ns - h->size;
+#endif
+	n = (h->info.halloc)(sizeof(struct _ohash_record) * ns, h->info.data);
+	if (!n)
+		return;
+
+	for (j = 0; j < h->size; j++) {
+		if (h->t[j].p != NULL && h->t[j].p != DELETED) {
+			i = h->t[j].hv % ns;
+			incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1;
+			while (n[i].p != NULL) {
+				i += incr;
+				if (i >= ns)
+					i -= ns;
+		    	}
+			n[i].hv = h->t[j].hv;
+			n[i].p = h->t[j].p;
+		}
+	}
+	(h->info.hfree)(h->t, sizeof(struct _ohash_record) * h->size, 
+		h->info.data);
+	h->t = n;
+	h->size = ns;
+	h->total -= h->deleted;
+	h->deleted = 0;
+}
+
+void *
+ohash_remove(struct ohash *h, unsigned int i)
+{
+	void 		*result = (void *)h->t[i].p;
+
+	if (result == NULL || result == DELETED)
+		return NULL;
+
+#ifdef STATS_HASH
+	STAT_HASH_ENTRIES--;
+#endif
+	h->t[i].p = DELETED;
+	h->deleted++;
+	if (h->deleted >= MINDELETED && 4 * h->deleted > h->total)
+		ohash_resize(h);
+	return result;
+}
+
+void *
+ohash_find(struct ohash *h, unsigned int i)
+{
+	if (h->t[i].p == DELETED)
+		return NULL;
+	else
+		return (void *)h->t[i].p;
+}
+
+void *
+ohash_insert(struct ohash *h, unsigned int i, void *p)
+{
+#ifdef STATS_HASH
+	STAT_HASH_ENTRIES++;
+#endif
+	if (h->t[i].p == DELETED) {
+		h->deleted--;
+		h->t[i].p = p;
+	} else {
+		h->t[i].p = p;
+	/* Arbitrary resize boundary.  Tweak if not efficient enough.  */
+		if (++h->total * 4 > h->size * 3)
+			ohash_resize(h);
+	}
+    	return p;
+}
Index: ohash_qlookupi.c
===================================================================
--- ohash_qlookupi.c	(revision 0)
+++ ohash_qlookupi.c	(revision 0)
@@ -0,0 +1,29 @@
+/* $OpenBSD: ohash_qlookupi.c,v 1.2 2004/06/22 20:00:17 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+
+unsigned int
+ohash_qlookupi(struct ohash *h, const char *s, const char **e)
+{
+	u_int32_t hv;
+
+	hv = ohash_interval(s, e);
+	return ohash_lookup_interval(h, s, *e, hv);
+}
Index: gnum4.c
===================================================================
--- gnum4.c	(revision 198625)
+++ gnum4.c	(working copy)
@@ -1,4 +1,4 @@
-/* $OpenBSD: gnum4.c,v 1.18 2002/04/26 16:15:16 espie Exp $ */
+/* $OpenBSD: gnum4.c,v 1.39 2008/08/21 21:01:04 espie Exp $ */
 
 /*
  * Copyright (c) 1999 Marc Espie
@@ -25,10 +25,7 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
+/* 
  * functions needed to support gnu-m4 extensions, including a fake freezing
  */
 
@@ -36,13 +33,13 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <ctype.h>
+#include <err.h>
 #include <paths.h>
 #include <regex.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <err.h>
 #include <errno.h>
 #include <unistd.h>
 #include "mdef.h"
@@ -54,7 +51,7 @@
 
 /*
  * Support for include path search
- * First search in the the current directory.
+ * First search in the current directory.
  * If not found, and the path is not absolute, include path kicks in.
  * First, -I options, in the order found on the command line.
  * Then M4PATH env variable
@@ -83,8 +80,8 @@
 	n->next = 0;
 	return n;
 }
-
-void
+	
+void 
 addtoincludepath(const char *dirname)
 {
 	struct path_entry *n;
@@ -100,7 +97,7 @@
 }
 
 static void
-ensure_m4path(void)
+ensure_m4path()
 {
 	static int envpathdone = 0;
 	char *envpath;
@@ -111,13 +108,13 @@
 		return;
 	envpathdone = TRUE;
 	envpath = getenv("M4PATH");
-	if (!envpath)
+	if (!envpath) 
 		return;
 	/* for portability: getenv result is read-only */
 	envpath = strdup(envpath);
 	if (!envpath)
 		errx(1, "out of memory");
-	for (sweep = envpath;
+	for (sweep = envpath; 
 	    (path = strsep(&sweep, ":")) != NULL;)
 	    addtoincludepath(path);
 	free(envpath);
@@ -133,7 +130,7 @@
 
 	for (pe = first; pe; pe = pe->next) {
 		snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
-		if ((f = fopen(path, "r")) != NULL) {
+		if ((f = fopen(path, "r")) != 0) {
 			set_input(i, f, path);
 			return i;
 		}
@@ -159,29 +156,32 @@
 	return dopath(i, filename);
 }
 
-void
+void 
 doindir(const char *argv[], int argc)
 {
-	ndptr p;
+	ndptr n;
+	struct macro_definition *p;
 
-	p = lookup(argv[2]);
-	if (p == NULL)
-		errx(1, "undefined macro %s", argv[2]);
+	n = lookup(argv[2]);
+	if (n == NULL || (p = macro_getdef(n)) == NULL)
+		m4errx(1, "indir: undefined macro %s.", argv[2]);
 	argv[1] = p->defn;
-	eval(argv+1, argc-1, p->type);
+	
+	eval(argv+1, argc-1, p->type, is_traced(n));
 }
 
-void
+void 
 dobuiltin(const char *argv[], int argc)
 {
-	int n;
+	ndptr p;
+
 	argv[1] = NULL;
-	n = builtin_type(argv[2]);
-	if (n != -1)
-		eval(argv+1, argc-1, n);
+	p = macro_getbuiltin(argv[2]);
+	if (p != NULL)
+		eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
 	else
-		errx(1, "unknown builtin %s", argv[2]);
-}
+		m4errx(1, "unknown builtin %s.", argv[2]);
+} 
 
 
 /* We need some temporary buffer space, as pb pushes BACK and substitution
@@ -198,11 +198,11 @@
 static void do_subst(const char *, regex_t *, const char *, regmatch_t *);
 static void do_regexpindex(const char *, regex_t *, regmatch_t *);
 static void do_regexp(const char *, regex_t *, const char *, regmatch_t *);
-static void add_sub(size_t, const char *, regex_t *, regmatch_t *);
+static void add_sub(int, const char *, regex_t *, regmatch_t *);
 static void add_replace(const char *, regex_t *, const char *, regmatch_t *);
 #define addconstantstring(s) addchars((s), sizeof(s)-1)
 
-static void
+static void 
 addchars(const char *c, size_t n)
 {
 	if (n == 0)
@@ -212,15 +212,13 @@
 			bufsize = 1024;
 		else
 			bufsize *= 2;
-		buffer = realloc(buffer, bufsize);
-		if (buffer == NULL)
-			errx(1, "out of memory");
+		buffer = xrealloc(buffer, bufsize, NULL);
 	}
 	memcpy(buffer+current, c, n);
 	current += n;
 }
 
-static void
+static void 
 addchar(int c)
 {
 	if (current +1 > bufsize) {
@@ -228,15 +226,13 @@
 			bufsize = 1024;
 		else
 			bufsize *= 2;
-		buffer = realloc(buffer, bufsize);
-		if (buffer == NULL)
-			errx(1, "out of memory");
+		buffer = xrealloc(buffer, bufsize, NULL);
 	}
 	buffer[current++] = c;
 }
 
 static char *
-getstring(void)
+getstring()
 {
 	addchar('\0');
 	current = 0;
@@ -244,23 +240,24 @@
 }
 
 
-static void
+static void 
 exit_regerror(int er, regex_t *re)
 {
 	size_t 	errlen;
 	char 	*errbuf;
 
 	errlen = regerror(er, re, NULL, 0);
-	errbuf = xalloc(errlen);
+	errbuf = xalloc(errlen, 
+	    "malloc in regerror: %lu", (unsigned long)errlen);
 	regerror(er, re, errbuf, errlen);
-	errx(1, "regular expression error: %s", errbuf);
+	m4errx(1, "regular expression error: %s.", errbuf);
 }
 
 static void
-add_sub(size_t n, const char *string, regex_t *re, regmatch_t *pm)
+add_sub(int n, const char *string, regex_t *re, regmatch_t *pm)
 {
 	if (n > re->re_nsub)
-		warnx("No subexpression %zu", n);
+		warnx("No subexpression %d", n);
 	/* Subexpressions that did not match are
 	 * not an error.  */
 	else if (pm[n].rm_so != -1 &&
@@ -273,7 +270,7 @@
 /* Add replacement string to the output buffer, recognizing special
  * constructs and replacing them with substrings of the original string.
  */
-static void
+static void 
 add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
 {
 	const char *p;
@@ -306,7 +303,7 @@
 	}
 }
 
-static void
+static void 
 do_subst(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
 {
 	int error;
@@ -321,11 +318,11 @@
 				flags = REG_NOTBOL;
 		}
 
-		/* NULL length matches are special... We use the `vi-mode'
+		/* NULL length matches are special... We use the `vi-mode' 
 		 * rule: don't allow a NULL-match at the last match
-		 * position.
+		 * position. 
 		 */
-		if (pm[0].rm_so == pm[0].rm_eo &&
+		if (pm[0].rm_so == pm[0].rm_eo && 
 		    string + pm[0].rm_so == last_match) {
 			if (*string == '\0')
 				return;
@@ -346,13 +343,13 @@
 	pbstr(string);
 }
 
-static void
+static void 
 do_regexp(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
 {
 	int error;
 
 	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
-	case 0:
+	case 0: 
 		add_replace(string, re, replace, pm);
 		pbstr(getstring());
 		break;
@@ -363,7 +360,7 @@
 	}
 }
 
-static void
+static void 
 do_regexpindex(const char *string, regex_t *re, regmatch_t *pm)
 {
 	int error;
@@ -386,6 +383,14 @@
 static char *
 twiddle(const char *p)
 {
+	/* + at start of regexp is a normal character for Gnu m4 */
+	if (*p == '^') {
+		addchar(*p);
+		p++;
+	}
+	if (*p == '+') {
+		addchar('\\');
+	}
 	/* This could use strcspn for speed... */
 	while (*p != '\0') {
 		if (*p == '\\') {
@@ -431,25 +436,46 @@
 void
 dopatsubst(const char *argv[], int argc)
 {
-	int error;
-	regex_t re;
-	regmatch_t *pmatch;
-
 	if (argc <= 3) {
 		warnx("Too few arguments to patsubst");
 		return;
 	}
-	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
-	    REG_NEWLINE | REG_EXTENDED);
-	if (error != 0)
-		exit_regerror(error, &re);
+	/* special case: empty regexp */
+	if (argv[3][0] == '\0') {
+		const char *s;
+		size_t len;
+		if (argv[4] && argc > 4) 
+			len = strlen(argv[4]);
+		else
+			len = 0;
+		for (s = argv[2]; *s != '\0'; s++) {
+			addchars(argv[4], len);
+			addchar(*s);
+		}
+	} else {
+		int error;
+		regex_t re;
+		regmatch_t *pmatch;
+		int mode = REG_EXTENDED;
+		size_t l = strlen(argv[3]);
 
-	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
-	do_subst(argv[2], &re,
-	    argc != 4 && argv[4] != NULL ? argv[4] : "", pmatch);
+		if (!mimic_gnu ||
+		    (argv[3][0] == '^') || 
+		    (l > 0 && argv[3][l-1] == '$'))
+			mode |= REG_NEWLINE;
+
+		error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3], 
+		    mode);
+		if (error != 0)
+			exit_regerror(error, &re);
+		
+		pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
+		do_subst(argv[2], &re, 
+		    argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch);
+		free(pmatch);
+		regfree(&re);
+	}
 	pbstr(getstring());
-	free(pmatch);
-	regfree(&re);
 }
 
 void
@@ -463,12 +489,12 @@
 		warnx("Too few arguments to regexp");
 		return;
 	}
-	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
+	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3], 
 	    REG_EXTENDED);
 	if (error != 0)
 		exit_regerror(error, &re);
-
-	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
+	
+	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
 	if (argv[4] == NULL || argc == 4)
 		do_regexpindex(argv[2], &re, pmatch);
 	else
@@ -478,16 +504,111 @@
 }
 
 void
+doformat(const char *argv[], int argc)
+{
+	const char *format = argv[2];
+	int pos = 3;
+	int left_padded;
+	long width;
+	size_t l;
+	const char *thisarg;
+	char temp[2];
+	long extra;
+
+	while (*format != 0) {
+		if (*format != '%') {
+			addchar(*format++);
+			continue;
+		}
+
+		format++;
+		if (*format == '%') {
+			addchar(*format++);
+			continue;
+		}
+		if (*format == 0) {
+			addchar('%');
+			break;
+		}
+
+		if (*format == '*') {
+			format++;
+			if (pos >= argc)
+				m4errx(1, 
+				    "Format with too many format specifiers.");
+			width = strtol(argv[pos++], NULL, 10);
+		} else {
+			width = strtol(format, (char **)&format, 10);
+		}
+		if (width < 0) {
+			left_padded = 1;
+			width = -width;
+		} else {
+			left_padded = 0;
+		}
+		if (*format == '.') {
+			format++;
+			if (*format == '*') {
+				format++;
+				if (pos >= argc)
+					m4errx(1, 
+					    "Format with too many format specifiers.");
+				extra = strtol(argv[pos++], NULL, 10);
+			} else {
+				extra = strtol(format, (char **)&format, 10);
+			}
+		} else {
+			extra = LONG_MAX;
+		}
+		if (pos >= argc)
+			m4errx(1, "Format with too many format specifiers.");
+		switch(*format) {
+		case 's':
+			thisarg = argv[pos++];
+			break;
+		case 'c':
+			temp[0] = strtoul(argv[pos++], NULL, 10);
+			temp[1] = 0;
+			thisarg = temp;
+			break;
+		default:
+			m4errx(1, "Unsupported format specification: %s.", 
+			    argv[2]);
+		}
+		format++;
+		l = strlen(thisarg);
+		if (l > extra)
+			l = extra;
+		if (!left_padded) {
+			while (l < width--)
+				addchar(' ');
+		}
+		addchars(thisarg, l);
+		if (left_padded) {
+			while (l < width--)
+				addchar(' ');
+		}
+	}
+	pbstr(getstring());
+}
+
+void
 doesyscmd(const char *cmd)
 {
 	int p[2];
 	pid_t pid, cpid;
+	char *argv[4];
 	int cc;
 	int status;
 
 	/* Follow gnu m4 documentation: first flush buffers. */
 	fflush(NULL);
 
+	argv[0] = "sh";
+	argv[1] = "-c";
+	argv[2] = (char *)cmd;
+	argv[3] = NULL;
+
 	/* Just set up standard output, share stderr and stdin with m4 */
 	if (pipe(p) == -1)
 		err(1, "bad pipe");
@@ -499,7 +620,7 @@
 		(void) close(p[0]);
 		(void) dup2(p[1], 1);
 		(void) close(p[1]);
-		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
+		execv(_PATH_BSHELL, argv);
 		exit(1);
 	default:
 		/* Read result in two stages, since m4's buffer is
@@ -518,3 +639,18 @@
 		pbstr(getstring());
 	}
 }
+
+void
+getdivfile(const char *name)
+{
+	FILE *f;
+	int c;
+
+	f = fopen(name, "r");
+	if (!f)
+		return;
+
+	while ((c = getc(f))!= EOF)
+		putc(c, active);
+	(void) fclose(f);
+}
Index: ohash_entries.c
===================================================================
--- ohash_entries.c	(revision 0)
+++ ohash_entries.c	(revision 0)
@@ -0,0 +1,26 @@
+/* $OpenBSD: ohash_entries.c,v 1.2 2004/06/22 20:00:16 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+
+unsigned int
+ohash_entries(struct ohash *h)
+{
+	return h->total - h->deleted;
+}
Index: Makefile
===================================================================
--- Makefile	(revision 198625)
+++ Makefile	(working copy)
@@ -1,12 +1,25 @@
-#	$OpenBSD: Makefile,v 1.10 2002/04/26 13:13:41 espie Exp $
-# $FreeBSD$
+#	$OpenBSD: Makefile,v 1.11 2004/05/12 21:17:03 espie Exp $
 
 # -DEXTENDED 
 # 	if you want the paste & spaste macros.
 
 PROG=	m4
-CFLAGS+=-DEXTENDED
+CFLAGS+=-DEXTENDED -I.
+CDIAGFLAGS=-W -Wall -Wstrict-prototypes -pedantic \
+	-Wno-unused -Wno-char-subscripts -Wno-sign-compare
 
-SRCS=	eval.c expr.c look.c main.c misc.c gnum4.c trace.c
+LDADD= -ly -ll
+DPADD= ${LIBY} ${LIBL}
 
+SRCS=	eval.c expr.c look.c main.c misc.c gnum4.c trace.c tokenizer.l parser.y
+MAN=	m4.1
+SRCS+=	ohash_create_entry.c ohash_delete.c ohash_do.c ohash_entries.c \
+	ohash_enum.c ohash_init.c ohash_interval.c \
+	ohash_lookup_interval.c ohash_lookup_memory.c \
+	ohash_qlookup.c ohash_qlookupi.c
+
+tokenizer.o: parser.h
+
+CLEANFILES+=parser.c parser.h tokenizer.o
+
 .include <bsd.prog.mk>
Index: TEST/hash.m4
===================================================================
--- TEST/hash.m4	(revision 198625)
+++ TEST/hash.m4	(working copy)
@@ -1,4 +1,4 @@
-#	$OpenBSD: hash.m4,v 1.2 1996/06/26 05:36:19 deraadt Exp $
+#	$OpenBSD: hash.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
 #	$NetBSD: hash.m4,v 1.4 1995/09/28 05:37:58 tls Exp $
 #
 # Copyright (c) 1989, 1993
@@ -15,11 +15,7 @@
 # 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.
 #
Index: TEST/string.m4
===================================================================
--- TEST/string.m4	(revision 198625)
+++ TEST/string.m4	(working copy)
@@ -1,4 +1,4 @@
-#	$OpenBSD: string.m4,v 1.2 1996/06/26 05:36:20 deraadt Exp $
+#	$OpenBSD: string.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
 #	$NetBSD: string.m4,v 1.4 1995/09/28 05:38:03 tls Exp $
 #
 # Copyright (c) 1989, 1993
@@ -15,11 +15,7 @@
 # 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.
 #
Index: TEST/sqroot.m4
===================================================================
--- TEST/sqroot.m4	(revision 198625)
+++ TEST/sqroot.m4	(working copy)
@@ -1,4 +1,4 @@
-#	$OpenBSD: sqroot.m4,v 1.2 1996/06/26 05:36:20 deraadt Exp $
+#	$OpenBSD: sqroot.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
 #	$NetBSD: sqroot.m4,v 1.4 1995/09/28 05:38:01 tls Exp $
 #
 # Copyright (c) 1989, 1993
@@ -15,11 +15,7 @@
 # 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.
 #
Index: TEST/ack.m4
===================================================================
--- TEST/ack.m4	(revision 198625)
+++ TEST/ack.m4	(working copy)
@@ -1,4 +1,4 @@
-#	$OpenBSD: ack.m4,v 1.2 1996/06/26 05:36:18 deraadt Exp $
+#	$OpenBSD: ack.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
 #	$NetBSD: ack.m4,v 1.4 1995/09/28 05:37:54 tls Exp $
 #
 # Copyright (c) 1989, 1993
@@ -15,11 +15,7 @@
 # 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.
 #
Index: TEST/hanoi.m4
===================================================================
--- TEST/hanoi.m4	(revision 198625)
+++ TEST/hanoi.m4	(working copy)
@@ -1,4 +1,4 @@
-#	$OpenBSD: hanoi.m4,v 1.2 1996/06/26 05:36:19 deraadt Exp $
+#	$OpenBSD: hanoi.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
 #	$NetBSD: hanoi.m4,v 1.4 1995/09/28 05:37:56 tls Exp $
 #
 # Copyright (c) 1989, 1993
@@ -15,11 +15,7 @@
 # 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.
 #
Index: TEST/test.m4
===================================================================
--- TEST/test.m4	(revision 198625)
+++ TEST/test.m4	(working copy)
@@ -1,4 +1,4 @@
-#	$OpenBSD: test.m4,v 1.2 1996/06/26 05:36:21 deraadt Exp $
+#	$OpenBSD: test.m4,v 1.3 2003/06/03 02:56:11 millert Exp $
 #	$NetBSD: test.m4,v 1.4 1995/09/28 05:38:05 tls Exp $
 #
 # Copyright (c) 1989, 1993
@@ -15,11 +15,7 @@
 # 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.
 #
Index: m4.1
===================================================================
--- m4.1	(revision 198625)
+++ m4.1	(working copy)
@@ -1,201 +1,265 @@
-.\"	@(#) $OpenBSD: m4.1,v 1.24 2002/04/18 18:57:23 espie Exp $
-.\" $FreeBSD$
+.\"	@(#) $OpenBSD: m4.1,v 1.56 2009/10/14 17:19:47 sthen Exp $
 .\"
-.Dd July 3, 2004
+.\" Copyright (c) 1989, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Ozan Yigit at York University.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 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. 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd $Mdocdate: October 14 2009 $
 .Dt M4 1
 .Os
 .Sh NAME
 .Nm m4
 .Nd macro language processor
 .Sh SYNOPSIS
-.Nm
+.Nm m4
+.Op Fl gPs
+.Oo
+.Sm off
+.Fl D Ar name Op No = Ar value
+.Sm on
+.Oc
 .Op Fl d Ar flags
-.Op Fl t Ar name
-.Op Fl gs
-.Op Fl D Ar name Ns Op = Ns Ar value
-.Op Fl U Ar name
 .Op Fl I Ar dirname
+.Op Fl o Ar filename
+.Bk -words
+.Op Fl t Ar macro
+.Op Fl U Ns Ar name
 .Op Ar
+.Ek
 .Sh DESCRIPTION
 The
-.Nm
+.Nm m4
 utility is a macro processor that can be used as a front end to any
 language (e.g., C, ratfor, fortran, lex, and yacc).
-The
-.Nm
-utility reads from the standard input and writes
-the processed text to the standard output.
+If no input files are given,
+.Nm m4
+reads from the standard input,
+otherwise files specified on the command line are
+processed in the given order.
+Input files can be regular files, files in the m4 include paths, or a
+single dash
+.Pq Sq - ,
+denoting standard input.
+.Nm m4
+writes
+the processed text to the standard output, unless told otherwise.
 .Pp
-Macro calls have the form
-.Ic name Ns Pq Ar argument1 Ns Op , Ar argument2 , ... , argumentN .
+Macro calls have the form name(argument1[, argument2, ..., argumentN]).
 .Pp
 There cannot be any space following the macro name and the open
 parenthesis
-.Pq Ql \&( .
+.Pq Sq ( .
 If the macro name is not followed by an open
 parenthesis it is processed with no arguments.
 .Pp
 Macro names consist of a leading alphabetic or underscore
 possibly followed by alphanumeric or underscore characters, e.g.,
 valid macro names match the pattern
-.Dq Li [a-zA-Z_][a-zA-Z0-9_]* .
+.Dq [a-zA-Z_][a-zA-Z0-9_]* .
 .Pp
 In arguments to macros, leading unquoted space, tab, and newline
-.Pq Ql \en
+.Pq Sq \en
 characters are ignored.
-To quote strings, use left and right single
-quotes (e.g.,
-.Sq "\ this is a string with a leading space" ) .
+To quote strings, use left and right single quotes
+.Po e.g.,\ \&
+.Sq "\ this is a string with a leading space"
+.Pc .
 You can change the quote characters with the
 .Ic changequote
 built-in macro.
 .Pp
-Most built-ins do not make any sense without arguments, and hence are not
+Most built-ins don't make any sense without arguments, and hence are not
 recognized as special when not followed by an open parenthesis.
 .Pp
 The options are as follows:
-.Bl -tag -width indent
-.It Fl s
-Emit
-.Ic #line
-directives for
-.Xr cpp 1 .
-.It Fl D Ar name Ns Op = Ns Ar value
+.Bl -tag -width Ds
+.It Fl D Ns Ar name Ns Oo
+.Pf = Ns Ar value
+.Oc
 Define the symbol
 .Ar name
 to have some value (or
 .Dv NULL ) .
-.It Fl U Ar name
-Undefine the symbol
-.Ar name .
-.It Fl I Ar dirname
-Add directory
-.Ar dirname
-to the include path.
-.It Fl d Ar flags
+.It Fl d Ar "flags"
 Set trace flags.
-The
 .Ar flags
-argument may hold the following:
-.Pp
-.Bl -tag -width indent -compact
-.It Cm a
-print macro arguments
-.It Cm c
-print macro expansion over several lines
-.It Cm e
-print result of macro expansion
-.It Cm f
-print filename location
-.It Cm l
-print line number
-.It Cm q
-quote arguments and expansion with the current quotes
-.It Cm t
-start with all macros traced
-.It Cm x
-number macro expansions
-.It Cm V
-turn on all options
+may hold the following:
+.Bl -tag -width Ds
+.It Ar a
+print macro arguments.
+.It Ar c
+print macro expansion over several lines.
+.It Ar e
+print result of macro expansion.
+.It Ar f
+print filename location.
+.It Ar l
+print line number.
+.It Ar q
+quote arguments and expansion with the current quotes.
+.It Ar t
+start with all macros traced.
+.It Ar x
+number macro expansions.
+.It Ar V
+turn on all options.
 .El
 .Pp
 By default, trace is set to
-.Cm eq .
+.Qq eq .
+.It Fl g
+Activate GNU-m4 compatibility mode.
+In this mode, translit handles simple character
+ranges (e.g., a-z), regular expressions mimic emacs behavior,
+multiple m4wrap calls are handled as a stack,
+the number of diversions is unlimited,
+empty names for macro definitions are allowed,
+and eval understands
+.Sq 0rbase:value
+numbers.
+.It Fl I Ar "dirname"
+Add directory
+.Ar dirname
+to the include path.
+.It Fl o Ar filename
+Send trace output to
+.Ar filename .
+.It Fl P
+Prefix all built-in macros with
+.Sq m4_ .
+For example, instead of writing
+.Ic define ,
+use
+.Ic m4_define .
+.It Fl s
+Output line synchronization directives, suitable for
+.Xr cpp 1 .
 .It Fl t Ar macro
 Turn tracing on for
 .Ar macro .
-.It Fl g
-Activate GNU-m4 compatibility mode.
-In this mode,
-.Ic changequote
-with two empty parameters deactivates quotes,
-.Ic translit
-handles simple character ranges (e.g.,
-.Li a-z ) ,
-regular expressions mimic
-.Xr emacs 1
-behavior,
-and the number of diversions is unlimited.
+.It Fl "U" Ns Ar "name"
+Undefine the symbol
+.Ar name .
 .El
 .Sh SYNTAX
-The
-.Nm
-utility provides the following built-in macros.
+.Nm m4
+provides the following built-in macros.
 They may be redefined, losing their original meaning.
 Return values are null unless otherwise stated.
-.Bl -tag -width ".Ic changequote"
-.It Ic builtin
-Calls a built-in by its name, overriding possible redefinitions.
-.It Ic changecom
-Changes the start and end comment sequences.
-The default is the pound sign
-.Pq Ql #
+.Bl -tag -width changequote
+.It Fn builtin name
+Calls a built-in by its
+.Fa name ,
+overriding possible redefinitions.
+.It Fn changecom startcomment endcomment
+Changes the start comment and end comment sequences.
+Comment sequences may be up to five characters long.
+The default values are the hash sign
 and the newline character.
-With no arguments, the comment sequence is reset to the default,
-in GNU
-.Nm
-mode, comments are turned off.
-The maximum length for a comment marker is five characters.
-.It Ic changequote
-Defines the quote symbols to be the first and second arguments.
-The symbols may be up to five characters long.
-If no arguments are
-given it restores the default open and close single quotes.
-.It Ic decr
-Decrements the argument by 1.
-The argument must be a valid numeric string.
-.It Ic define
-Define a new macro named by the first argument to have the
-value of the second argument.
+.Bd -literal -offset indent
+# This is a comment
+.Ed
+.Pp
+With no arguments, comments are turned off.
+With one single argument, the end comment sequence is set
+to the newline character.
+.It Fn changequote beginquote endquote
+Defines the open quote and close quote sequences.
+Quote sequences may be up to five characters long.
+The default values are the backquote character and the quote
+character.
+.Bd -literal -offset indent
+`Here is a quoted string'
+.Ed
+.Pp
+With no arguments, the default quotes are restored.
+With one single argument, the close quote sequence is set
+to the newline character.
+.It Fn decr arg
+Decrements the argument
+.Fa arg
+by 1.
+The argument
+.Fa arg
+must be a valid numeric string.
+.It Fn define name value
+Define a new macro named by the first argument
+.Fa name
+to have the
+value of the second argument
+.Fa value .
 Each occurrence of
-.Sq Li $ Ns Ar n
+.Sq $n
 (where
 .Ar n
 is 0 through 9) is replaced by the
 .Ar n Ns 'th
 argument.
-.Ql $0
+.Sq $0
 is the name of the calling macro.
 Undefined arguments are replaced by a null string.
-.Ql $#
+.Sq $#
 is replaced by the number of arguments;
-.Ql $*
+.Sq $*
 is replaced by all arguments comma separated;
-.Ql $@
+.Sq $@
 is the same as
-.Ql $*
+.Sq $*
 but all arguments are quoted against further expansion.
-.It Ic defn
+.It Fn defn name ...
 Returns the quoted definition for each argument.
 This can be used to rename
 macro definitions (even for built-in macros).
-.It Ic divert
+.It Fn divert num
 There are 10 output queues (numbered 0-9).
 At the end of processing
-.Nm
+.Nm m4
 concatenates all the queues in numerical order to produce the
 final output.
 Initially the output queue is 0.
-The
-.Ic divert
+The divert
 macro allows you to select a new output queue (an invalid argument
-passed to
-.Ic divert
-causes output to be discarded).
+passed to divert causes output to be discarded).
 .It Ic divnum
 Returns the current output queue number.
 .It Ic dnl
-Discards input characters up to and including the next newline.
-.It Ic dumpdef
+Discard input characters up to and including the next newline.
+.It Fn dumpdef name ...
 Prints the names and definitions for the named items, or for everything
 if no arguments are passed.
-.It Ic errprint
+.It Fn errprint msg
 Prints the first argument on the standard error output stream.
-.It Ic esyscmd
+.It Fn esyscmd cmd
 Passes its first argument to a shell and returns the shell's standard output.
 Note that the shell shares its standard input and standard error with
-.Nm .
-.It Ic eval
+.Nm m4 .
+.It Fn eval expr
 Computes the first argument as an arithmetic expression using 32-bit
 arithmetic.
 Operators are the standard C ternary, arithmetic, logical,
@@ -203,30 +267,46 @@
 You can specify
 octal, decimal, and hexadecimal numbers as in C.
 The second argument (if any)
-specifies the radix for the result, and the third argument (if any)
+specifies the radix for the result and the third argument (if any)
 specifies the minimum number of digits in the result.
-.It Ic expr
+.It Fn expr expr
 This is an alias for
 .Ic eval .
-.It Ic ifdef
+.It Fn format formatstring arg1 ...
+Returns
+.Fa formatstring
+with escape sequences substituted with
+.Fa arg1
+and following arguments, in a way similar to
+.Xr printf 3 .
+This built-in is only available in GNU-m4 compatibility mode, and the only
+parameters implemented are there for autoconf compatibility:
+left-padding flag, an optional field width, a maximum field width,
+*-specified field widths, and the %s and %c data type.
+.It Fn ifdef name yes no
 If the macro named by the first argument is defined then return the second
 argument, otherwise the third.
 If there is no third argument, the value is
 .Dv NULL .
 The word
-.Ic unix
+.Qq unix
 is predefined.
-.It Ic ifelse
-If the first argument matches the second argument then
-.Ic ifelse
+.It Fn ifelse a b yes ...
+If the first argument
+.Fa a
+matches the second argument
+.Fa b
+then
+.Fn ifelse
 returns
-the third argument.
-If the match fails, the three arguments are
+the third argument
+.Fa yes .
+If the match fails the three arguments are
 discarded and the next three arguments are used until there is
 zero or one arguments left, either this last argument or
 .Dv NULL
 is returned if no other matches were found.
-.It Ic include
+.It Fn include name
 Returns the contents of the file specified in the first argument.
 If the file is not found as is, look through the include path:
 first the directories specified with
@@ -234,114 +314,114 @@
 on the command line, then the environment variable
 .Ev M4PATH ,
 as a colon-separated list of directories.
-Aborts with an error message if the file cannot be included.
-.It Ic incr
+Include aborts with an error message if the file cannot be included.
+.It Fn incr arg
 Increments the argument by 1.
 The argument must be a valid numeric string.
-.It Ic index
+.It Fn index string substring
 Returns the index of the second argument in the first argument (e.g.,
-.Fn index "the quick brown fox jumped" fox
+.Ic index(the quick brown fox jumped, fox)
 returns 16).
 If the second
-argument is not found,
-.Ic index
-returns \-1.
-.It Ic indir
-Indirectly calls the macro whose name is passed as the first arguments,
-with the remaining arguments passed as first, etc.\& arguments.
-.It Ic len
+argument is not found index returns \-1.
+.It Fn indir macro arg1 ...
+Indirectly calls the macro whose name is passed as the first argument,
+with the remaining arguments passed as first, ... arguments.
+.It Fn len arg
 Returns the number of characters in the first argument.
 Extra arguments
 are ignored.
-.It Ic m4exit
+.It Fn m4exit code
 Immediately exits with the return value specified by the first argument,
 0 if none.
-.It Ic m4wrap
+.It Fn m4wrap todo
 Allows you to define what happens at the final
 .Dv EOF ,
 usually for cleanup purposes (e.g.,
-.Fn m4wrap cleanup(tempfile)
-causes the macro
-.Ic cleanup
-to be
+.Ic m4wrap("cleanup(tempfile)")
+causes the macro cleanup to be
 invoked after all other processing is done).
-.It Ic maketemp
-Translates the string
-.Dq Li XXXXX
-in the first argument with the current process
-ID leaving other characters alone.
+.Pp
+Multiple calls to
+.Fn m4wrap
+get inserted in sequence at the final
+.Dv EOF .
+.It Fn maketemp template
+Invokes
+.Xr mkstemp 3
+on the first argument, and returns the modified string.
 This can be used to create unique
 temporary file names.
-.It Ic paste
+.It Fn paste file
 Includes the contents of the file specified by the first argument without
 any macro processing.
 Aborts with an error message if the file cannot be
 included.
-.It Ic patsubst
+.It Fn patsubst string regexp replacement
 Substitutes a regular expression in a string with a replacement string.
 Usual substitution patterns apply: an ampersand
-.Pq Ql &
+.Pq Sq \&&
 is replaced by the string matching the regular expression.
 The string
-.Sq \e Ns Ar # ,
+.Sq \e# ,
 where
-.Ar #
+.Sq #
 is a digit, is replaced by the corresponding back-reference.
-.It Ic popdef
+.It Fn popdef arg ...
 Restores the
 .Ic pushdef Ns ed
 definition for each argument.
-.It Ic pushdef
+.It Fn pushdef macro def
 Takes the same arguments as
 .Ic define ,
 but it saves the definition on a
 stack for later retrieval by
-.Ic popdef .
-.It Ic regexp
+.Fn popdef .
+.It Fn regexp string regexp replacement
 Finds a regular expression in a string.
 If no further arguments are given,
 it returns the first match position or \-1 if no match.
 If a third argument
 is provided, it returns the replacement string, with sub-patterns replaced.
-.It Ic shift
+.It Fn shift arg1 ...
 Returns all but the first argument, the remaining arguments are
 quoted and pushed back with commas in between.
 The quoting
 nullifies the effect of the extra scan that will subsequently be
 performed.
-.It Ic sinclude
+.It Fn sinclude file
 Similar to
 .Ic include ,
 except it ignores any errors.
-.It Ic spaste
+.It Fn spaste file
 Similar to
-.Ic paste ,
+.Fn paste ,
 except it ignores any errors.
-.It Ic substr
+.It Fn substr string offset length
 Returns a substring of the first argument starting at the offset specified
 by the second argument and the length specified by the third argument.
 If no third argument is present it returns the rest of the string.
-.It Ic syscmd
+.It Fn syscmd cmd
 Passes the first argument to the shell.
 Nothing is returned.
 .It Ic sysval
 Returns the return value from the last
 .Ic syscmd .
-.It Ic traceon
+.It Fn traceon arg ...
 Enables tracing of macro expansions for the given arguments, or for all
 macros if no argument is given.
-.It Ic traceoff
+.It Fn traceoff arg ...
 Disables tracing of macro expansions for the given arguments, or for all
 macros if no argument is given.
-.It Ic translit
+.It Fn translit string mapfrom mapto
 Transliterate the characters in the first argument from the set
 given by the second argument to the set given by the third.
 You cannot use
 .Xr tr 1
 style abbreviations.
-.It Ic undefine
+.It Fn undefine name1 ...
 Removes the definition for the macros specified by its arguments.
-.It Ic undivert
+.It Fn undivert arg ...
 Flushes the named output queues (or all queues if no arguments).
 .It Ic unix
 A pre-defined macro for testing the OS platform.
@@ -350,37 +430,20 @@
 .It Ic __file__
 Returns the current file's name.
 .El
-.Sh EXIT STATUS
-.Ex -std
-.Pp
+.Sh STANDARDS
 The
-.Ic m4exit
-macro may be used to change the exit status from the input file.
-.Sh COMPATIBILITY
-The
 .Nm
-utility follows the
-.St -susv2 ,
-along with a few extensions taken from GNU-m4.
-Flags
-.Fl I , d ,
-and
-.Fl t
-are non-standard.
+utility is compliant with the
+.St -p1003.1-2008
+specification.
 .Pp
-The output format of tracing and of
-.Ic dumpdef
-are not specified in any standard,
-are likely to change and should not be relied upon.
-The current format of tracing is closely modeled on GNU-m4,
-to allow
-.Nm autoconf
-to work.
-.Pp
-For portability, one should not use the macros
+The flags
+.Op Fl dgIot
+and the macros
 .Ic builtin ,
 .Ic esyscmd ,
 .Ic expr ,
+.Ic format ,
 .Ic indir ,
 .Ic paste ,
 .Ic patsubst ,
@@ -389,33 +452,43 @@
 .Ic unix ,
 .Ic __line__ ,
 and
-.Ic __file__ .
+.Ic __file__
+are extensions to that specification.
 .Pp
+The output format of tracing and of
+.Ic dumpdef
+are not specified in any standard,
+are likely to change and should not be relied upon.
+The current format of tracing is closely modelled on
+.Nm gnu-m4 ,
+to allow
+.Nm autoconf
+to work.
+.Pp
+The built-ins
+.Ic pushdef
+and
+.Ic popdef
+handle macro definitions as a stack.
+However,
+.Ic define
+interacts with the stack in an undefined way.
+In this implementation,
+.Ic define
+replaces the top-most definition only.
+Other implementations may erase all definitions on the stack instead.
+.Pp
 All built-ins do expand without arguments in many other
-.Nm
-implementations.
+.Nm m4 .
 .Pp
 Many other
 .Nm
-implementations have dire size limitations with respect to buffer sizes.
-.Sh STANDARDS
-The
-.Nm
-utility
-conforms to
-.St -p1003.1-2001 .
-.Sh HISTORY
-An
-.Nm
-command appeared in PWB UNIX.
+have dire size limitations with respect to buffer sizes.
 .Sh AUTHORS
 .An -nosplit
 .An Ozan Yigit Aq oz@sis.yorku.ca
 and
 .An Richard A. O'Keefe Aq ok@goanna.cs.rmit.OZ.AU .
+.Pp
 GNU-m4 compatibility extensions by
 .An Marc Espie Aq espie@cvs.openbsd.org .
-.Sh BUGS
-The
-.Nm
-utility does not recognize multibyte characters.
Index: ohash_lookup_interval.c
===================================================================
--- ohash_lookup_interval.c	(revision 0)
+++ ohash_lookup_interval.c	(revision 0)
@@ -0,0 +1,68 @@
+/* $OpenBSD: ohash_lookup_interval.c,v 1.3 2006/01/16 15:52:25 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+	
+unsigned int
+ohash_lookup_interval(struct ohash *h, const char *start, const char *end, 
+    uint32_t hv)
+{
+	unsigned int 	i, incr;
+	unsigned int	empty;
+	
+#ifdef STATS_HASH
+	STAT_HASH_LOOKUP++;
+#endif
+	empty = NONE;
+	i = hv % h->size;
+	incr = ((hv % (h->size-2)) & ~1) + 1;
+	while (h->t[i].p != NULL) {
+#ifdef STATS_HASH
+		STAT_HASH_LENGTH++;
+#endif
+		if (h->t[i].p == DELETED) {
+			if (empty == NONE)
+				empty = i;
+		} else if (h->t[i].hv == hv && 
+		    strncmp(h->t[i].p+h->info.key_offset, start, 
+		    	end - start) == 0 &&
+		    (h->t[i].p+h->info.key_offset)[end-start] == '\0') {
+		    	if (empty != NONE) {
+				h->t[empty].hv = hv;
+				h->t[empty].p = h->t[i].p;
+				h->t[i].p = DELETED;
+				return empty;
+			} else {
+#ifdef STATS_HASH
+				STAT_HASH_POSITIVE++;
+#endif
+				return i;
+			}
+		}
+		i += incr;
+		if (i >= h->size) 
+			i -= h->size;
+	}
+
+	/* Found an empty position.  */
+	if (empty != NONE) 
+		i = empty;
+	h->t[i].hv = hv;
+	return i;
+}
Index: stdd.h
===================================================================
--- stdd.h	(revision 198625)
+++ stdd.h	(working copy)
@@ -1,4 +1,4 @@
-/*	$OpenBSD: stdd.h,v 1.4 1999/11/09 18:16:18 deraadt Exp $	*/
+/*	$OpenBSD: stdd.h,v 1.5 2003/06/03 02:56:10 millert Exp $	*/
 /*	$NetBSD: stdd.h,v 1.2 1995/09/28 05:37:50 tls Exp $	*/
 
 /*-
@@ -16,11 +16,7 @@
  * 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.
  *
@@ -37,7 +33,6 @@
  * SUCH DAMAGE.
  *
  *	@(#)stdd.h	8.1 (Berkeley) 6/6/93
- * $FreeBSD$
  */
 
 /*
@@ -49,9 +44,9 @@
 
 #define iswhite(c) ((c) == ' ' || (c) == '\t')
 
-/*
- * STREQ is an optimised strcmp(a,b)==0
- * STREQN is an optimised strncmp(a,b,n)==0; assumes n > 0
+/* 
+ * STREQ is an optimised strcmp(a,b)==0 
+ * STREQN is an optimised strncmp(a,b,n)==0; assumes n > 0 
  */
 #define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
 #define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0)
Index: look.c
===================================================================
--- look.c	(revision 198625)
+++ look.c	(working copy)
@@ -1,4 +1,4 @@
-/*	$OpenBSD: look.c,v 1.10 2002/04/26 16:15:16 espie Exp $	*/
+/*	$OpenBSD: look.c,v 1.21 2009/10/14 17:23:17 sthen Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -15,11 +15,7 @@
  * 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.
  *
@@ -36,115 +32,251 @@
  * SUCH DAMAGE.
  */
 
-#if 0
-#ifndef lint
-static char sccsid[] = "@(#)look.c	8.1 (Berkeley) 6/6/93";
-#endif /* not lint */
-#endif
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
 /*
  * look.c
  * Facility: m4 macro processor
  * by: oz
  */
 
-#include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
 #include <stddef.h>
 #include <string.h>
+#include <ohash.h>
 #include "mdef.h"
 #include "stdd.h"
 #include "extern.h"
 
-static void freent(ndptr);
+static void *hash_alloc(size_t, void *);
+static void hash_free(void *, size_t, void *);
+static void *element_alloc(size_t, void *);
+static void setup_definition(struct macro_definition *, const char *, 
+    const char *);
 
-unsigned int
-hash(const char *name)
+static struct ohash_info macro_info = {
+	offsetof(struct ndblock, name),
+	NULL, hash_alloc, hash_free, element_alloc };
+
+struct ohash macros;
+
+/* Support routines for hash tables.  */
+void *
+hash_alloc(s, u)
+	size_t s;
+	void *u 	UNUSED;
 {
-	unsigned int h = 0;
-	while (*name)
-		h = (h << 5) + h + *name++;
-	return (h);
+	void *storage = xalloc(s, "hash alloc");
+	if (storage)
+		memset(storage, 0, s);
+	return storage;
 }
 
+void
+hash_free(p, s, u)
+	void *p;
+	size_t s	UNUSED;
+	void *u 	UNUSED;
+{
+	free(p);
+}
+
+void *
+element_alloc(s, u)
+	size_t s;
+	void *u 	UNUSED;
+{
+	return xalloc(s, "element alloc");
+}
+
+void
+init_macros()
+{
+	ohash_init(&macros, 10, &macro_info);
+}
+
 /*
  * find name in the hash table
  */
-ndptr
+ndptr 
 lookup(const char *name)
 {
+	return ohash_find(&macros, ohash_qlookup(&macros, name));
+}
+
+struct macro_definition *
+lookup_macro_definition(const char *name)
+{
 	ndptr p;
-	unsigned int h;
 
-	h = hash(name);
-	for (p = hashtab[h % HASHSIZE]; p != nil; p = p->nxtptr)
-		if (h == p->hv && STREQ(name, p->name))
-			break;
-	return (p);
+	p = ohash_find(&macros, ohash_qlookup(&macros, name));
+	if (p)
+		return p->d;
+	else
+		return NULL;
 }
 
-/*
- * hash and create an entry in the hash table.
- * The new entry is added in front of a hash bucket.
- */
-ndptr
-addent(const char *name)
+static void 
+setup_definition(struct macro_definition *d, const char *defn, const char *name)
 {
-	unsigned int h;
 	ndptr p;
 
-	h = hash(name);
-	p = (ndptr) xalloc(sizeof(struct ndblock));
-	p->nxtptr = hashtab[h % HASHSIZE];
-	hashtab[h % HASHSIZE] = p;
-	p->name = xstrdup(name);
-	p->hv = h;
-	return p;
+	if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0 &&
+	    (p = macro_getbuiltin(defn+sizeof(BUILTIN_MARKER)-1)) != NULL) {
+		d->type = macro_builtin_type(p);
+		d->defn = xstrdup(defn+sizeof(BUILTIN_MARKER)-1);
+	} else {
+		if (!*defn)
+			d->defn = null;
+		else
+			d->defn = xstrdup(defn);
+		d->type = MACRTYPE;
+	}
+	if (STREQ(name, defn))
+		d->type |= RECDEF;
 }
 
-static void
-freent(ndptr p)
+static ndptr
+create_entry(const char *name)
 {
-	free((char *) p->name);
-	if (p->defn != null)
-		free((char *) p->defn);
-	free((char *) p);
+	const char *end = NULL;
+	unsigned int i;
+	ndptr n;
+
+	i = ohash_qlookupi(&macros, name, &end);
+	n = ohash_find(&macros, i);
+	if (n == NULL) {
+		n = ohash_create_entry(&macro_info, name, &end);
+		ohash_insert(&macros, i, n);
+		n->trace_flags = FLAG_NO_TRACE;
+		n->builtin_type = MACRTYPE;
+		n->d = NULL;
+	}
+	return n;
 }
 
-/*
- * remove an entry from the hashtable
- */
 void
-remhash(const char *name, int all)
+macro_define(const char *name, const char *defn)
 {
-	unsigned int h;
-	ndptr xp, tp, mp;
+	ndptr n = create_entry(name);
+	if (n->d != NULL) {
+		if (n->d->defn != null)
+			free(n->d->defn);
+	} else {
+		n->d = xalloc(sizeof(struct macro_definition), NULL);
+		n->d->next = NULL;
+	}
+	setup_definition(n->d, defn, name);
+}
 
-	h = hash(name);
-	mp = hashtab[h % HASHSIZE];
-	tp = nil;
-	while (mp != nil) {
-		if (mp->hv == h && STREQ(mp->name, name)) {
-			mp = mp->nxtptr;
-			if (tp == nil) {
-				freent(hashtab[h % HASHSIZE]);
-				hashtab[h % HASHSIZE] = mp;
-			}
-			else {
-				xp = tp->nxtptr;
-				tp->nxtptr = mp;
-				freent(xp);
-			}
-			if (!all)
-				break;
+void
+macro_pushdef(const char *name, const char *defn)
+{
+	ndptr n;
+	struct macro_definition *d;
+	
+	n = create_entry(name);
+	d = xalloc(sizeof(struct macro_definition), NULL);
+	d->next = n->d;
+	n->d = d;
+	setup_definition(n->d, defn, name);
+}
+
+void
+macro_undefine(const char *name)
+{
+	ndptr n = lookup(name);
+	if (n != NULL) {
+		struct macro_definition *r, *r2;
+
+		for (r = n->d; r != NULL; r = r2) {
+			r2 = r->next;
+			if (r->defn != null)
+				free(r->defn);
+			free(r);
 		}
-		else {
-			tp = mp;
-			mp = mp->nxtptr;
+		n->d = NULL;
+	}
+}
+
+void
+macro_popdef(const char *name)
+{
+	ndptr n = lookup(name);
+
+	if (n != NULL) {
+		struct macro_definition *r = n->d;
+		if (r != NULL) {
+			n->d = r->next;
+			if (r->defn != null)
+				free(r->defn);
+			free(r);
 		}
 	}
 }
+
+void
+macro_for_all(void (*f)(const char *, struct macro_definition *))
+{
+	ndptr n;
+	unsigned int i;
+
+	for (n = ohash_first(&macros, &i); n != NULL; 
+	    n = ohash_next(&macros, &i))
+		if (n->d != NULL)
+			f(n->name, n->d);
+}
+
+void 
+setup_builtin(const char *name, unsigned int type)
+{
+	ndptr n;
+	char *name2;
+
+	if (prefix_builtins) {
+		name2 = xalloc(strlen(name)+3+1, NULL);
+		memcpy(name2, "m4_", 3);
+		memcpy(name2 + 3, name, strlen(name)+1);
+	} else
+		name2 = xstrdup(name);
+
+	n = create_entry(name2);
+	n->builtin_type = type;
+	n->d = xalloc(sizeof(struct macro_definition), NULL);
+	n->d->defn = name2;
+	n->d->type = type;
+	n->d->next = NULL;
+}
+
+void
+mark_traced(const char *name, int on)
+{
+	ndptr p;
+	unsigned int i;
+
+	if (name == NULL) {
+		if (on)
+			trace_flags |= TRACE_ALL;
+		else
+			trace_flags &= ~TRACE_ALL;
+		for (p = ohash_first(&macros, &i); p != NULL; 
+		    p = ohash_next(&macros, &i))
+		    	p->trace_flags = FLAG_NO_TRACE;
+	} else {
+		p = create_entry(name);
+		p->trace_flags = on;
+	}
+}
+
+ndptr 
+macro_getbuiltin(const char *name)
+{
+	ndptr p;
+
+	p = lookup(name);
+	if (p == NULL || p->builtin_type == MACRTYPE)
+		return NULL;
+	else
+		return p;
+}
+
Index: ohash_create_entry.c
===================================================================
--- ohash_create_entry.c	(revision 0)
+++ ohash_create_entry.c	(revision 0)
@@ -0,0 +1,38 @@
+/* $OpenBSD: ohash_create_entry.c,v 1.2 2004/06/22 20:00:16 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+
+/* This handles the common case of variable length keys, where the 
+ * key is stored at the end of the record.
+ */
+void *
+ohash_create_entry(struct ohash_info *i, const char *start, const char **end)
+{
+	char *p;
+
+	if (!*end)
+		*end = start + strlen(start);
+	p = (i->alloc)(i->key_offset + (*end - start) + 1, i->data);
+	if (p) {
+	    memcpy(p+i->key_offset, start, *end-start);
+	    p[i->key_offset + (*end - start)] = '\0';
+	}
+	return (void *)p;
+}
Index: ohash_interval.c
===================================================================
--- ohash_interval.c	(revision 0)
+++ ohash_interval.c	(revision 0)
@@ -0,0 +1,36 @@
+/* $OpenBSD: ohash_interval.c,v 1.3 2006/01/16 15:52:25 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+
+uint32_t
+ohash_interval(const char *s, const char **e)
+{
+	uint32_t k;
+
+	if (!*e)
+		*e = s + strlen(s);
+	if (s == *e)
+		k = 0;
+	else
+		k = *s++;
+	while (s != *e)
+		k =  ((k << 2) | (k >> 30)) ^ *s++;
+	return k;
+}
Index: expr.c
===================================================================
--- expr.c	(revision 198625)
+++ expr.c	(working copy)
@@ -1,645 +1,43 @@
-/*	$OpenBSD: expr.c,v 1.14 2002/04/26 16:15:16 espie Exp $	*/
-/*	$NetBSD: expr.c,v 1.7 1995/09/28 05:37:31 tls Exp $	*/
-
+/* $OpenBSD: expr.c,v 1.17 2006/01/20 23:10:19 espie Exp $ */
 /*
- * Copyright (c) 1989, 1993
- *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org>
  *
- * This code is derived from software contributed to Berkeley by
- * Ozan Yigit at York University.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 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
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)expr.c	8.2 (Berkeley) 4/29/95";
-#else
-#if 0
-static char rcsid[] = "$OpenBSD: expr.c,v 1.14 2002/04/26 16:15:16 espie Exp $";
-#endif
-#endif
-#endif /* not lint */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/types.h>
-#include <ctype.h>
-#include <err.h>
-#include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
+#include <stddef.h>
 #include "mdef.h"
 #include "extern.h"
 
-/*
- *      expression evaluator: performs a standard recursive
- *      descent parse to evaluate any expression permissible
- *      within the following grammar:
- *
- *      expr    :       query EOS
- *      query   :       lor
- *              |       lor "?" query ":" query
- *      lor     :       land { "||" land }
- *      land    :       bor { "&&" bor }
- *      bor     :       xor { "|" xor }
- *      xor     :       band { "^" eqrel }
- *      band    :       eqrel { "&" eqrel }
- *      eqrel   :       nerel { ("==" | "!=") nerel }
- *      nerel   :       shift { ("<" | ">" | "<=" | ">=") shift }
- *      shift   :       primary { ("<<" | ">>") primary }
- *      primary :       term { ("+" | "-") term }
- *      term    :       exp { ("*" | "/" | "%") exp }
- *      exp     :       unary { "**" unary }
- *      unary   :       factor
- *              |       ("+" | "-" | "~" | "!") unary
- *      factor  :       constant
- *              |       "(" query ")"
- *      constant:       num
- *              |       "'" CHAR "'"
- *      num     :       DIGIT
- *              |       DIGIT num
- *
- *
- *      This expression evaluator is lifted from a public-domain
- *      C Pre-Processor included with the DECUS C Compiler distribution.
- *      It is hacked somewhat to be suitable for m4.
- *
- *      Originally by:  Mike Lutz
- *                      Bob Harper
- */
+int32_t end_result;
+const char *copy_toeval;
 
-#define EQL     0
-#define NEQ     1
-#define LSS     2
-#define LEQ     3
-#define GTR     4
-#define GEQ     5
-#define OCTAL   8
-#define DECIMAL 10
-#define HEX	16
+extern void yy_scan_string(const char *);
+extern int yyparse(void);
 
-static const char *nxtch;		       /* Parser scan pointer */
-static const char *where;
-
-static int query(int mayeval);
-static int lor(int mayeval);
-static int land(int mayeval);
-static int bor(int mayeval);
-static int xor(int mayeval);
-static int band(int mayeval);
-static int eqrel(int mayeval);
-static int nerel(int mayeval);
-static int shift(int mayeval);
-static int primary(int mayeval);
-static int term(int mayeval);
-static int exp(int mayeval);
-static int unary(int mayeval);
-static int factor(int mayeval);
-static int constant(int mayeval);
-static int num(int mayeval);
-static int geteqrel(int mayeval);
-static int skipws(void);
-static void experr(const char *);
-
-/*
- * For longjmp
- */
-#include <setjmp.h>
-static jmp_buf expjump;
-
-/*
- * macros:
- *      ungetch - Put back the last character examined.
- *      getch   - return the next character from expr string.
- */
-#define ungetch()       nxtch--
-#define getch()         *nxtch++
-
 int
-expr(const char *expbuf)
+yyerror(const char *msg)
 {
-	int rval;
-
-	nxtch = expbuf;
-	where = expbuf;
-	if (setjmp(expjump) != 0)
-		return FALSE;
-
-	rval = query(1);
-	if (skipws() == EOS)
-		return rval;
-
-	printf("m4: ill-formed expression.\n");
-	return FALSE;
+	fprintf(stderr, "m4: %s in expr %s\n", msg, copy_toeval);
+	return(0);
 }
 
-/*
- * query : lor | lor '?' query ':' query
- */
-static int
-query(int mayeval)
+int 
+expr(const char *toeval)
 {
-	int result, true_val, false_val;
-
-	result = lor(mayeval);
-	if (skipws() != '?') {
-		ungetch();
-		return result;
-	}
-
-	true_val = query(result);
-	if (skipws() != ':')
-		experr("bad query: missing \":\"");
-
-	false_val = query(!result);
-	return result ? true_val : false_val;
+	copy_toeval = toeval;
+	yy_scan_string(toeval);
+	yyparse();
+	return end_result;
 }
-
-/*
- * lor : land { '||' land }
- */
-static int
-lor(int mayeval)
-{
-	int c, vl, vr;
-
-	vl = land(mayeval);
-	while ((c = skipws()) == '|') {
-		if (getch() != '|') {
-			ungetch();
-			break;
-		}
-		if (vl != 0)
-			mayeval = 0;
-		vr = land(mayeval);
-		vl = vl || vr;
-	}
-
-	ungetch();
-	return vl;
-}
-
-/*
- * land : not { '&&' not }
- */
-static int
-land(int mayeval)
-{
-	int c, vl, vr;
-
-	vl = bor(mayeval);
-	while ((c = skipws()) == '&') {
-		if (getch() != '&') {
-			ungetch();
-			break;
-		}
-		if (vl == 0)
-			mayeval = 0;
-		vr = bor(mayeval);
-		vl = vl && vr;
-	}
-
-	ungetch();
-	return vl;
-}
-
-/*
- * bor : xor { "|" xor }
- */
-static int
-bor(int mayeval)
-{
-	int vl, vr, c, cr;
-
-	vl = xor(mayeval);
-	while ((c = skipws()) == '|') {
-		cr = getch();
-		ungetch();
-		if (cr == '|')
-			break;
-		vr = xor(mayeval);
-		vl |= vr;
-	}
-	ungetch();
-	return (vl);
-}
-
-/*
- * xor : band { "^" band }
- */
-static int
-xor(int mayeval)
-{
-	int vl, vr, c;
-
-	vl = band(mayeval);
-	while ((c = skipws()) == '^') {
-		vr = band(mayeval);
-		vl ^= vr;
-	}
-	ungetch();
-	return (vl);
-}
-
-/*
- * band : eqrel { "&" eqrel }
- */
-static int
-band(int mayeval)
-{
-	int c, cr, vl, vr;
-
-	vl = eqrel(mayeval);
-	while ((c = skipws()) == '&') {
-		cr = getch();
-		ungetch();
-		if (cr == '&')
-			break;
-		vr = eqrel(mayeval);
-		vl &= vr;
-	}
-	ungetch();
-	return vl;
-}
-
-/*
- * eqrel : nerel { ("==" | "!=" ) nerel }
- */
-static int
-eqrel(int mayeval)
-{
-	int vl, vr, c, cr;
-
-	vl = nerel(mayeval);
-	while ((c = skipws()) == '!' || c == '=') {
-		if ((cr = getch()) != '=') {
-			ungetch();
-			break;
-		}
-		vr = nerel(mayeval);
-		switch (c) {
-		case '=':
-			vl = (vl == vr);
-			break;
-		case '!':
-			vl = (vl != vr);
-			break;
-		}
-	}
-	ungetch();
-	return vl;
-}
-
-/*
- * nerel : shift { ("<=" | ">=" | "<" | ">") shift }
- */
-static int
-nerel(int mayeval)
-{
-	int vl, vr, c, cr;
-
-	vl = shift(mayeval);
-	while ((c = skipws()) == '<' || c == '>') {
-		if ((cr = getch()) != '=') {
-			ungetch();
-			cr = '\0';
-		}
-		vr = shift(mayeval);
-		switch (c) {
-		case '<':
-			vl = (cr == '\0') ? (vl < vr) : (vl <= vr);
-			break;
-		case '>':
-			vl = (cr == '\0') ? (vl > vr) : (vl >= vr);
-			break;
-		}
-	}
-	ungetch();
-	return vl;
-}
-
-/*
- * shift : primary { ("<<" | ">>") primary }
- */
-static int
-shift(int mayeval)
-{
-	int vl, vr, c;
-
-	vl = primary(mayeval);
-	while (((c = skipws()) == '<' || c == '>') && getch() == c) {
-		vr = primary(mayeval);
-
-		if (c == '<')
-			vl <<= vr;
-		else
-			vl >>= vr;
-	}
-
-	if (c == '<' || c == '>')
-		ungetch();
-	ungetch();
-	return vl;
-}
-
-/*
- * primary : term { ("+" | "-") term }
- */
-static int
-primary(int mayeval)
-{
-	int c, vl, vr;
-
-	vl = term(mayeval);
-	while ((c = skipws()) == '+' || c == '-') {
-		vr = term(mayeval);
-
-		if (c == '+')
-			vl += vr;
-		else
-			vl -= vr;
-	}
-
-	ungetch();
-	return vl;
-}
-
-/*
- * term : exp { ("*" | "/" | "%") exp }
- */
-static int
-term(int mayeval)
-{
-	int c, vl, vr;
-
-	vl = exp(mayeval);
-	while ((c = skipws()) == '*' || c == '/' || c == '%') {
-		vr = exp(mayeval);
-
-		switch (c) {
-		case '*':
-			vl *= vr;
-			break;
-		case '/':
-			if (!mayeval)
-				/* short-circuit */;
-			else if (vr == 0)
-				errx(1, "division by zero in eval.");
-			else
-				vl /= vr;
-			break;
-		case '%':
-			if (!mayeval)
-				/* short-circuit */;
-			else if (vr == 0)
-				errx(1, "modulo zero in eval.");
-			else
-				vl %= vr;
-			break;
-		}
-	}
-	ungetch();
-	return vl;
-}
-
-/*
- * exp : unary { "**" exp }
- */
-static int
-exp(int mayeval)
-{
-	int c, vl, vr, n;
-
-	vl = unary(mayeval);
-	while ((c = skipws()) == '*') {
-		if (getch() != '*') {
-			ungetch();
-			break;
-		}
-		vr = unary(mayeval);
-		n = 1;
-		while (vr-- > 0)
-			n *= vl;
-		return n;
-	}
-
-	ungetch();
-	return vl;
-}
-
-/*
- * unary : factor | ("+" | "-" | "~" | "!") unary
- */
-static int
-unary(int mayeval)
-{
-	int val, c;
-
-	if ((c = skipws()) == '+' || c == '-' || c == '~' || c == '!') {
-		val = unary(mayeval);
-
-		switch (c) {
-		case '+':
-			return val;
-		case '-':
-			return -val;
-		case '~':
-			return ~val;
-		case '!':
-			return !val;
-		}
-	}
-
-	ungetch();
-	return factor(mayeval);
-}
-
-/*
- * factor : constant | '(' query ')'
- */
-static int
-factor(int mayeval)
-{
-	int val;
-
-	if (skipws() == '(') {
-		val = query(mayeval);
-		if (skipws() != ')')
-			experr("bad factor: missing \")\"");
-		return val;
-	}
-
-	ungetch();
-	return constant(mayeval);
-}
-
-/*
- * constant: num | 'char'
- * Note: constant() handles multi-byte constants
- */
-static int
-constant(int mayeval)
-{
-	int i;
-	int value;
-	int c;
-	int v[sizeof(int)];
-
-	if (skipws() != '\'') {
-		ungetch();
-		return num(mayeval);
-	}
-	for (i = 0; i < (ssize_t)sizeof(int); i++) {
-		if ((c = getch()) == '\'') {
-			ungetch();
-			break;
-		}
-		if (c == '\\') {
-			switch (c = getch()) {
-			case '0':
-			case '1':
-			case '2':
-			case '3':
-			case '4':
-			case '5':
-			case '6':
-			case '7':
-				ungetch();
-				c = num(mayeval);
-				break;
-			case 'n':
-				c = 012;
-				break;
-			case 'r':
-				c = 015;
-				break;
-			case 't':
-				c = 011;
-				break;
-			case 'b':
-				c = 010;
-				break;
-			case 'f':
-				c = 014;
-				break;
-			}
-		}
-		v[i] = c;
-	}
-	if (i == 0 || getch() != '\'')
-		experr("illegal character constant");
-	for (value = 0; --i >= 0;) {
-		value <<= 8;
-		value += v[i];
-	}
-	return value;
-}
-
-/*
- * num : digit | num digit
- */
-static int
-num(int mayeval)
-{
-	int rval, c, base;
-	int ndig;
-
-	rval = 0;
-	ndig = 0;
-	c = skipws();
-	if (c == '0') {
-		c = skipws();
-		if (c == 'x' || c == 'X') {
-			base = HEX;
-			c = skipws();
-		} else {
-			base = OCTAL;
-			ndig++;
-		}
-	} else
-		base = DECIMAL;
-	for(;;) {
-		switch(c) {
-			case '8': case '9':
-				if (base == OCTAL)
-					goto bad_digit;
-				/*FALLTHRU*/
-			case '0': case '1': case '2': case '3':
-			case '4': case '5': case '6': case '7':
-				rval *= base;
-				rval += c - '0';
-				break;
-			case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
-				c = tolower(c);
-			case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
-				if (base == HEX) {
-					rval *= base;
-					rval += c - 'a' + 10;
-					break;
-				}
-				/*FALLTHRU*/
-			default:
-				goto bad_digit;
-		}
-		c = getch();
-		ndig++;
-	}
-bad_digit:
-	ungetch();
-
-	if (ndig == 0)
-		experr("bad constant");
-
-	return rval;
-}
-
-/*
- * Skip over any white space and return terminating char.
- */
-static int
-skipws(void)
-{
-	int c;
-
-	while ((c = getch()) <= ' ' && c > EOS)
-		;
-	return c;
-}
-
-/*
- * resets environment to eval(), prints an error
- * and forces eval to return FALSE.
- */
-static void
-experr(const char *msg)
-{
-	printf("m4: %s in expr %s.\n", msg, where);
-	longjmp(expjump, -1);
-}
Index: mdef.h
===================================================================
--- mdef.h	(revision 198625)
+++ mdef.h	(working copy)
@@ -1,4 +1,4 @@
-/*	$OpenBSD: mdef.h,v 1.21 2001/09/27 11:40:33 espie Exp $	*/
+/*	$OpenBSD: mdef.h,v 1.29 2006/03/20 20:27:45 espie Exp $	*/
 /*	$NetBSD: mdef.h,v 1.7 1996/01/13 23:25:27 pk Exp $	*/
 
 /*
@@ -16,11 +16,7 @@
  * 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.
  *
@@ -37,9 +33,14 @@
  * SUCH DAMAGE.
  *
  *	@(#)mdef.h	8.1 (Berkeley) 6/6/93
- * $FreeBSD$
  */
 
+#ifdef __GNUC__
+# define UNUSED	__attribute__((__unused__))
+#else
+# define UNUSED
+#endif
+
 #define MACRTYPE        1
 #define DEFITYPE        2
 #define EXPRTYPE        3
@@ -83,8 +84,10 @@
 #define ESYSCMDTYPE	41
 #define TRACEONTYPE	42
 #define TRACEOFFTYPE	43
+#define FORMATTYPE	44
 
-
+#define BUILTIN_MARKER	"__builtin_"
+ 
 #define TYPEMASK	63	/* Keep bits really corresponding to a type. */
 #define RECDEF		256	/* Pure recursive def, don't expand it */
 #define NOARGS		512	/* builtin needs no args */
@@ -93,7 +96,7 @@
 /*
  * m4 special characters
  */
-
+ 
 #define ARGFLAG         '$'
 #define LPAREN          '('
 #define RPAREN          ')'
@@ -120,10 +123,10 @@
 #define MAXTOK          512          	/* maximum chars in a tokn 	    */
 #define HASHSIZE        199             /* maximum size of hashtab 	    */
 #define MAXCCHARS	5		/* max size of comment/quote delim  */
-
+ 
 #define ALL             1
 #define TOP             0
-
+ 
 #define TRUE            1
 #define FALSE           0
 #define cycle           for(;;)
@@ -131,24 +134,23 @@
 /*
  * m4 data structures
  */
-
+ 
 typedef struct ndblock *ndptr;
-
-struct ndblock {		/* hastable structure         */
-	char		*name;	/* entry name..               */
+ 
+struct macro_definition {
+	struct macro_definition *next;
 	char		*defn;	/* definition..               */
 	unsigned int	type;	/* type of the entry..        */
-	unsigned int 	hv;	/* hash function value..      */
-	ndptr		nxtptr;	/* link to next entry..       */
 };
 
-#define nil     ((ndptr) 0)
 
-struct keyblk {
-        const char    *knam;    /* keyword name */
-        int     ktyp;           /* keyword type */
+struct ndblock {			/* hashtable structure         */
+	unsigned int 		builtin_type;
+	unsigned int		trace_flags;
+	struct macro_definition *d;
+	char		name[1];	/* entry name..               */
 };
-
+ 
 typedef union {			/* stack structure */
 	int	sfra;		/* frame entry  */
 	char 	*sstr;		/* string entry */
@@ -158,6 +160,7 @@
 	FILE 		*file;
 	char 		*name;
 	unsigned long 	lineno;
+	unsigned long   synch_lineno;	/* used for -s */
 	int 		c;
 };
 
@@ -166,31 +169,33 @@
 /*
  * macros for readibility and/or speed
  *
+ *      gpbc()  - get a possibly pushed-back character
  *      pushf() - push a call frame entry onto stack
  *      pushs() - push a string pointer onto stack
  */
-#define pushf(x) 					\
-	do {						\
-		if ((uintptr_t)++sp == STACKMAX) 	\
-			enlarge_stack();		\
-		mstack[sp].sfra = (x);			\
-		sstack[sp] = 0;				\
+#define gpbc() 	 (bp > bufbase) ? *--bp : obtain_char(infile+ilevel)
+#define pushf(x) 			\
+	do {				\
+		if (++sp == STACKMAX) 	\
+			enlarge_stack();\
+		mstack[sp].sfra = (x);	\
+		sstack[sp] = 0; \
 	} while (0)
 
-#define pushs(x) 					\
-	do {						\
-		if ((uintptr_t)++sp == STACKMAX) 	\
-			enlarge_stack();		\
-		mstack[sp].sstr = (x);			\
-		sstack[sp] = 1;				\
+#define pushs(x) 			\
+	do {				\
+		if (++sp == STACKMAX) 	\
+			enlarge_stack();\
+		mstack[sp].sstr = (x);	\
+		sstack[sp] = 1; \
 	} while (0)
 
-#define pushs1(x) 					\
-	do {						\
-		if ((uintptr_t)++sp == STACKMAX) 	\
-			enlarge_stack();		\
-		mstack[sp].sstr = (x);			\
-		sstack[sp] = 0;				\
+#define pushs1(x) 			\
+	do {				\
+		if (++sp == STACKMAX) 	\
+			enlarge_stack();\
+		mstack[sp].sstr = (x);	\
+		sstack[sp] = 0; \
 	} while (0)
 
 /*
@@ -217,7 +222,8 @@
  *
  */
 #define PARLEV  (mstack[fp].sfra)
-#define CALTYP  (mstack[fp-1].sfra)
+#define CALTYP  (mstack[fp-2].sfra)
+#define TRACESTATUS (mstack[fp-1].sfra)
 #define PREVEP	(mstack[fp+3].sstr)
-#define PREVSP	(fp-3)
-#define PREVFP	(mstack[fp-2].sfra)
+#define PREVSP	(fp-4)
+#define PREVFP	(mstack[fp-3].sfra)
Index: tokenizer.l
===================================================================
--- tokenizer.l	(revision 0)
+++ tokenizer.l	(revision 0)
@@ -0,0 +1,97 @@
+%{
+/* $OpenBSD: tokenizer.l,v 1.6 2008/08/21 21:00:14 espie Exp $ */
+/*
+ * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "parser.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <limits.h>
+
+extern int mimic_gnu;
+extern int32_t yylval;
+
+int32_t number(void);
+int32_t parse_radix(void);
+%}
+
+delim 	[ \t\n]
+ws	{delim}+
+hex	0[xX][0-9a-fA-F]+
+oct	0[0-7]*
+dec	[1-9][0-9]*
+radix	0[rR][0-9]+:[0-9a-zA-Z]+
+
+%%
+{ws}			{/* just skip it */}
+{hex}|{oct}|{dec}	{ yylval = number(); return(NUMBER); }
+{radix}			{ if (mimic_gnu) {
+				yylval = parse_radix(); return(NUMBER);
+			  } else {
+			  	return(ERROR);
+			  }
+			}
+"<="			{ return(LE); }
+">="			{ return(GE); }
+"<<"			{ return(LSHIFT); }
+">>"			{ return(RSHIFT); }
+"=="			{ return(EQ); }
+"!="			{ return(NE); }
+"&&"			{ return(LAND); }
+"||"			{ return(LOR); }
+.			{ return yytext[0]; }
+%%
+
+int32_t
+number()
+{
+	long l;
+
+	errno = 0;
+	l = strtol(yytext, NULL, 0);
+	if (((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE) ||
+	    l > INT32_MAX || l < INT32_MIN) {
+		fprintf(stderr, "m4: numeric overflow in expr: %s\n", yytext);
+	}
+	return l;
+}
+
+int32_t
+parse_radix()
+{
+	long base;
+	char *next;
+	long l;
+
+	l = 0;
+	base = strtol(yytext+2, &next, 0);
+	if (base > 36 || next == NULL) {
+		fprintf(stderr, "m4: error in number %s\n", yytext);
+	} else {
+		next++;
+		while (*next != 0) {
+			if (*next >= '0' && *next <= '9')
+				l = base * l + *next - '0';
+			else if (*next >= 'a' && *next <= 'z')
+				l = base * l + *next - 'a' + 10;
+			else if (*next >= 'A' && *next <= 'Z')
+				l = base * l + *next - 'A' + 10;
+			next++;
+		}
+	}
+	return l;
+}
+
Index: main.c
===================================================================
--- main.c	(revision 198625)
+++ main.c	(working copy)
@@ -1,4 +1,4 @@
-/*	$OpenBSD: main.c,v 1.53 2002/04/26 16:15:16 espie Exp $	*/
+/*	$OpenBSD: main.c,v 1.77 2009/10/14 17:19:47 sthen Exp $	*/
 /*	$NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $	*/
 
 /*-
@@ -16,11 +16,7 @@
  * 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.
  *
@@ -37,43 +33,24 @@
  * SUCH DAMAGE.
  */
 
-#ifndef lint
-static char copyright[] =
-"@(#) Copyright (c) 1989, 1993\n\
-	The Regents of the University of California.  All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
-#else
-#if 0
-static char rcsid[] = "$OpenBSD: main.c,v 1.53 2002/04/26 16:15:16 espie Exp $";
-#endif
-#endif
-#endif /* not lint */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
 /*
  * main.c
  * Facility: m4 macro processor
  * 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"
@@ -86,21 +63,27 @@
 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 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..     */
+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 synccpp;			/* Line synchronisation for C preprocessor */
+int  synch_lines = 0;		/* line synchronisation for C preprocessor */
+int  prefix_builtins = 0;	/* -P option to prefix builtin keywords */
 
+struct keyblk {
+        char    *knam;          /* keyword name */
+        int     ktyp;           /* keyword type */
+};
+
 struct keyblk keywrds[] = {	/* m4 keywords to be installed */
 	{ "include",      INCLTYPE },
 	{ "sinclude",     SINCTYPE },
@@ -148,7 +131,7 @@
 	{ "traceon",	  TRACEONTYPE | NOARGS },
 	{ "traceoff",	  TRACEOFFTYPE | NOARGS },
 
-#if defined(unix) || defined(__unix__)
+#if defined(unix) || defined(__unix__) 
 	{ "unix",         SELFTYPE | NOARGS },
 #else
 #ifdef vms
@@ -159,6 +142,9 @@
 
 #define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
 
+extern int optind;
+extern char *optarg;
+
 #define MAXRECORD 50
 static struct position {
 	char *name;
@@ -172,43 +158,41 @@
 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 main(int, char *[]);
+
 int
 main(int argc, char *argv[])
 {
 	int c;
 	int n;
-	int rval;
 	char *p;
 
-	setlocale(LC_ALL, "");
-
-	traceout = stderr;
-
 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
 		signal(SIGINT, onintr);
 
-	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, "gst:d:D:U:o:I:")) != -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++)
 				if (*p == '=')
 					break;
-			if (p == optarg)
-				errx(1, "null variable cannot be defined");
 			if (*p)
 				*p++ = EOS;
 			dodefine(optarg, p);
@@ -216,8 +200,11 @@
 		case 'I':
 			addtoincludepath(optarg);
 			break;
+		case 'P':
+			prefix_builtins = 1;
+			break;
 		case 'U':               /* undefine...       */
-			remhash(optarg, TOP);
+			macro_popdef(optarg);
 			break;
 		case 'g':
 			mimic_gnu = 1;
@@ -226,7 +213,7 @@
 			set_trace_flags(optarg);
 			break;
 		case 's':
-			synccpp = 1;
+			synch_lines = 1;
 			break;
 		case 't':
 			mark_traced(optarg, 1);
@@ -235,14 +222,16 @@
 			trace_file(optarg);
                         break;
 		case '?':
-		default:
 			usage();
 		}
 
         argc -= optind;
         argv += optind;
 
-	rval = 0;
+	initkwds();
+	if (mimic_gnu)
+		setup_builtin("format", FORMATTYPE);
+
 	active = stdout;		/* default active output     */
 	bbase[0] = bufbase;
         if (!argc) {
@@ -250,36 +239,38 @@
 		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
 		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();
+			fp = 0; 
 			macro();
 		    	release_input(infile);
 		}
 
-	if (*m4wraps) { 		/* anything for rundown ??   */
+	if (wrapindex) {
+		int i;
+
 		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)
@@ -292,7 +283,7 @@
 		(void) fclose(outfile[0]);
 	}
 
-	exit(rval);
+	return 0;
 }
 
 /*
@@ -312,9 +303,9 @@
 	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;
 		}
 	}
@@ -338,61 +329,8 @@
 
 	cycle {
 		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++);
 			/*
@@ -418,32 +356,77 @@
 				} 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)) {
+			reallyoutputstr(scommt);
 
-		else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
-			fputs(scommt, active);
-
 			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 */
+				pushf(macro_getdef(p)->type); /* type of the call  */
+				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..*/
 
-		else if (sp < 0) {		/* not in a macro at all */
-			putc(t, active);	/* output directly..	 */
+				if (l != LPAREN && PARLEV == 0)  {   
+				    /* no bracks  */
+					chrsave(EOS);
+
+					if (sp == 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) {
@@ -451,9 +434,10 @@
 		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;
 
@@ -463,11 +447,11 @@
 			else {			/* end of argument list */
 				chrsave(EOS);
 
-				if ((uintptr_t)sp == STACKMAX)
+				if (sp == 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..  */
@@ -480,7 +464,7 @@
 				chrsave(EOS);		/* new argument   */
 				while (isspace(l = gpbc()))
 					;
-				putback(l);
+				pushback(l);
 				pushs(ep);
 			} else
 				chrsave(t);
@@ -488,14 +472,14 @@
 
 		default:
 			if (LOOK_AHEAD(t, scommt)) {
-				char *pc;
-				for (pc = scommt; *pc; pc++)
-					chrsave(*pc);
+				char *p;
+				for (p = scommt; *p; p++)
+					chrsave(*p);
 				for(;;) {
 					t = gpbc();
 					if (LOOK_AHEAD(t, ecommt)) {
-						for (pc = ecommt; *pc; pc++)
-							chrsave(*pc);
+						for (p = ecommt; *p; p++)
+							chrsave(*p);
 						break;
 					}
 					if (t == EOF)
@@ -509,39 +493,64 @@
 	}
 }
 
-/*
- * output string directly, without pushing it for reparses.
+/* 
+ * output string directly, without pushing it for reparses. 
  */
 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)
+inspect(int c, char *tp) 
 {
 	char *name = tp;
 	char *etp = tp+MAXTOK;
 	ndptr p;
-	unsigned int h;
+	
+	*tp++ = c;
 
-	h = *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. */
@@ -549,71 +558,43 @@
 		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 - initialise 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
+ * such as calling lookup. Malloc is not used for storing the 
  * keyword strings, since we simply use the static pointers
  * within keywrds block.
  */
 static void
 initkwds(void)
 {
-	size_t i;
-	unsigned int h;
-	ndptr p;
+	unsigned int type;
+	int i;
 
 	for (i = 0; i < MAXKEYS; i++) {
-		h = hash(keywrds[i].knam);
-		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;
+		type = keywrds[i].ktyp & TYPEMASK;
 		if ((keywrds[i].ktyp & NOARGS) == 0)
-			p->type |= NEEDARGS;
+			type |= NEEDARGS;
+		setup_builtin(keywrds[i].knam, type);
 	}
 }
 
-/* Look up a builtin type, even if overridden by the user */
-int
-builtin_type(const char *key)
-{
-	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;
-}
-
 static void
 record(struct position *t, int lev)
 {
@@ -633,29 +614,20 @@
 			fprintf(stderr, "   ...\n");
 			break;
 		}
-		fprintf(stderr, "   %s at line %lu\n",
+		fprintf(stderr, "   %s at line %lu\n", 
 			t[i].name, t[i].line);
 	}
 }
 
 
-static void
+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);
+	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);
 }
-
-/* Emit preprocessor #line directive if -s option used. */
-void
-emitline(void)
-{
-
-	if (synccpp)
-		fprintf(active, "#line %d \"%s\"\n", inlineno[ilevel],
-			inname[ilevel]);
-}
Index: ohash_delete.c
===================================================================
--- ohash_delete.c	(revision 0)
+++ ohash_delete.c	(revision 0)
@@ -0,0 +1,31 @@
+/* $OpenBSD: ohash_delete.c,v 1.2 2004/06/22 20:00:16 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+/* hash_delete only frees the hash structure. Use hash_first/hash_next
+ * to free entries as well.  */
+void 
+ohash_delete(struct ohash *h)
+{
+	(h->info.hfree)(h->t, sizeof(struct _ohash_record) * h->size,
+		h->info.data);
+#ifndef NDEBUG
+	h->t = NULL;
+#endif
+}
Index: pathnames.h
===================================================================
--- pathnames.h	(revision 198625)
+++ pathnames.h	(working copy)
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pathnames.h,v 1.4 1997/04/04 18:41:29 deraadt Exp $	*/
+/*	$OpenBSD: pathnames.h,v 1.5 2003/06/03 02:56:10 millert Exp $	*/
 /*	$NetBSD: pathnames.h,v 1.6 1995/09/29 00:27:55 cgd Exp $	*/
 
 /*
@@ -16,11 +16,7 @@
  * 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.
  *
@@ -37,7 +33,6 @@
  * SUCH DAMAGE.
  *
  *	@(#)pathnames.h	8.1 (Berkeley) 6/6/93
- * $FreeBSD$
  */
 
 /*
@@ -50,8 +45,7 @@
 #define	UNIQUE		3			/* unique char location */
 #endif
 
-#if defined(unix) || defined(__FreeBSD__) || defined(__NetBSD__) || \
-	defined(__OpenBSD__)
+#if defined(unix) || defined(__NetBSD__) || defined(__OpenBSD__)
 #define _PATH_DIVNAME	"/tmp/m4.0XXXXXXXXXX"	/* unix diversion files */
 #define UNIQUE		8			/* unique char location */
 #endif
Index: ohash_init.c
===================================================================
--- ohash_init.c	(revision 0)
+++ ohash_init.c	(revision 0)
@@ -0,0 +1,41 @@
+/* $OpenBSD: ohash_init.c,v 1.2 2004/06/22 20:00:16 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+
+void 
+ohash_init(struct ohash *h, unsigned int size, struct ohash_info *info)
+{
+	h->size = 1UL << size;
+	if (h->size < MINSIZE)
+		h->size = MINSIZE;
+#ifdef STATS_HASH
+	STAT_HASH_CREATION++;
+	STAT_HASH_SIZE += h->size;
+#endif
+	/* Copy info so that caller may free it.  */
+	h->info.key_offset = info->key_offset;
+	h->info.halloc = info->halloc;
+	h->info.hfree = info->hfree;
+	h->info.alloc = info->alloc;
+	h->info.data = info->data;
+	h->t = (h->info.halloc)(sizeof(struct _ohash_record) * h->size,
+	    h->info.data);
+	h->total = h->deleted = 0;
+}
Index: ohash_enum.c
===================================================================
--- ohash_enum.c	(revision 0)
+++ ohash_enum.c	(revision 0)
@@ -0,0 +1,36 @@
+/* $OpenBSD: ohash_enum.c,v 1.3 2004/06/22 20:00:16 espie Exp $ */
+/* ex:ts=8 sw=4: 
+ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ohash_int.h"
+
+void *
+ohash_first(struct ohash *h, unsigned int *pos)
+{
+	*pos = 0;
+	return ohash_next(h, pos);
+}
+	
+void *
+ohash_next(struct ohash *h, unsigned int *pos)
+{
+	for (; *pos < h->size; (*pos)++) 
+		if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL) 
+			return (void *)h->t[(*pos)++].p;
+	return NULL;
+}
Index: ohash_interval.3
===================================================================
--- ohash_interval.3	(revision 0)
+++ ohash_interval.3	(revision 0)
@@ -0,0 +1,90 @@
+.\"	$OpenBSD: ohash_interval.3,v 1.11 2007/05/31 19:19:30 jmc Exp $
+.\" Copyright (c) 2001 Marc Espie <espie@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt OPEN_HASH_HELPER 3
+.Os
+.Sh NAME
+.Nm ohash_interval ,
+.Nm ohash_create_entry ,
+.Nm ohash_qlookup ,
+.Nm ohash_qlookupi
+.Nd helper functions for open hashing
+.Sh SYNOPSIS
+.Fd #include <stdint.h>
+.Fd #include <stddef.h>
+.Fd #include <ohash.h>
+.Ft u_int32_t
+.Fn ohash_interval "const char *start" "const char **pend"
+.Ft "void *"
+.Fn ohash_create_entry "struct ohash_info *info" "const char *start" "const char **pend"
+.Ft "unsigned int"
+.Fn ohash_qlookupi "struct ohash *h" "const char *start" "const char **pend"
+.Ft "unsigned int"
+.Fn ohash_qlookup "struct ohash *h" "const char *start"
+.Sh DESCRIPTION
+These functions are commonly used to simplify open hashing usage, and use
+similar conventions.
+They operate indifferently on NUL-terminated strings
+.Po
+by setting
+.Fa *pend
+=
+.Dv NULL
+.Pc
+or memory ranges
+.Po
+delimited by
+.Fa start
+and
+.Fa *pend
+.Pc .
+For NUL-terminated strings, as a side effect, those functions
+set
+.Fa *pend
+to the terminating NUL byte.
+.Pp
+.Fn ohash_interval
+is a simple hashing function that yields good results on common data sets.
+.Pp
+.Fn ohash_create_entry
+can be used to create a new record with a given key.
+In that case,
+the alloc field of
+.Fa info
+should point to a
+.Xr malloc 3 Ns -like
+function to allocate the storage.
+.Pp
+.Fn ohash_qlookupi
+is a wrapper function that simply calls
+.Fn ohash_interval
+and
+.Fn ohash_lookup_interval .
+.Pp
+.Fn ohash_qlookup
+is a variation on
+.Fn ohash_qlookupi
+designed for NUL-terminated strings.
+.Sh SEE ALSO
+.Xr ohash_init 3
+.Sh STANDARDS
+Those functions are completely non-standard and should be avoided in
+portable programs.
+.Sh HISTORY
+Those functions were designed and written for
+.Ox
+.Xr make 1
+by Marc Espie in 1999.
Index: eval.c
===================================================================
--- eval.c	(revision 198625)
+++ eval.c	(working copy)
@@ -1,4 +1,4 @@
-/*	$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $	*/
+/*	$OpenBSD: eval.c,v 1.66 2008/08/21 21:01:47 espie Exp $	*/
 /*	$NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $	*/
 
 /*
@@ -16,11 +16,7 @@
  * 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.
  *
@@ -37,41 +33,28 @@
  * SUCH DAMAGE.
  */
 
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)eval.c	8.2 (Berkeley) 4/27/95";
-#else
-#if 0
-static char rcsid[] = "$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $";
-#endif
-#endif
-#endif /* not lint */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
 /*
  * eval.c
  * Facility: m4 macro processor
  * by: oz
  */
 
+#include <sys/stdint.h>
 #include <sys/types.h>
+#include <err.h>
 #include <errno.h>
+#include <limits.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 #include <fcntl.h>
-#include <err.h>
 #include "mdef.h"
 #include "stdd.h"
 #include "extern.h"
 #include "pathnames.h"
 
-#define BUILTIN_MARKER	"__builtin_"
-
 static void	dodefn(const char *);
 static void	dopushdef(const char *, const char *);
 static void	dodump(const char *[], int);
@@ -79,10 +62,9 @@
 static void	doifelse(const char *[], int);
 static int	doincl(const char *);
 static int	dopaste(const char *);
-static void	gnu_dochq(const char *[], int);
 static void	dochq(const char *[], int);
-static void	gnu_dochc(const char *[], int);
 static void	dochc(const char *[], int);
+static void	dom4wrap(const char *);
 static void	dodiv(int);
 static void	doundiv(const char *[], int);
 static void	dosub(const char *[], int);
@@ -90,7 +72,7 @@
 static const char *handledash(char *, char *, const char *);
 static void	expand_builtin(const char *[], int, int);
 static void	expand_macro(const char *[], int);
-static void	dump_one_def(ndptr);
+static void	dump_one_def(const char *, struct macro_definition *);
 
 unsigned long	expansion_id;
 
@@ -99,7 +81,7 @@
  *	  argc - number of elements in argv.
  *	  argv - element vector :
  *			argv[0] = definition of a user
- *				  macro or nil if built-in.
+ *				  macro or NULL if built-in.
  *			argv[1] = name of the macro or
  *				  built-in.
  *			argv[2] = parameters to user-defined
@@ -114,21 +96,20 @@
  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
  */
 void
-eval(const char *argv[], int argc, int td)
+eval(const char *argv[], int argc, int td, int is_traced)
 {
-	ssize_t mark = -1;
+	size_t mark = SIZE_MAX;
 
 	expansion_id++;
-	if (td & RECDEF)
-		errx(1, "%s at line %lu: expanding recursive definition for %s",
-			CURRENT_NAME, CURRENT_LINE, argv[1]);
-	if (traced_macros && is_traced(argv[1]))
+	if (td & RECDEF) 
+		m4errx(1, "expanding recursive definition for %s.", argv[1]);
+	if (is_traced)
 		mark = trace(argv, argc, infile+ilevel);
 	if (td == MACRTYPE)
 		expand_macro(argv, argc);
 	else
 		expand_builtin(argv, argc, td);
-    	if (mark != -1)
+    	if (mark != SIZE_MAX)
 		finish_trace(mark);
 }
 
@@ -154,9 +135,12 @@
   * have macro-or-builtin() type call. We adjust
   * argc to avoid further checking..
   */
+ /* we keep the initial value for those built-ins that differentiate
+  * between builtin() and builtin.
+  */
   	ac = argc;
 
-	if (argc == 3 && !*(argv[2]))
+	if (argc == 3 && !*(argv[2]) && !mimic_gnu)
 		argc--;
 
 	switch (td & TYPEMASK) {
@@ -188,9 +172,27 @@
 	 * doexpr - evaluate arithmetic
 	 * expression
 	 */
+	{
+		int base = 10;
+		int maxdigits = 0;
+		const char *errstr;
+
+		if (argc > 3) {
+			base = strtonum(argv[3], 2, 36, &errstr);
+			if (errstr) {
+				m4errx(1, "expr: base %s invalid.", argv[3]);
+			}
+		}
+		if (argc > 4) {
+			maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr);
+			if (errstr) {
+				m4errx(1, "expr: maxdigits %s invalid.", argv[4]);
+			}
+		}
 		if (argc > 2)
-			pbnum(expr(argv[2]));
+			pbnumbase(expr(argv[2]), base, maxdigits);
 		break;
+	}
 
 	case IFELTYPE:
 		if (argc > 4)
@@ -204,7 +206,7 @@
 	 * another definition
 	 */
 		if (argc > 3) {
-			if (lookup(argv[2]) != nil)
+			if (lookup_macro_definition(argv[2]) != NULL)
 				pbstr(argv[3]);
 			else if (argc > 4)
 				pbstr(argv[4]);
@@ -242,7 +244,7 @@
 	 * dosys - execute system command
 	 */
 		if (argc > 2) {
-			fflush(NULL);
+			fflush(stdout);
 			sysval = system(argv[2]);
 		}
 		break;
@@ -251,7 +253,7 @@
 	/*
 	 * dosysval - return value of the last
 	 * system call.
-	 *
+	 * 
 	 */
 		pbnum(sysval);
 		break;
@@ -275,7 +277,7 @@
 	case PASTTYPE:
 		if (argc > 2)
 			if (!dopaste(argv[2]))
-				err(1, "%s at line %lu: paste(%s)",
+				err(1, "%s at line %lu: paste(%s)", 
 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
 		break;
 
@@ -283,25 +285,22 @@
 		if (argc > 2)
 			(void) dopaste(argv[2]);
 		break;
+	case FORMATTYPE:
+		doformat(argv, argc);
+		break;
 #endif
 	case CHNQTYPE:
-		if (mimic_gnu)
-			gnu_dochq(argv, ac);
-		else
-			dochq(argv, argc);
+		dochq(argv, ac);
 		break;
 
 	case CHNCTYPE:
-		if (mimic_gnu)
-			gnu_dochc(argv, ac);
-		else
-			dochc(argv, argc);
+		dochc(argv, argc);
 		break;
 
 	case SUBSTYPE:
 	/*
 	 * dosub - select substring
-	 *
+	 * 
 	 */
 		if (argc > 3)
 			dosub(argv, argc);
@@ -318,7 +317,7 @@
 				pbstr(rquote);
 				pbstr(argv[n]);
 				pbstr(lquote);
-				putback(COMMA);
+				pushback(COMMA);
 			}
 			pbstr(rquote);
 			pbstr(argv[3]);
@@ -354,7 +353,7 @@
 	 */
 		if (argc > 2)
 			for (n = 2; n < argc; n++)
-				remhash(argv[n], ALL);
+				macro_undefine(argv[n]);
 		break;
 
 	case POPDTYPE:
@@ -365,7 +364,7 @@
 	 */
 		if (argc > 2)
 			for (n = 2; n < argc; n++)
-				remhash(argv[n], TOP);
+				macro_popdef(argv[n]);
 		break;
 
 	case MKTMTYPE:
@@ -377,11 +376,11 @@
 			char *temp;
 
 			temp = xstrdup(argv[2]);
-
+			
 			fd = mkstemp(temp);
 			if (fd == -1)
-				err(1,
-	    "%s at line %lu: couldn't make temp file %s",
+				err(1, 
+	    "%s at line %lu: couldn't make temp file %s", 
 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
 			close(fd);
 			pbstr(temp);
@@ -399,7 +398,7 @@
 		if (argc > 3) {
 			char *temp;
 
-			temp = xalloc(strlen(argv[2])+1);
+			temp = xalloc(strlen(argv[2])+1, NULL);
 			if (argc > 4)
 				map(temp, argv[2], argv[3], argv[4]);
 			else
@@ -445,7 +444,8 @@
 	 * dom4wrap - set up for
 	 * wrap-up/wind-down activity
 	 */
-		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
+		if (argc > 2)
+			dom4wrap(argv[2]);
 		break;
 
 	case EXITTYPE:
@@ -466,7 +466,7 @@
 		if (argc > 2)
 			doindir(argv, argc);
 		break;
-
+	
 	case BUILTINTYPE: /* Builtins only */
 		if (argc > 2)
 			dobuiltin(argv, argc);
@@ -492,8 +492,7 @@
 		pbstr(lquote);
 		break;
 	default:
-		errx(1, "%s at line %lu: eval: major botch.",
-			CURRENT_NAME, CURRENT_LINE);
+		m4errx(1, "eval: major botch.");
 		break;
 	}
 }
@@ -516,7 +515,7 @@
 	p--;			       /* last character of defn */
 	while (p > t) {
 		if (*(p - 1) != ARGFLAG)
-			PUTBACK(*p);
+			PUSHBACK(*p);
 		else {
 			switch (*p) {
 
@@ -540,7 +539,7 @@
 				if (argc > 2) {
 					for (n = argc - 1; n > 2; n--) {
 						pbstr(argv[n]);
-						putback(COMMA);
+						pushback(COMMA);
 					}
 					pbstr(argv[2]);
 			    	}
@@ -551,7 +550,7 @@
 						pbstr(rquote);
 						pbstr(argv[n]);
 						pbstr(lquote);
-						putback(COMMA);
+						pushback(COMMA);
 					}
 					pbstr(rquote);
 					pbstr(argv[2]);
@@ -559,8 +558,8 @@
 				}
                                 break;
 			default:
-				PUTBACK(*p);
-				PUTBACK('$');
+				PUSHBACK(*p);
+				PUSHBACK('$');
 				break;
 			}
 			p--;
@@ -568,42 +567,20 @@
 		p--;
 	}
 	if (p == t)		       /* do last character */
-		PUTBACK(*p);
+		PUSHBACK(*p);
 }
 
+
 /*
  * dodefine - install definition in the table
  */
 void
 dodefine(const char *name, const char *defn)
 {
-	ndptr p;
-	int n;
-
-	if (!*name)
-		errx(1, "%s at line %lu: null definition.", CURRENT_NAME,
-		    CURRENT_LINE);
-	if ((p = lookup(name)) == nil)
-		p = addent(name);
-	else if (p->defn != null)
-		free((char *) p->defn);
-	if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) {
-		n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1);
-		if (n != -1) {
-			p->type = n & TYPEMASK;
-			if ((n & NOARGS) == 0)
-				p->type |= NEEDARGS;
-			p->defn = null;
-			return;
-		}
-	}
-	if (!*defn)
-		p->defn = null;
-	else
-		p->defn = xstrdup(defn);
-	p->type = MACRTYPE;
-	if (STREQ(name, defn))
-		p->type |= RECDEF;
+	if (!*name && !mimic_gnu)
+		m4errx(1, "null definition.");
+	else 
+		macro_define(name, defn);
 }
 
 /*
@@ -613,16 +590,15 @@
 static void
 dodefn(const char *name)
 {
-	ndptr p;
-	const char *real;
+	struct macro_definition *p;
 
-	if ((p = lookup(name)) != nil) {
-		if (p->defn != null) {
+	if ((p = lookup_macro_definition(name)) != NULL) {
+		if ((p->type & TYPEMASK) == MACRTYPE) {
 			pbstr(rquote);
 			pbstr(p->defn);
 			pbstr(lquote);
-		} else if ((real = builtin_realname(p->type)) != NULL) {
-			pbstr(real);
+		} else {
+			pbstr(p->defn);
 			pbstr(BUILTIN_MARKER);
 		}
 	}
@@ -638,40 +614,28 @@
 static void
 dopushdef(const char *name, const char *defn)
 {
-	ndptr p;
-
-	if (!*name)
-		errx(1, "%s at line %lu: null definition", CURRENT_NAME,
-		    CURRENT_LINE);
-	p = addent(name);
-	if (!*defn)
-		p->defn = null;
+	if (!*name && !mimic_gnu)
+		m4errx(1, "null definition.");
 	else
-		p->defn = xstrdup(defn);
-	p->type = MACRTYPE;
-	if (STREQ(name, defn))
-		p->type |= RECDEF;
+		macro_pushdef(name, defn);
 }
 
 /*
  * dump_one_def - dump the specified definition.
  */
 static void
-dump_one_def(ndptr p)
+dump_one_def(const char *name, struct macro_definition *p)
 {
-	const char *real;
-
+	if (!traceout)
+		traceout = stderr;
 	if (mimic_gnu) {
 		if ((p->type & TYPEMASK) == MACRTYPE)
-			fprintf(traceout, "%s:\t%s\n", p->name, p->defn);
+			fprintf(traceout, "%s:\t%s\n", name, p->defn);
 		else {
-			real = builtin_realname(p->type);
-			if (real == NULL)
-				real = null;
-			fprintf(traceout, "%s:\t<%s>\n", p->name, real);
+			fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
 	    	}
 	} else
-		fprintf(traceout, "`%s'\t`%s'\n", p->name, p->defn);
+		fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
 }
 
 /*
@@ -683,17 +647,14 @@
 dodump(const char *argv[], int argc)
 {
 	int n;
-	ndptr p;
+	struct macro_definition *p;
 
 	if (argc > 2) {
 		for (n = 2; n < argc; n++)
-			if ((p = lookup(argv[n])) != nil)
-				dump_one_def(p);
-	} else {
-		for (n = 0; n < HASHSIZE; n++)
-			for (p = hashtab[n]; p != nil; p = p->nxtptr)
-				dump_one_def(p);
-	}
+			if ((p = lookup_macro_definition(argv[n])) != NULL)
+				dump_one_def(argv[n], p);
+	} else
+		macro_for_all(dump_one_def);
 }
 
 /*
@@ -738,15 +699,10 @@
 doincl(const char *ifile)
 {
 	if (ilevel + 1 == MAXINP)
-		errx(1, "%s at line %lu: too many include files.",
-		    CURRENT_NAME, CURRENT_LINE);
+		m4errx(1, "too many include files.");
 	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
 		ilevel++;
-		if ((inname[ilevel] = strdup(ifile)) == NULL)
-			err(1, NULL);
-		inlineno[ilevel] = 1;
 		bbase[ilevel] = bufbase = bp;
-		emitline();
 		return (1);
 	} else
 		return (0);
@@ -764,97 +720,74 @@
 	int c;
 
 	if ((pf = fopen(pfile, "r")) != NULL) {
-		fprintf(active, "#line 1 \"%s\"\n", pfile);
+		if (synch_lines)
+		    fprintf(active, "#line 1 \"%s\"\n", pfile);
 		while ((c = getc(pf)) != EOF)
 			putc(c, active);
 		(void) fclose(pf);
-		emitline();
+		emit_synchline();
 		return (1);
 	} else
 		return (0);
 }
 #endif
 
+/*
+ * dochq - change quote characters
+ */
 static void
-gnu_dochq(const char *argv[], int ac)
+dochq(const char *argv[], int ac)
 {
-	/* In gnu-m4 mode, the only way to restore quotes is to have no
-	 * arguments at all. */
 	if (ac == 2) {
-		lquote[0] = LQUOTE, lquote[1] = EOS;
-		rquote[0] = RQUOTE, rquote[1] = EOS;
+		lquote[0] = LQUOTE; lquote[1] = EOS;
+		rquote[0] = RQUOTE; rquote[1] = EOS;
 	} else {
 		strlcpy(lquote, argv[2], sizeof(lquote));
-		if(ac > 3)
+		if (ac > 3) {
 			strlcpy(rquote, argv[3], sizeof(rquote));
-		else
-			rquote[0] = EOS;
+		} else {
+			rquote[0] = ECOMMT; rquote[1] = EOS;
+		}
 	}
 }
 
 /*
- * dochq - change quote characters
+ * dochc - change comment characters
  */
 static void
-dochq(const char *argv[], int argc)
+dochc(const char *argv[], int argc)
 {
-	if (argc > 2) {
-		if (*argv[2])
-			strlcpy(lquote, argv[2], sizeof(lquote));
-		else {
-			lquote[0] = LQUOTE;
-			lquote[1] = EOS;
-		}
-		if (argc > 3) {
-			if (*argv[3])
-				strlcpy(rquote, argv[3], sizeof(rquote));
-		} else
-			strcpy(rquote, lquote);
-	} else {
-		lquote[0] = LQUOTE, lquote[1] = EOS;
-		rquote[0] = RQUOTE, rquote[1] = EOS;
-	}
-}
-
-static void
-gnu_dochc(const char *argv[], int ac)
-{
-	/* In gnu-m4 mode, no arguments mean no comment
-	 * arguments at all. */
-	if (ac == 2) {
+/* XXX Note that there is no difference between no argument and a single
+ * empty argument.
+ */
+	if (argc == 2) {
 		scommt[0] = EOS;
 		ecommt[0] = EOS;
 	} else {
-		if (*argv[2])
-			strlcpy(scommt, argv[2], sizeof(scommt));
-		else
-			scommt[0] = SCOMMT, scommt[1] = EOS;
-		if(ac > 3 && *argv[3])
+		strlcpy(scommt, argv[2], sizeof(scommt));
+		if (argc == 3) {
+			ecommt[0] = ECOMMT; ecommt[1] = EOS;
+		} else {
 			strlcpy(ecommt, argv[3], sizeof(ecommt));
-		else
-			ecommt[0] = ECOMMT, ecommt[1] = EOS;
+		}
 	}
 }
+
 /*
- * dochc - change comment characters
+ * dom4wrap - expand text at EOF
  */
 static void
-dochc(const char *argv[], int argc)
+dom4wrap(const char *text)
 {
-	if (argc > 2) {
-		if (*argv[2])
-			strlcpy(scommt, argv[2], sizeof(scommt));
-		if (argc > 3) {
-			if (*argv[3])
-				strlcpy(ecommt, argv[3], sizeof(ecommt));
-		}
+	if (wrapindex >= maxwraps) {
+		if (maxwraps == 0)
+			maxwraps = 16;
 		else
-			ecommt[0] = ECOMMT, ecommt[1] = EOS;
+			maxwraps *= 2;
+		m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
+		   "too many m4wraps");
 	}
-	else {
-		scommt[0] = SCOMMT, scommt[1] = EOS;
-		ecommt[0] = ECOMMT, ecommt[1] = EOS;
-	}
+	m4wraps[wrapindex++] = xstrdup(text);
 }
 
 /*
@@ -878,7 +811,7 @@
 	if (outfile[n] == NULL) {
 		char fname[] = _PATH_DIVNAME;
 
-		if ((fd = mkstemp(fname)) < 0 ||
+		if ((fd = mkstemp(fname)) < 0 || 
 			(outfile[n] = fdopen(fd, "w+")) == NULL)
 				err(1, "%s: cannot divert", fname);
 		if (unlink(fname) == -1)
@@ -899,10 +832,15 @@
 
 	if (argc > 2) {
 		for (ind = 2; ind < argc; ind++) {
-			n = atoi(argv[ind]);
-			if (n > 0 && n < maxout && outfile[n] != NULL)
-				getdiv(n);
-
+			const char *errstr;
+			n = strtonum(argv[ind], 1, INT_MAX, &errstr);
+			if (errstr) {
+				if (errno == EINVAL && mimic_gnu)
+					getdivfile(argv[ind]);
+			} else {
+				if (n < maxout && outfile[n] != NULL)
+					getdiv(n);
+			}
 		}
 	}
 	else
@@ -935,7 +873,7 @@
 #endif
 	if (fc >= ap && fc < ap + strlen(ap))
 		for (k = fc + nc - 1; k >= fc; k--)
-			putback(*k);
+			pushback(*k);
 }
 
 /*
@@ -1005,7 +943,7 @@
 	 * "to"
 	 */
 		while (*from)
-			mapvec[(unsigned char)(*from++)] = (*to) ?
+			mapvec[(unsigned char)(*from++)] = (*to) ? 
 				(unsigned char)(*to++) : 0;
 
 		while (*src) {
@@ -1039,18 +977,29 @@
 handledash(char *buffer, char *end, const char *src)
 {
 	char *p;
-
+	
 	p = buffer;
 	while(*src) {
 		if (src[1] == '-' && src[2]) {
 			unsigned char i;
-			for (i = (unsigned char)src[0];
-			    i <= (unsigned char)src[2]; i++) {
-				*p++ = i;
-				if (p == end) {
-					*p = '\0';
-					return buffer;
+			if ((unsigned char)src[0] <= (unsigned char)src[2]) {
+				for (i = (unsigned char)src[0]; 
+				    i <= (unsigned char)src[2]; i++) {
+					*p++ = i;
+					if (p == end) {
+						*p = '\0';
+						return buffer;
+					}
 				}
+			} else {
+				for (i = (unsigned char)src[0]; 
+				    i >= (unsigned char)src[2]; i--) {
+					*p++ = i;
+					if (p == end) {
+						*p = '\0';
+						return buffer;
+					}
+				}
 			}
 			src += 3;
 		} else
Index: misc.c
===================================================================
--- misc.c	(revision 198625)
+++ misc.c	(working copy)
@@ -1,4 +1,4 @@
-/*	$OpenBSD: misc.c,v 1.27 2002/04/26 16:15:16 espie Exp $	*/
+/*	$OpenBSD: misc.c,v 1.41 2009/10/14 17:19:47 sthen Exp $	*/
 /*	$NetBSD: misc.c,v 1.6 1995/09/28 05:37:41 tls Exp $	*/
 
 /*
@@ -16,11 +16,7 @@
  * 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.
  *
@@ -37,22 +33,10 @@
  * SUCH DAMAGE.
  */
 
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)misc.c	8.1 (Berkeley) 6/6/93";
-#else
-#if 0
-static char rcsid[] = "$OpenBSD: misc.c,v 1.27 2002/04/26 16:15:16 espie Exp $";
-#endif
-#endif
-#endif /* not lint */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
 #include <sys/types.h>
 #include <errno.h>
 #include <unistd.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -70,11 +54,11 @@
 static size_t strsize = STRSPMAX;
 static size_t bufsize = BUFSIZE;
 
-char *buf;			/* push-back buffer	       */
-char *bufbase;			/* the base for current ilevel */
-char *bbase[MAXINP];		/* the base for each ilevel    */
-char *bp; 			/* first available character   */
-char *endpbb;			/* end of push-back buffer     */
+unsigned char *buf;			/* push-back buffer	       */
+unsigned char *bufbase;			/* the base for current ilevel */
+unsigned char *bbase[MAXINP];		/* the base for each ilevel    */
+unsigned char *bp; 			/* first available character   */
+unsigned char *endpbb;			/* end of push-back buffer     */
 
 
 /*
@@ -92,10 +76,10 @@
 		return (t - s1);
 }
 /*
- *  putback - push character back onto input
+ *  pushback - push character back onto input
  */
 void
-putback(int c)
+pushback(int c)
 {
 	if (c == EOF)
 		return;
@@ -106,7 +90,7 @@
 
 /*
  *  pbstr - push string back onto input
- *          putback is replicated to improve
+ *          pushback is replicated to improve
  *          performance.
  */
 void
@@ -115,7 +99,7 @@
 	size_t n;
 
 	n = strlen(s);
-	while ((size_t)(endpbb - bp) <= n)
+	while (endpbb - bp <= n)
 		enlarge_bufspace();
 	while (n > 0)
 		*bp++ = s[--n];
@@ -127,16 +111,36 @@
 void
 pbnum(int n)
 {
+	pbnumbase(n, 10, 0);
+}
+
+void
+pbnumbase(int n, int base, int d)
+{
+	static char digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
 	int num;
+	int printed = 0;
 
+	if (base > 36)
+		m4errx(1, "base %d > 36: not supported.", base);
+
+	if (base < 2)
+		m4errx(1, "bad base %d for conversion.", base);
+
 	num = (n < 0) ? -n : n;
 	do {
-		putback(num % 10 + '0');
+		pushback(digits[num % base]);
+		printed++;
 	}
-	while ((num /= 10) > 0);
+	while ((num /= base) > 0);
 
 	if (n < 0)
-		putback('-');
+		printed++;
+	while (printed++ < d)
+		pushback('0');
+
+	if (n < 0)
+		pushback('-');
 }
 
 /*
@@ -146,20 +150,20 @@
 pbunsigned(unsigned long n)
 {
 	do {
-		putback(n % 10 + '0');
+		pushback(n % 10 + '0');
 	}
 	while ((n /= 10) > 0);
 }
 
-void
-initspaces(void)
+void 
+initspaces()
 {
 	int i;
 
-	strspace = xalloc(strsize+1);
+	strspace = xalloc(strsize+1, NULL);
 	ep = strspace;
 	endest = strspace+strsize;
-	buf = (char *)xalloc(bufsize);
+	buf = (unsigned char *)xalloc(bufsize, NULL);
 	bufbase = buf;
 	bp = buf;
 	endpbb = buf + bufsize;
@@ -167,8 +171,8 @@
 		bbase[i] = buf;
 }
 
-void
-enlarge_strspace(void)
+void 
+enlarge_strspace()
 {
 	char *newstrspace;
 	int i;
@@ -178,9 +182,9 @@
 	if (!newstrspace)
 		errx(1, "string space overflow");
 	memcpy(newstrspace, strspace, strsize/2);
-	for (i = 0; i <= sp; i++)
+	for (i = 0; i <= sp; i++) 
 		if (sstack[i])
-			mstack[i].sstr = (mstack[i].sstr - strspace)
+			mstack[i].sstr = (mstack[i].sstr - strspace) 
 			    + newstrspace;
 	ep = (ep-strspace) + newstrspace;
 	free(strspace);
@@ -189,15 +193,13 @@
 }
 
 void
-enlarge_bufspace(void)
+enlarge_bufspace()
 {
-	char *newbuf;
+	unsigned char *newbuf;
 	int i;
 
-	bufsize *= 2;
-	newbuf = realloc(buf, bufsize);
-	if (!newbuf)
-		errx(1, "too many characters pushed back");
+	bufsize += bufsize/2;
+	newbuf = xrealloc(buf, bufsize, "too many characters pushed back");
 	for (i = 0; i < MAXINP; i++)
 		bbase[i] = (bbase[i]-buf)+newbuf;
 	bp = (bp-buf)+newbuf;
@@ -212,7 +214,7 @@
 void
 chrsave(int c)
 {
-	if (ep >= endest)
+	if (ep >= endest) 
 		enlarge_strspace();
 	*ep++ = c;
 }
@@ -226,7 +228,7 @@
 	int c;
 
 	if (active == outfile[n])
-		errx(1, "undivert: diversion still active");
+		m4errx(1, "undivert: diversion still active.");
 	rewind(outfile[n]);
 	while ((c = getc(outfile[n])) != EOF)
 		putc(c, active);
@@ -235,7 +237,7 @@
 }
 
 void
-onintr(int signo __unused)
+onintr(int signo)
 {
 #define intrmessage	"m4: interrupted.\n"
 	write(STDERR_FILENO, intrmessage, sizeof(intrmessage)-1);
@@ -246,7 +248,7 @@
  * killdiv - get rid of the diversion files
  */
 void
-killdiv(void)
+killdiv()
 {
 	int n;
 
@@ -256,6 +258,24 @@
 		}
 }
 
+extern char *__progname;
+
+void
+m4errx(int eval, const char *fmt, ...)
+{
+	fprintf(stderr, "%s: ", __progname);
+	fprintf(stderr, "%s at line %lu: ", CURRENT_NAME, CURRENT_LINE);
+	if (fmt != NULL) {
+		va_list ap;
+
+		va_start(ap, fmt);
+		vfprintf(stderr, fmt, ap);
+		va_end(ap);
+	}
+	fprintf(stderr, "\n");
+	exit(eval);
+}
+
 /*
  * resizedivs: allocate more diversion files */
 void
@@ -263,24 +283,52 @@
 {
 	int i;
 
-	outfile = (FILE **)realloc(outfile, sizeof(FILE *) * n);
-	if (outfile == NULL)
-		    errx(1, "too many diverts %d", n);
+	outfile = (FILE **)xrealloc(outfile, sizeof(FILE *) * n, 
+	    "too many diverts %d", n);
 	for (i = maxout; i < n; i++)
 		outfile[i] = NULL;
 	maxout = n;
 }
 
 void *
-xalloc(size_t n)
+xalloc(size_t n, const char *fmt, ...)
 {
-	char *p = malloc(n);
+	void *p = malloc(n);
 
-	if (p == NULL)
-		err(1, "malloc");
+	if (p == NULL) {
+		if (fmt == NULL)
+			err(1, "malloc");
+		else {
+			va_list va;
+			
+			va_start(va, fmt);
+			verr(1, fmt, va);
+			va_end(va);
+		}
+	}
 	return p;
 }
 
+void *
+xrealloc(void *old, size_t n, const char *fmt, ...)
+{
+	char *p = realloc(old, n);
+
+	if (p == NULL) {
+		free(old);
+		if (fmt == NULL)
+			err(1, "realloc");
+		else {
+			va_list va;
+
+			va_start(va, fmt);
+			verr(1, fmt, va);
+			va_end(va);
+	    	}
+	}
+	return p;
+}
+
 char *
 xstrdup(const char *s)
 {
@@ -291,43 +339,53 @@
 }
 
 void
-usage(void)
+usage()
 {
-	fprintf(stderr,
-"usage: m4 [-d flags] [-t name] [-gs] [-D name[=value]]...\n"
-"          [-U name]... [-I dirname]... file...\n");
+	fprintf(stderr, "usage: m4 [-gPs] [-Dname[=value]] [-d flags] "
+			"[-I dirname] [-o filename]\n"
+			"\t[-t macro] [-Uname] [file ...]\n");
 	exit(1);
 }
 
-int
+int 
 obtain_char(struct input_file *f)
 {
 	if (f->c == EOF)
 		return EOF;
-	else if (f->c == '\n')
+
+	f->c = fgetc(f->file);
+	if (f->c == '\n')
 		f->lineno++;
 
-	f->c = fgetc(f->file);
 	return f->c;
 }
 
-void
+void 
 set_input(struct input_file *f, FILE *real, const char *name)
 {
 	f->file = real;
 	f->lineno = 1;
 	f->c = 0;
 	f->name = xstrdup(name);
+	emit_synchline();
 }
 
 void
+do_emit_synchline()
+{
+	fprintf(active, "#line %lu \"%s\"\n",
+	    infile[ilevel].lineno, infile[ilevel].name);
+	infile[ilevel].synch_lineno = infile[ilevel].lineno;
+}
+
+void 
 release_input(struct input_file *f)
 {
 	if (f->file != stdin)
 	    fclose(f->file);
 	f->c = EOF;
 	/*
-	 * XXX can't free filename, as there might still be
+	 * XXX can't free filename, as there might still be 
 	 * error information pointing to it.
 	 */
 }
@@ -346,12 +404,12 @@
 	pbstr(lquote);
 }
 
-/*
+/* 
  * buffer_mark/dump_buffer: allows one to save a mark in a buffer,
  * and later dump everything that was added since then to a file.
  */
 size_t
-buffer_mark(void)
+buffer_mark()
 {
 	return bp - buf;
 }
@@ -360,8 +418,8 @@
 void
 dump_buffer(FILE *f, size_t m)
 {
-	char *s;
+	unsigned char *s;
 
-	for (s = bp; s - buf > (int)m;)
+	for (s = bp; s-buf > m;)
 		fputc(*--s, f);
 }
