Synchronize with laster version of m4 from OpenBSD and NetBSD

This bring better compatibility with gnum4

Reviewed by:	cognet
Approved by:	cognet
Obtained from:	OpenBSD, NetBSD
This commit is contained in:
Baptiste Daroussin 2011-11-28 13:32:39 +00:00
parent 89b9325530
commit a841e1ebe6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=228063
30 changed files with 2488 additions and 1638 deletions

View file

@ -5,8 +5,20 @@
# if you want the paste & spaste macros.
PROG= m4
CFLAGS+=-DEXTENDED
CFLAGS+=-DEXTENDED -I${.CURDIR}/lib
LDADD= -ly -ll
# clang needs 1 while with gcc we can use 2
#WARNS= 1
SRCS= eval.c expr.c look.c main.c misc.c gnum4.c trace.c
SRCS= eval.c expr.c look.c main.c misc.c gnum4.c trace.c parser.y tokenizer.l
.PATH: ${.CURDIR}/lib
SRCS+= ohash_create_entry.c ohash_delete.c ohash_do.c ohash_entries.c \
ohash_enum.c ohash_init.c ohash_int.h 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>

View file

@ -1,4 +1,4 @@
/* $OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $ */
/* $OpenBSD: eval.c,v 1.69 2011/03/24 11:23:08 espie Exp $ */
/* $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $ */
/*
@ -16,7 +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.
* 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.
*
@ -33,19 +33,10 @@
* 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
@ -53,21 +44,21 @@ __FBSDID("$FreeBSD$");
*/
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <stdint.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);
@ -75,10 +66,9 @@ static void dotrace(const char *[], int, int);
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);
@ -86,7 +76,7 @@ static void map(char *, const char *, const char *, const char *);
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;
@ -95,7 +85,7 @@ unsigned long expansion_id;
* 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
@ -110,21 +100,20 @@ unsigned long expansion_id;
* 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]))
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);
}
@ -150,9 +139,12 @@ expand_builtin(const char *argv[], int argc, int td)
* have macro-or-builtin() type call. We adjust
* argc to avoid further checking..
*/
ac = argc;
/* 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) {
@ -184,9 +176,27 @@ expand_builtin(const char *argv[], int argc, int td)
* 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)
@ -200,7 +210,7 @@ expand_builtin(const char *argv[], int argc, int td)
* 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]);
@ -238,7 +248,7 @@ expand_builtin(const char *argv[], int argc, int td)
* dosys - execute system command
*/
if (argc > 2) {
fflush(NULL);
fflush(stdout);
sysval = system(argv[2]);
}
break;
@ -255,7 +265,7 @@ expand_builtin(const char *argv[], int argc, int td)
case ESYSCMDTYPE:
if (argc > 2)
doesyscmd(argv[2]);
break;
break;
case INCLTYPE:
if (argc > 2)
if (!doincl(argv[2]))
@ -271,7 +281,7 @@ expand_builtin(const char *argv[], int argc, int td)
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;
@ -279,19 +289,16 @@ expand_builtin(const char *argv[], int argc, int td)
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:
@ -314,7 +321,7 @@ expand_builtin(const char *argv[], int argc, int td)
pbstr(rquote);
pbstr(argv[n]);
pbstr(lquote);
putback(COMMA);
pushback(COMMA);
}
pbstr(rquote);
pbstr(argv[3]);
@ -350,7 +357,7 @@ expand_builtin(const char *argv[], int argc, int td)
*/
if (argc > 2)
for (n = 2; n < argc; n++)
remhash(argv[n], ALL);
macro_undefine(argv[n]);
break;
case POPDTYPE:
@ -361,7 +368,7 @@ expand_builtin(const char *argv[], int argc, int td)
*/
if (argc > 2)
for (n = 2; n < argc; n++)
remhash(argv[n], TOP);
macro_popdef(argv[n]);
break;
case MKTMTYPE:
@ -395,7 +402,7 @@ expand_builtin(const char *argv[], int argc, int td)
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
@ -441,7 +448,8 @@ expand_builtin(const char *argv[], int argc, int td)
* 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:
@ -488,8 +496,7 @@ expand_builtin(const char *argv[], int argc, int td)
pbstr(lquote);
break;
default:
errx(1, "%s at line %lu: eval: major botch.",
CURRENT_NAME, CURRENT_LINE);
m4errx(1, "eval: major botch.");
break;
}
}
@ -512,7 +519,7 @@ expand_macro(const char *argv[], int argc)
p--; /* last character of defn */
while (p > t) {
if (*(p - 1) != ARGFLAG)
PUTBACK(*p);
PUSHBACK(*p);
else {
switch (*p) {
@ -536,10 +543,10 @@ expand_macro(const char *argv[], int argc)
if (argc > 2) {
for (n = argc - 1; n > 2; n--) {
pbstr(argv[n]);
putback(COMMA);
pushback(COMMA);
}
pbstr(argv[2]);
}
}
break;
case '@':
if (argc > 2) {
@ -547,7 +554,7 @@ expand_macro(const char *argv[], int argc)
pbstr(rquote);
pbstr(argv[n]);
pbstr(lquote);
putback(COMMA);
pushback(COMMA);
}
pbstr(rquote);
pbstr(argv[2]);
@ -555,8 +562,8 @@ expand_macro(const char *argv[], int argc)
}
break;
default:
PUTBACK(*p);
PUTBACK('$');
PUSHBACK(*p);
PUSHBACK('$');
break;
}
p--;
@ -564,42 +571,20 @@ expand_macro(const char *argv[], int argc)
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;
if (!*name && !mimic_gnu)
m4errx(1, "null definition.");
else
p->defn = xstrdup(defn);
p->type = MACRTYPE;
if (STREQ(name, defn))
p->type |= RECDEF;
macro_define(name, defn);
}
/*
@ -609,16 +594,15 @@ dodefine(const char *name, const char *defn)
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);
}
}
@ -634,40 +618,28 @@ dodefn(const char *name)
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);
}
/*
@ -679,17 +651,14 @@ static void
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);
}
/*
@ -734,15 +703,10 @@ static int
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);
@ -760,97 +724,74 @@ dopaste(const char *pfile)
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
static void
gnu_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;
} else {
strlcpy(lquote, argv[2], sizeof(lquote));
if(ac > 3)
strlcpy(rquote, argv[3], sizeof(rquote));
else
rquote[0] = EOS;
}
}
/*
* dochq - change quote characters
*/
static void
dochq(const char *argv[], int argc)
dochq(const char *argv[], int ac)
{
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);
if (ac == 2) {
lquote[0] = LQUOTE; lquote[1] = EOS;
rquote[0] = RQUOTE; rquote[1] = EOS;
} else {
lquote[0] = LQUOTE, lquote[1] = EOS;
rquote[0] = RQUOTE, rquote[1] = EOS;
strlcpy(lquote, argv[2], sizeof(lquote));
if (ac > 3) {
strlcpy(rquote, argv[3], sizeof(rquote));
} else {
rquote[0] = ECOMMT; 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) {
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(ecommt, argv[3], sizeof(ecommt));
else
ecommt[0] = ECOMMT, ecommt[1] = EOS;
}
}
/*
* dochc - change comment characters
*/
static void
dochc(const char *argv[], int argc)
{
if (argc > 2) {
if (*argv[2])
strlcpy(scommt, argv[2], sizeof(scommt));
if (argc > 3) {
if (*argv[3])
strlcpy(ecommt, argv[3], sizeof(ecommt));
/* 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 {
strlcpy(scommt, argv[2], sizeof(scommt));
if (argc == 3) {
ecommt[0] = ECOMMT; ecommt[1] = EOS;
} else {
strlcpy(ecommt, argv[3], sizeof(ecommt));
}
}
}
/*
* dom4wrap - expand text at EOF
*/
static void
dom4wrap(const char *text)
{
if (wrapindex >= maxwraps) {
if (maxwraps == 0)
maxwraps = 16;
else
ecommt[0] = ECOMMT, ecommt[1] = EOS;
}
else {
scommt[0] = SCOMMT, scommt[1] = EOS;
ecommt[0] = ECOMMT, ecommt[1] = EOS;
maxwraps *= 2;
m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
"too many m4wraps");
}
m4wraps[wrapindex++] = xstrdup(text);
}
/*
@ -867,14 +808,14 @@ dodiv(int n)
resizedivs(n + 10);
else
n = 0; /* bitbucket */
}
}
if (n < 0)
n = 0; /* bitbucket */
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)
@ -895,10 +836,15 @@ doundiv(const char *argv[], int argc)
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
@ -931,7 +877,7 @@ dosub(const char *argv[], int argc)
#endif
if (fc >= ap && fc < ap + strlen(ap))
for (k = fc + nc - 1; k >= fc; k--)
putback(*k);
pushback(*k);
}
/*
@ -939,25 +885,11 @@ dosub(const char *argv[], int argc)
* map every character of s1 that is specified in from
* into s3 and replace in s. (source s1 remains untouched)
*
* This is a standard implementation of map(s,from,to) function of ICON
* language. Within mapvec, we replace every character of "from" with
* the corresponding character in "to". If "to" is shorter than "from",
* than the corresponding entries are null, which means that those
* characters dissapear altogether. Furthermore, imagine
* map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
* `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
* ultimately maps to `*'. In order to achieve this effect in an efficient
* manner (i.e. without multiple passes over the destination string), we
* loop over mapvec, starting with the initial source character. if the
* character value (dch) in this location is different than the source
* character (sch), sch becomes dch, once again to index into mapvec, until
* the character value stabilizes (i.e. sch = dch, in other words
* mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
* character, it will stabilize, since mapvec[0] == 0 at all times. At the
* end, we restore mapvec* back to normal where mapvec[n] == n for
* 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
* about 5 times faster than any algorithm that makes multiple passes over
* destination string.
* This is derived from the a standard implementation of map(s,from,to)
* function of ICON language. Within mapvec, we replace every character
* of "from" with the corresponding character in "to".
* If "to" is shorter than "from", than the corresponding entries are null,
* which means that those characters dissapear altogether.
*/
static void
map(char *dest, const char *src, const char *from, const char *to)
@ -966,6 +898,8 @@ map(char *dest, const char *src, const char *from, const char *to)
unsigned char sch, dch;
static char frombis[257];
static char tobis[257];
int i;
char seen[256];
static unsigned char mapvec[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
@ -1000,17 +934,21 @@ map(char *dest, const char *src, const char *from, const char *to)
* create a mapping between "from" and
* "to"
*/
while (*from)
mapvec[(unsigned char)(*from++)] = (*to) ?
(unsigned char)(*to++) : 0;
for (i = 0; i < 256; i++)
seen[i] = 0;
while (*from) {
if (!seen[(unsigned char)(*from)]) {
mapvec[(unsigned char)(*from)] = (unsigned char)(*to);
seen[(unsigned char)(*from)] = 1;
}
from++;
if (*to)
to++;
}
while (*src) {
sch = (unsigned char)(*src++);
dch = mapvec[sch];
while (dch != sch) {
sch = dch;
dch = mapvec[sch];
}
if ((*dest = (char)dch))
dest++;
}
@ -1040,12 +978,23 @@ handledash(char *buffer, char *end, const char *src)
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;

View file

@ -1,640 +1,47 @@
/* $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.18 2010/09/07 19:58:09 marco 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.
* 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 : exponent { ("*" | "/" | "%") exponent }
* exponent: 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;
int yyerror(const char *msg);
#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
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 exponent(int mayeval);
static int unary(int mayeval);
static int factor(int mayeval);
static int constant(int mayeval);
static int num(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++
extern void yy_scan_string(const char *);
extern int yyparse(void);
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;
}
/*
* 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 : exponent { ("*" | "/" | "%") exponent }
*/
static int
term(int mayeval)
{
int c, vl, vr;
vl = exponent(mayeval);
while ((c = skipws()) == '*' || c == '/' || c == '%') {
vr = exponent(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;
}
/*
* exponent : unary { "**" exponent }
*/
static int
exponent(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 __unused)
{
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);
copy_toeval = toeval;
yy_scan_string(toeval);
yyparse();
return end_result;
}

View file

@ -1,4 +1,4 @@
/* $OpenBSD: extern.h,v 1.29 2002/02/16 21:27:48 millert Exp $ */
/* $OpenBSD: extern.h,v 1.51 2011/09/27 07:24:02 espie Exp $ */
/* $NetBSD: extern.h,v 1.3 1996/01/13 23:25:24 pk Exp $ */
/*-
@ -16,7 +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.
* 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 +37,7 @@
*/
/* 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;
@ -45,7 +45,7 @@ extern unsigned long expansion_id;
extern int expr(const char *);
/* gnum4.c */
extern void addtoincludepath(const char *);
extern void addtoincludepath(const char *);
extern struct input_file *fopen_trypath(struct input_file *, const char *);
extern void doindir(const char *[], int);
extern void dobuiltin(const char *[], int);
@ -56,45 +56,67 @@ extern void doprintlineno(struct input_file *);
extern void doprintfilename(struct input_file *);
extern void doesyscmd(const char *);
extern void getdivfile(const char *);
extern void doformat(const char *[], int);
/* 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);
extern char *compute_prevep(void);
extern char *compute_prevep(void);
extern void getdiv(int);
extern ptrdiff_t indx(const char *, const char *);
extern void initspaces(void);
extern void initspaces(void);
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 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(); \
@ -111,60 +133,44 @@ extern void release_input(struct input_file *);
/* 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;
extern ndptr hashtab[]; /* hash table for macros etc. */
extern stae *mstack; /* stack of m4 machine */
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 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 const 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);
}

View file

@ -1,4 +1,4 @@
/* $OpenBSD: gnum4.c,v 1.18 2002/04/26 16:15:16 espie Exp $ */
/* $OpenBSD: gnum4.c,v 1.42 2011/11/06 12:25:43 espie Exp $ */
/*
* Copyright (c) 1999 Marc Espie
@ -24,7 +24,6 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@ -36,13 +35,13 @@ __FBSDID("$FreeBSD$");
#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"
@ -60,7 +59,7 @@ int mimic_gnu = 0;
* Then M4PATH env variable
*/
static struct path_entry {
struct path_entry {
char *name;
struct path_entry *next;
} *first, *last;
@ -133,7 +132,7 @@ dopath(struct input_file *i, const char *filename)
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;
}
@ -162,25 +161,28 @@ fopen_trypath(struct input_file *i, const char *filename)
void
doindir(const char *argv[], int argc)
{
ndptr p;
ndptr n;
struct macro_definition *p = NULL;
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
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]);
}
@ -198,7 +200,7 @@ static void exit_regerror(int, regex_t *);
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)
@ -212,9 +214,7 @@ addchars(const char *c, size_t n)
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;
@ -228,9 +228,7 @@ addchar(int c)
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;
}
@ -247,20 +245,21 @@ getstring(void)
static void
exit_regerror(int er, regex_t *re)
{
size_t errlen;
char *errbuf;
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);
if (n > (int)re->re_nsub)
warnx("No subexpression %d", n);
/* Subexpressions that did not match are
* not an error. */
else if (pm[n].rm_so != -1 &&
@ -302,7 +301,7 @@ add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm
continue;
}
}
addchar(*p);
addchar(*p);
}
}
@ -386,6 +385,14 @@ do_regexpindex(const char *string, regex_t *re, regmatch_t *pm)
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 +438,46 @@ twiddle(const char *p)
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 (argc > 4 && argv[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,13 +491,20 @@ doregexp(const char *argv[], int argc)
warnx("Too few arguments to regexp");
return;
}
/* special gnu case */
if (argv[3][0] == '\0' && mimic_gnu) {
if (argc == 4 || argv[4] == NULL)
return;
else
pbstr(argv[4]);
}
error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3],
REG_EXTENDED);
REG_EXTENDED|REG_NEWLINE);
if (error != 0)
exit_regerror(error, &re);
pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
if (argv[4] == NULL || argc == 4)
pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
if (argc == 4 || argv[4] == NULL)
do_regexpindex(argv[2], &re, pmatch);
else
do_regexp(argv[2], &re, argv[4], pmatch);
@ -477,17 +512,112 @@ doregexp(const char *argv[], int argc)
regfree(&re);
}
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 = NULL;
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, __DECONST(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, __DECONST(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 ((long)l > extra)
l = extra;
if (!left_padded) {
while ((long)l < width--)
addchar(' ');
}
addchars(thisarg, l);
if (left_padded) {
while ((long)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] = __DECONST(char *, "sh");
argv[1] = __DECONST(char *, "-c");
argv[2] = __DECONST(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 +629,7 @@ doesyscmd(const char *cmd)
(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 +648,18 @@ doesyscmd(const char *cmd)
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);
}

75
usr.bin/m4/lib/ohash.h Normal file
View file

@ -0,0 +1,75 @@
#ifndef OHASH_H
#define OHASH_H
/* $OpenBSD: ohash.h,v 1.8 2005/12/29 18:54:47 jaredy 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.
*
* $FreeBSD$
*/
/* Open hashing support.
* Open hashing was chosen because it is much lighter than other hash
* techniques, and more efficient in most cases.
*/
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 *, u_int32_t);
unsigned int ohash_lookup_memory(struct ohash *, const char *,
size_t, u_int32_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

View file

@ -0,0 +1,40 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#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;
}

View file

@ -0,0 +1,33 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#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
}

113
usr.bin/m4/lib/ohash_do.c Normal file
View file

@ -0,0 +1,113 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#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 = __DECONST(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 __DECONST(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;
}

View file

@ -0,0 +1,28 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "ohash_int.h"
unsigned int
ohash_entries(struct ohash *h)
{
return h->total - h->deleted;
}

View file

@ -0,0 +1,38 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#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 __DECONST(void *, h->t[(*pos)++].p);
return NULL;
}

231
usr.bin/m4/lib/ohash_init.3 Normal file
View file

@ -0,0 +1,231 @@
.\" $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.
.\"
.\" $FreeBSD$
.\"
.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.

View file

@ -0,0 +1,43 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#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;
}

View file

@ -0,0 +1,25 @@
/* $OpenBSD: ohash_int.h,v 1.3 2006/01/16 15:52:25 espie Exp $ */
/* $FreeBSD$ */
#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#include <sys/types.h>
#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

View file

@ -0,0 +1,92 @@
.\" $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.
.\"
.\" $FreeBSD$
.\"
.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.

View file

@ -0,0 +1,38 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#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;
}

View file

@ -0,0 +1,70 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#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;
}

View file

@ -0,0 +1,66 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#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;
}

View file

@ -0,0 +1,29 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "ohash_int.h"
unsigned int
ohash_qlookup(struct ohash *h, const char *s)
{
const char *e = NULL;
return ohash_qlookupi(h, s, &e);
}

View file

@ -0,0 +1,31 @@
/* $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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#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);
}

View file

@ -1,4 +1,4 @@
/* $OpenBSD: look.c,v 1.10 2002/04/26 16:15:16 espie Exp $ */
/* $OpenBSD: look.c,v 1.22 2010/09/07 19:58:09 marco Exp $ */
/*
* Copyright (c) 1989, 1993
@ -15,7 +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.
* 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.
*
@ -31,13 +31,6 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* 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$");
@ -50,21 +43,52 @@ __FBSDID("$FreeBSD$");
#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(size_t s, __unused void *u)
{
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(void *p, __unused size_t s, __unused void *u)
{
free(p);
}
void *
element_alloc(size_t s, __unused void *u)
{
return xalloc(s, "element alloc");
}
void
init_macros(void)
{
ohash_init(&macros, 10, &macro_info);
}
/*
@ -73,74 +97,181 @@ hash(const char *name)
ndptr
lookup(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);
return ohash_find(&macros, ohash_qlookup(&macros, name));
}
/*
* 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)
struct macro_definition *
lookup_macro_definition(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;
p = ohash_find(&macros, ohash_qlookup(&macros, name));
if (p)
return p->d;
else
return NULL;
}
static void
freent(ndptr p)
setup_definition(struct macro_definition *d, const char *defn, const char *name)
{
free((char *) p->name);
if (p->defn != null)
free((char *) p->defn);
free((char *) p);
ndptr 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 = __DECONST(char *, null);
else
d->defn = xstrdup(defn);
d->type = MACRTYPE;
}
if (STREQ(name, defn))
d->type |= RECDEF;
}
/*
* remove an entry from the hashtable
*/
void
remhash(const char *name, int all)
static ndptr
create_entry(const char *name)
{
unsigned int h;
ndptr xp, tp, mp;
const char *end = NULL;
unsigned int i;
ndptr n;
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;
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;
}
void
macro_define(const char *name, const char *defn)
{
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);
}
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;
}

View file

@ -1,201 +1,266 @@
.\" @(#) $OpenBSD: m4.1,v 1.24 2002/04/18 18:57:23 espie Exp $
.\" $NetBSD: m4.1,v 1.22 2010/05/14 17:14:28 joerg Exp $
.\" @(#) $OpenBSD: m4.1,v 1.56 2009/10/14 17:19:47 sthen Exp $
.\"
.\" 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.
.\"
.\" $FreeBSD$
.\"
.Dd July 3, 2004
.Dd 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 \&( .
.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 Op Pf = Ns Ar value
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 +268,46 @@ shift, relational, bitwise, and parentheses operators.
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 +315,114 @@ first the directories specified with
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 +431,20 @@ Returns the current file's line number.
.It Ic __file__
Returns the current file's name.
.El
.Sh EXIT STATUS
.Ex -std
.Pp
The
.Ic m4exit
macro may be used to change the exit status from the input file.
.Sh COMPATIBILITY
.Sh STANDARDS
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 +453,43 @@ For portability, one should not use the macros
.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.

View file

@ -1,4 +1,4 @@
/* $OpenBSD: main.c,v 1.53 2002/04/26 16:15:16 espie Exp $ */
/* $OpenBSD: main.c,v 1.80 2011/09/27 07:24:02 espie Exp $ */
/* $NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $ */
/*-
@ -16,7 +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.
* 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.
*
@ -33,73 +33,59 @@
* SUCH DAMAGE.
*/
#ifndef lint
#if 0
static char copyright[] =
"@(#) Copyright (c) 1989, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif
#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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <assert.h>
#include <signal.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <err.h>
#include <locale.h>
#include <ohash.h>
#include "mdef.h"
#include "stdd.h"
#include "extern.h"
#include "pathnames.h"
ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */
stae *mstack; /* stack of m4 machine */
char *sstack; /* shadow stack, for string space extension */
stae *mstack; /* stack of m4 machine */
char *sstack; /* shadow stack, for string space extension */
static size_t STACKMAX; /* current maximum size of stack */
int sp; /* current m4 stack pointer */
int fp; /* m4 call frame pointer */
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.. */
int ilevel = 0; /* input file stack pointer */
int oindex = 0; /* diversion index.. */
const char *null = ""; /* as it says.. just a null.. */
char **m4wraps = NULL; /* m4wraps array. */
int maxwraps = 0; /* size of m4wraps array */
int wrapindex = 0; /* current offset in m4wraps */
char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */
char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */
char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */
char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */
int 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 */
static const struct keyblk keywrds[] = { /* m4 keywords to be installed */
struct keyblk {
const char *knam; /* keyword name */
int ktyp; /* keyword type */
};
struct keyblk keywrds[] = { /* m4 keywords to be installed */
{ "include", INCLTYPE },
{ "sinclude", SINCTYPE },
{ "define", DEFITYPE },
@ -120,7 +106,7 @@ static const struct keyblk keywrds[] = { /* m4 keywords to be installed */
#ifdef EXTENDED
{ "paste", PASTTYPE },
{ "spaste", SPASTYPE },
/* Newer extensions, needed to handle gnu-m4 scripts */
/* Newer extensions, needed to handle gnu-m4 scripts */
{ "indir", INDIRTYPE},
{ "builtin", BUILTINTYPE},
{ "patsubst", PATSTYPE},
@ -138,6 +124,7 @@ static const struct keyblk keywrds[] = { /* m4 keywords to be installed */
{ "undivert", UNDVTYPE | NOARGS },
{ "divnum", DIVNTYPE | NOARGS },
{ "maketemp", MKTMTYPE },
{ "mkstemp", MKTMTYPE },
{ "errprint", ERRPTYPE | NOARGS },
{ "m4wrap", M4WRTYPE | NOARGS },
{ "m4exit", EXITTYPE | NOARGS },
@ -170,43 +157,41 @@ static void macro(void);
static void initkwds(void);
static ndptr inspect(int, char *);
static int do_look_ahead(int, const char *);
static void reallyoutputstr(const char *);
static void reallyputchar(int);
static void enlarge_stack(void);
int 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);
@ -214,8 +199,11 @@ main(int argc, char *argv[])
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;
@ -224,7 +212,7 @@ main(int argc, char *argv[])
set_trace_flags(optarg);
break;
case 's':
synccpp = 1;
synch_lines = 1;
break;
case 't':
mark_traced(optarg, 1);
@ -233,51 +221,55 @@ main(int argc, char *argv[])
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) {
sp = -1; /* stack pointer initialized */
fp = 0; /* frame pointer initialized */
sp = -1; /* stack pointer initialized */
fp = 0; /* frame pointer initialized */
set_input(infile+0, stdin, "stdin");
/* default input (naturally) */
if ((inname[0] = strdup("-")) == NULL)
err(1, NULL);
inlineno[0] = 1;
emitline();
macro();
} else
for (; argc--; ++argv) {
p = *argv;
if (p[0] == '-' && p[1] == EOS)
set_input(infile, stdin, "stdin");
else if (fopen_trypath(infile, p) == NULL) {
warn("%s", p);
rval = 1;
continue;
}
else if (fopen_trypath(infile, p) == NULL)
err(1, "%s", p);
sp = -1;
fp = 0;
if ((inname[0] = strdup(p)) == NULL)
err(1, NULL);
inlineno[0] = 1;
emitline();
macro();
release_input(infile);
release_input(infile);
}
if (*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)
@ -290,7 +282,7 @@ main(int argc, char *argv[])
(void) fclose(outfile[0]);
}
exit(rval);
return 0;
}
/*
@ -310,17 +302,17 @@ do_look_ahead(int t, const char *token)
for (i = 1; *++token; i++) {
t = gpbc();
if (t == EOF || (unsigned char)t != (unsigned char)*token) {
putback(t);
pushback(t);
while (--i)
putback(*--token);
pushback(*--token);
return 0;
}
}
return 1;
}
#define LOOK_AHEAD(t, token) (t != EOF && \
(unsigned char)(t)==(unsigned char)(token)[0] && \
#define LOOK_AHEAD(t, token) (t != EOF && \
(unsigned char)(t)==(unsigned char)(token)[0] && \
do_look_ahead(t,token))
/*
@ -336,61 +328,8 @@ macro(void)
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++);
/*
@ -416,32 +355,77 @@ macro(void)
} else {
if (nlpar > 0) {
if (sp < 0)
putc(l, active);
reallyputchar(l);
else
CHRSAVE(l);
}
}
}
while (nlpar != 0);
}
else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
fputs(scommt, active);
} else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
reallyoutputstr(scommt);
for(;;) {
t = gpbc();
if (LOOK_AHEAD(t, ecommt)) {
fputs(ecommt, active);
reallyoutputstr(ecommt);
break;
}
if (t == EOF)
break;
putc(t, active);
reallyputchar(t);
}
}
} else if (t == '_' || isalpha(t)) {
p = inspect(t, token);
if (p != NULL)
pushback(l = gpbc());
if (p == NULL || (l != LPAREN &&
(macro_getdef(p)->type & NEEDARGS) != 0))
outputstr(token);
else {
/*
* real thing.. First build a call frame:
*/
pushf(fp); /* previous call frm */
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 == (int)STACKMAX)
errx(1, "internal stack overflow");
eval((const char **) mstack+fp+1, 2,
CALTYP, TRACESTATUS);
ep = PREVEP; /* flush strspace */
sp = PREVSP; /* previous sp.. */
fp = PREVFP; /* rewind stack...*/
}
}
} else if (t == EOF) {
if (sp > -1 && ilevel <= 0) {
warnx( "unexpected end of input, unclosed parenthesis:");
dump_stack(paren, PARLEV);
exit(1);
}
if (ilevel <= 0)
break; /* all done thanks.. */
release_input(infile+ilevel--);
emit_synchline();
bufbase = bbase[ilevel];
continue;
} else if (sp < 0) { /* not in a macro at all */
reallyputchar(t); /* output directly.. */
}
else switch(t) {
@ -449,9 +433,10 @@ macro(void)
case LPAREN:
if (PARLEV > 0)
chrsave(t);
while (isspace(l = gpbc()))
; /* skip blank, tab, nl.. */
putback(l);
while (isspace(l = gpbc())) /* skip blank, tab, nl.. */
if (PARLEV > 0)
chrsave(l);
pushback(l);
record(paren, PARLEV++);
break;
@ -461,11 +446,11 @@ macro(void)
else { /* end of argument list */
chrsave(EOS);
if ((uintptr_t)sp == STACKMAX)
if (sp == (int)STACKMAX)
errx(1, "internal stack overflow");
eval((const char **) mstack+fp+1, sp-fp,
CALTYP);
CALTYP, TRACESTATUS);
ep = PREVEP; /* flush strspace */
sp = PREVSP; /* previous sp.. */
@ -478,7 +463,7 @@ macro(void)
chrsave(EOS); /* new argument */
while (isspace(l = gpbc()))
;
putback(l);
pushback(l);
pushs(ep);
} else
chrsave(t);
@ -486,14 +471,14 @@ macro(void)
default:
if (LOOK_AHEAD(t, scommt)) {
char *pc;
for (pc = scommt; *pc; pc++)
chrsave(*pc);
char *cp;
for (cp = scommt; *cp; cp++)
chrsave(*cp);
for(;;) {
t = gpbc();
if (LOOK_AHEAD(t, ecommt)) {
for (pc = ecommt; *pc; pc++)
chrsave(*pc);
for (cp = ecommt; *cp; cp++)
chrsave(*cp);
break;
}
if (t == EOF)
@ -514,17 +499,43 @@ 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)
@ -532,14 +543,13 @@ inspect(int c, char *tp)
char *name = tp;
char *etp = tp+MAXTOK;
ndptr p;
unsigned int h;
h = *tp++ = c;
*tp++ = c;
while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
h = (h << 5) + h + (*tp++ = c);
*tp++ = c;
if (c != EOF)
PUTBACK(c);
PUSHBACK(c);
*tp = EOS;
/* token is too long, it won't match anything, but it can still
* be output. */
@ -547,17 +557,19 @@ inspect(int c, char *tp)
outputstr(name);
while (isalnum(c = gpbc()) || c == '_') {
if (sp < 0)
putc(c, active);
reallyputchar(c);
else
CHRSAVE(c);
}
*name = EOS;
return nil;
return NULL;
}
for (p = hashtab[h % HASHSIZE]; p != nil; p = p->nxtptr)
if (h == p->hv && STREQ(name, p->name))
break;
p = ohash_find(&macros, ohash_qlookupi(&macros, name, (const char **)&tp));
if (p == NULL)
return NULL;
if (macro_getdef(p) == NULL)
return NULL;
return p;
}
@ -571,47 +583,17 @@ inspect(int c, char *tp)
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;
for (i = 0; i < (int)MAXKEYS; i++) {
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)
{
@ -640,20 +622,11 @@ dump_stack(struct position *t, int lev)
static void
enlarge_stack(void)
{
STACKMAX *= 2;
mstack = realloc(mstack, sizeof(stae) * STACKMAX);
sstack = realloc(sstack, STACKMAX);
if (mstack == NULL || sstack == NULL)
errx(1, "Evaluation stack overflow (%lu)",
(unsigned long)STACKMAX);
}
/* Emit preprocessor #line directive if -s option used. */
void
emitline(void)
{
if (synccpp)
fprintf(active, "#line %d \"%s\"\n", inlineno[ilevel],
inname[ilevel]);
STACKMAX += STACKMAX/2;
mstack = xrealloc(mstack, sizeof(stae) * STACKMAX,
"Evaluation stack overflow (%lu)",
(unsigned long)STACKMAX);
sstack = xrealloc(sstack, STACKMAX,
"Evaluation stack overflow (%lu)",
(unsigned long)STACKMAX);
}

View file

@ -1,4 +1,4 @@
/* $OpenBSD: mdef.h,v 1.21 2001/09/27 11:40:33 espie Exp $ */
/* $OpenBSD: mdef.h,v 1.31 2011/09/27 07:24:02 espie Exp $ */
/* $NetBSD: mdef.h,v 1.7 1996/01/13 23:25:27 pk Exp $ */
/*
@ -16,7 +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.
* 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,6 +36,12 @@
* $FreeBSD$
*/
#ifdef __GNUC__
# define UNUSED __attribute__((__unused__))
#else
# define UNUSED
#endif
#define MACRTYPE 1
#define DEFITYPE 2
#define EXPRTYPE 3
@ -79,7 +85,9 @@
#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 */
@ -108,13 +116,12 @@
*/
#define EOS '\0'
#define MAXINP 10 /* maximum include files */
#define MAXOUT 10 /* maximum # of diversions */
#define MAXINP 10 /* maximum include files */
#define MAXOUT 10 /* maximum # of diversions */
#define BUFSIZE 4096 /* starting size of pushback buffer */
#define INITSTACKMAX 4096 /* starting size of call stack */
#define INITSTACKMAX 4096 /* starting size of call stack */
#define STRSPMAX 4096 /* starting size of string space */
#define MAXTOK 512 /* maximum chars in a tokn */
#define HASHSIZE 199 /* maximum size of hashtab */
#define MAXTOK 512 /* maximum chars in a tokn */
#define MAXCCHARS 5 /* max size of comment/quote delim */
#define ALL 1
@ -130,31 +137,31 @@
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 */
char *sstr; /* string entry */
} stae;
struct input_file {
FILE *file;
char *name;
unsigned long lineno;
int c;
FILE *file;
char *name;
unsigned long lineno;
unsigned long synch_lineno; /* used for -s */
int c;
};
#define CURRENT_NAME (infile[ilevel].name)
@ -162,31 +169,33 @@ struct input_file {
/*
* 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 == (int)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 == (int)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 == (int)STACKMAX) \
enlarge_stack();\
mstack[sp].sstr = (x); \
sstack[sp] = 0; \
} while (0)
/*
@ -195,25 +204,26 @@ struct input_file {
* +-------+ +-----+
* | arg 3 ----------------------->| str |
* +-------+ | . |
* | arg 2 ---PREVEP-----+ .
* | arg 2 ---PREVEP-----+ .
* +-------+ |
* . | | |
* +-------+ | +-----+
* +-------+ | +-----+
* | plev | PARLEV +-------->| str |
* +-------+ | . |
* | type | CALTYP .
* +-------+
* | prcf ---PREVFP--+
* +-------+ |
* +-------+ |
* | . | PREVSP |
* . |
* . |
* +-------+ |
* | <----------+
* +-------+
*
*/
#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)

View file

@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.27 2002/04/26 16:15:16 espie Exp $ */
/* $OpenBSD: misc.c,v 1.42 2010/09/07 19:58:09 marco Exp $ */
/* $NetBSD: misc.c,v 1.6 1995/09/28 05:37:41 tls Exp $ */
/*
@ -16,7 +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.
* 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.
*
@ -32,23 +32,13 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* 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>
@ -66,11 +56,11 @@ char *endest; /* end of string space */
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 */
/*
@ -88,10 +78,10 @@ indx(const char *s1, const char *s2)
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;
@ -102,7 +92,7 @@ putback(int c)
/*
* pbstr - push string back onto input
* putback is replicated to improve
* pushback is replicated to improve
* performance.
*/
void
@ -111,7 +101,7 @@ pbstr(const char *s)
size_t n;
n = strlen(s);
while ((size_t)(endpbb - bp) <= n)
while (endpbb - bp <= (long)n)
enlarge_bufspace();
while (n > 0)
*bp++ = s[--n];
@ -123,16 +113,36 @@ pbstr(const char *s)
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('-');
}
/*
@ -142,7 +152,7 @@ void
pbunsigned(unsigned long n)
{
do {
putback(n % 10 + '0');
pushback(n % 10 + '0');
}
while ((n /= 10) > 0);
}
@ -152,10 +162,10 @@ initspaces(void)
{
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;
@ -187,13 +197,11 @@ enlarge_strspace(void)
void
enlarge_bufspace(void)
{
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;
@ -222,7 +230,7 @@ getdiv(int n)
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);
@ -231,7 +239,7 @@ getdiv(int n)
}
void
onintr(int signo __unused)
onintr(__unused int signo)
{
#define intrmessage "m4: interrupted.\n"
write(STDERR_FILENO, intrmessage, sizeof(intrmessage)-1);
@ -252,6 +260,24 @@ killdiv(void)
}
}
extern char *__progname;
void
m4errx(int evaluation, 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(evaluation);
}
/*
* resizedivs: allocate more diversion files */
void
@ -259,21 +285,49 @@ resizedivs(int n)
{
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;
}
@ -289,9 +343,9 @@ xstrdup(const char *s)
void
usage(void)
{
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);
}
@ -300,10 +354,11 @@ obtain_char(struct input_file *f)
{
if (f->c == EOF)
return EOF;
else if (f->c == '\n')
f->lineno++;
f->c = fgetc(f->file);
if (f->c == '\n')
f->lineno++;
return f->c;
}
@ -314,6 +369,15 @@ set_input(struct input_file *f, FILE *real, const char *name)
f->lineno = 1;
f->c = 0;
f->name = xstrdup(name);
emit_synchline();
}
void
do_emit_synchline(void)
{
fprintf(active, "#line %lu \"%s\"\n",
infile[ilevel].lineno, infile[ilevel].name);
infile[ilevel].synch_lineno = infile[ilevel].lineno;
}
void
@ -356,8 +420,8 @@ buffer_mark(void)
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 > (long)m;)
fputc(*--s, f);
}

83
usr.bin/m4/parser.y Normal file
View file

@ -0,0 +1,83 @@
%{
/* $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.
*
* $FreeBSD$
*/
#include <stdint.h>
#define YYSTYPE int32_t
extern int32_t end_result;
extern int yylex(void);
extern int yyerror(const char *);
extern int yyparse(void);
%}
%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
;
%%

View file

@ -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,7 +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.
* 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.
*
@ -46,8 +46,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

View file

@ -1,4 +1,4 @@
/* $OpenBSD: stdd.h,v 1.4 1999/11/09 18:16:18 deraadt Exp $ */
/* $OpenBSD: stdd.h,v 1.6 2010/09/07 19:58:09 marco Exp $ */
/* $NetBSD: stdd.h,v 1.2 1995/09/28 05:37:50 tls Exp $ */
/*-
@ -16,7 +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.
* 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.
*

111
usr.bin/m4/tokenizer.l Normal file
View file

@ -0,0 +1,111 @@
%option nounput noinput
%{
/* $OpenBSD: tokenizer.l,v 1.7 2010/03/22 20:40:44 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.
*
* $FreeBSD$
*/
#include "parser.h"
#include <assert.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);
extern int yylex(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(void)
{
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(void)
{
long base;
char *next;
long l;
int d;
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')
d = *next - '0';
else if (*next >= 'a' && *next <= 'z')
d = *next - 'a' + 10;
else {
assert(*next >= 'A' && *next <= 'Z');
d = *next - 'A' + 10;
}
if (d >= base) {
fprintf(stderr,
"m4: error in number %s\n", yytext);
return 0;
}
l = base * l + d;
next++;
}
}
return l;
}

View file

@ -1,4 +1,4 @@
/* $OpenBSD: trace.c,v 1.6 2002/04/26 16:15:16 espie Exp $ */
/* $OpenBSD: trace.c,v 1.16 2010/09/07 19:58:09 marco Exp $ */
/*
* Copyright (c) 2001 Marc Espie.
*
@ -23,25 +23,21 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <stddef.h>
#include <stdio.h>
#include <err.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.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_ARGS 1
#define TRACE_EXPANSION 2
#define TRACE_QUOTE 4
#define TRACE_FILENAME 8
@ -50,81 +46,19 @@ int traced_macros = 0;
#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;
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;
}
unsigned int trace_flags = TRACE_QUOTE | TRACE_EXPANSION;
void
trace_file(const char *name)
{
if (traceout != stderr)
if (traceout && traceout != stderr)
fclose(traceout);
traceout = fopen(name, "w");
if (!traceout)
@ -168,21 +102,19 @@ set_trace_flags(const char *s)
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;
}
}
@ -194,7 +126,7 @@ frame_level(void)
int framep;
for (framep = fp, level = 0; framep != 0;
level++,framep = mstack[framep-2].sfra)
level++,framep = mstack[framep-3].sfra)
;
return level;
}
@ -203,25 +135,27 @@ static void
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;
@ -229,25 +163,25 @@ trace(const char *argv[], int argc, struct input_file *inp)
delim[1] = EOS;
for (i = 2; i < argc; i++) {
fprintf(traceout, "%s%s%s%s", delim,
(flags & TRACE_QUOTE) ? lquote : "",
(trace_flags & TRACE_QUOTE) ? lquote : "",
argv[i],
(flags & TRACE_QUOTE) ? rquote : "");
(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;
}
}
@ -255,10 +189,10 @@ 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");
}