head	1.13;
access;
symbols
	netbsd-11-0-RC4:1.13
	netbsd-11-0-RC3:1.13
	netbsd-11-0-RC2:1.13
	netbsd-11-0-RC1:1.13
	perseant-exfatfs-base-20250801:1.13
	netbsd-11:1.13.0.4
	netbsd-11-base:1.13
	netbsd-10-1-RELEASE:1.11.2.2
	perseant-exfatfs-base-20240630:1.13
	perseant-exfatfs:1.13.0.2
	perseant-exfatfs-base:1.13
	netbsd-8-3-RELEASE:1.10
	netbsd-9-4-RELEASE:1.10
	netbsd-10-0-RELEASE:1.11.2.2
	netbsd-10-0-RC6:1.11.2.2
	netbsd-10-0-RC5:1.11.2.2
	netbsd-10-0-RC4:1.11.2.2
	netbsd-10-0-RC3:1.11.2.2
	netbsd-10-0-RC2:1.11.2.2
	netbsd-10-0-RC1:1.11.2.2
	netbsd-10:1.11.0.2
	netbsd-10-base:1.11
	netbsd-9-3-RELEASE:1.10
	cjep_sun2x-base1:1.10
	cjep_sun2x:1.10.0.24
	cjep_sun2x-base:1.10
	cjep_staticlib_x-base1:1.10
	netbsd-9-2-RELEASE:1.10
	cjep_staticlib_x:1.10.0.22
	cjep_staticlib_x-base:1.10
	netbsd-9-1-RELEASE:1.10
	phil-wifi-20200421:1.10
	phil-wifi-20200411:1.10
	is-mlppp:1.10.0.20
	is-mlppp-base:1.10
	phil-wifi-20200406:1.10
	netbsd-8-2-RELEASE:1.10
	netbsd-9-0-RELEASE:1.10
	netbsd-9-0-RC2:1.10
	netbsd-9-0-RC1:1.10
	phil-wifi-20191119:1.10
	netbsd-9:1.10.0.18
	netbsd-9-base:1.10
	phil-wifi-20190609:1.10
	netbsd-8-1-RELEASE:1.10
	netbsd-8-1-RC1:1.10
	pgoyette-compat-merge-20190127:1.10
	pgoyette-compat-20190127:1.10
	pgoyette-compat-20190118:1.10
	pgoyette-compat-1226:1.10
	pgoyette-compat-1126:1.10
	pgoyette-compat-1020:1.10
	pgoyette-compat-0930:1.10
	pgoyette-compat-0906:1.10
	netbsd-7-2-RELEASE:1.9
	pgoyette-compat-0728:1.10
	netbsd-8-0-RELEASE:1.10
	phil-wifi:1.10.0.16
	phil-wifi-base:1.10
	pgoyette-compat-0625:1.10
	netbsd-8-0-RC2:1.10
	pgoyette-compat-0521:1.10
	pgoyette-compat-0502:1.10
	pgoyette-compat-0422:1.10
	netbsd-8-0-RC1:1.10
	pgoyette-compat-0415:1.10
	pgoyette-compat-0407:1.10
	pgoyette-compat-0330:1.10
	pgoyette-compat-0322:1.10
	pgoyette-compat-0315:1.10
	netbsd-7-1-2-RELEASE:1.9
	pgoyette-compat:1.10.0.14
	pgoyette-compat-base:1.10
	netbsd-7-1-1-RELEASE:1.9
	matt-nb8-mediatek:1.10.0.12
	matt-nb8-mediatek-base:1.10
	perseant-stdc-iso10646:1.10.0.10
	perseant-stdc-iso10646-base:1.10
	netbsd-8:1.10.0.8
	netbsd-8-base:1.10
	prg-localcount2-base3:1.10
	prg-localcount2-base2:1.10
	prg-localcount2-base1:1.10
	prg-localcount2:1.10.0.6
	prg-localcount2-base:1.10
	pgoyette-localcount-20170426:1.10
	bouyer-socketcan-base1:1.10
	pgoyette-localcount-20170320:1.10
	netbsd-7-1:1.9.0.16
	netbsd-7-1-RELEASE:1.9
	netbsd-7-1-RC2:1.9
	netbsd-7-nhusb-base-20170116:1.9
	bouyer-socketcan:1.10.0.4
	bouyer-socketcan-base:1.10
	pgoyette-localcount-20170107:1.10
	netbsd-7-1-RC1:1.9
	pgoyette-localcount-20161104:1.10
	netbsd-7-0-2-RELEASE:1.9
	localcount-20160914:1.10
	netbsd-7-nhusb:1.9.0.14
	netbsd-7-nhusb-base:1.9
	pgoyette-localcount-20160806:1.10
	pgoyette-localcount-20160726:1.10
	pgoyette-localcount:1.10.0.2
	pgoyette-localcount-base:1.10
	netbsd-7-0-1-RELEASE:1.9
	netbsd-7-0:1.9.0.12
	netbsd-7-0-RELEASE:1.9
	netbsd-7-0-RC3:1.9
	netbsd-7-0-RC2:1.9
	netbsd-7-0-RC1:1.9
	netbsd-5-2-3-RELEASE:1.5
	netbsd-5-1-5-RELEASE:1.5
	netbsd-6-0-6-RELEASE:1.8
	netbsd-6-1-5-RELEASE:1.8
	netbsd-7:1.9.0.10
	netbsd-7-base:1.9
	yamt-pagecache-base9:1.9
	yamt-pagecache-tag8:1.8.2.1
	netbsd-6-1-4-RELEASE:1.8
	netbsd-6-0-5-RELEASE:1.8
	tls-earlyentropy:1.9.0.8
	tls-earlyentropy-base:1.9
	riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.9
	riastradh-drm2-base3:1.9
	netbsd-6-1-3-RELEASE:1.8
	netbsd-6-0-4-RELEASE:1.8
	netbsd-5-2-2-RELEASE:1.5
	netbsd-5-1-4-RELEASE:1.5
	netbsd-6-1-2-RELEASE:1.8
	netbsd-6-0-3-RELEASE:1.8
	netbsd-5-2-1-RELEASE:1.5
	netbsd-5-1-3-RELEASE:1.5
	netbsd-6-1-1-RELEASE:1.8
	riastradh-drm2-base2:1.9
	riastradh-drm2-base1:1.9
	riastradh-drm2:1.9.0.4
	riastradh-drm2-base:1.9
	netbsd-6-1:1.8.0.10
	netbsd-6-0-2-RELEASE:1.8
	netbsd-6-1-RELEASE:1.8
	netbsd-6-1-RC4:1.8
	netbsd-6-1-RC3:1.8
	agc-symver:1.9.0.6
	agc-symver-base:1.9
	netbsd-6-1-RC2:1.8
	netbsd-6-1-RC1:1.8
	yamt-pagecache-base8:1.9
	netbsd-5-2:1.5.0.46
	netbsd-6-0-1-RELEASE:1.8
	yamt-pagecache-base7:1.9
	netbsd-5-2-RELEASE:1.5
	netbsd-5-2-RC1:1.5
	matt-nb6-plus-nbase:1.8
	yamt-pagecache-base6:1.9
	netbsd-6-0:1.8.0.8
	netbsd-6-0-RELEASE:1.8
	netbsd-6-0-RC2:1.8
	tls-maxphys:1.9.0.2
	tls-maxphys-base:1.9
	matt-nb6-plus:1.8.0.6
	matt-nb6-plus-base:1.8
	netbsd-6-0-RC1:1.8
	yamt-pagecache-base5:1.9
	yamt-pagecache-base4:1.9
	netbsd-6:1.8.0.4
	netbsd-6-base:1.8
	netbsd-5-1-2-RELEASE:1.5
	netbsd-5-1-1-RELEASE:1.5
	yamt-pagecache-base3:1.8
	yamt-pagecache-base2:1.8
	yamt-pagecache:1.8.0.2
	yamt-pagecache-base:1.8
	cherry-xenmp:1.7.0.4
	cherry-xenmp-base:1.7
	bouyer-quota2-nbase:1.7
	bouyer-quota2:1.7.0.2
	bouyer-quota2-base:1.7
	matt-mips64-premerge-20101231:1.7
	matt-nb5-mips64-premerge-20101231:1.5
	matt-nb5-pq3:1.5.0.44
	matt-nb5-pq3-base:1.5
	netbsd-5-1:1.5.0.42
	netbsd-5-1-RELEASE:1.5
	netbsd-5-1-RC4:1.5
	matt-nb5-mips64-k15:1.5
	netbsd-5-1-RC3:1.5
	netbsd-5-1-RC2:1.5
	netbsd-5-1-RC1:1.5
	netbsd-5-0-2-RELEASE:1.5
	matt-nb5-mips64-premerge-20091211:1.5
	matt-premerge-20091211:1.7
	OPENBSD20091026:1.1.1.1
	OPENBSD:1.1.1
	matt-nb5-mips64-u2-k2-k4-k7-k8-k9:1.5
	matt-nb4-mips64-k7-u2a-k9b:1.5
	matt-nb5-mips64-u1-k1-k5:1.5
	matt-nb5-mips64:1.5.0.40
	netbsd-5-0-1-RELEASE:1.5
	jym-xensuspend-nbase:1.5
	netbsd-5-0:1.5.0.38
	netbsd-5-0-RELEASE:1.5
	netbsd-5-0-RC4:1.5
	netbsd-5-0-RC3:1.5
	netbsd-5-0-RC2:1.5
	jym-xensuspend:1.5.0.36
	jym-xensuspend-base:1.5
	netbsd-5-0-RC1:1.5
	netbsd-5:1.5.0.34
	netbsd-5-base:1.5
	matt-mips64-base2:1.5
	matt-mips64:1.5.0.32
	mjf-devfs2:1.5.0.30
	mjf-devfs2-base:1.5
	netbsd-4-0-1-RELEASE:1.5
	wrstuden-revivesa-base-3:1.5
	wrstuden-revivesa-base-2:1.5
	wrstuden-fixsa-newbase:1.5
	wrstuden-revivesa-base-1:1.5
	yamt-pf42-base4:1.5
	yamt-pf42-base3:1.5
	hpcarm-cleanup-nbase:1.5
	yamt-pf42-baseX:1.5
	yamt-pf42-base2:1.5
	wrstuden-revivesa:1.5.0.28
	wrstuden-revivesa-base:1.5
	yamt-pf42:1.5.0.26
	yamt-pf42-base:1.5
	keiichi-mipv6-nbase:1.5
	keiichi-mipv6:1.5.0.24
	keiichi-mipv6-base:1.5
	matt-armv6-nbase:1.5
	matt-armv6-prevmlocking:1.5
	wrstuden-fixsa-base-1:1.5
	netbsd-4-0:1.5.0.22
	netbsd-4-0-RELEASE:1.5
	cube-autoconf:1.5.0.20
	cube-autoconf-base:1.5
	netbsd-4-0-RC5:1.5
	netbsd-4-0-RC4:1.5
	netbsd-4-0-RC3:1.5
	netbsd-4-0-RC2:1.5
	netbsd-4-0-RC1:1.5
	matt-armv6:1.5.0.18
	matt-armv6-base:1.5
	matt-mips64-base:1.5
	hpcarm-cleanup:1.5.0.16
	hpcarm-cleanup-base:1.5
	netbsd-3-1-1-RELEASE:1.5
	netbsd-3-0-3-RELEASE:1.5
	wrstuden-fixsa:1.5.0.14
	wrstuden-fixsa-base:1.5
	abandoned-netbsd-4-base:1.5
	abandoned-netbsd-4:1.5.0.8
	netbsd-3-1:1.5.0.10
	netbsd-3-1-RELEASE:1.5
	netbsd-3-0-2-RELEASE:1.5
	netbsd-3-1-RC4:1.5
	netbsd-3-1-RC3:1.5
	netbsd-3-1-RC2:1.5
	netbsd-3-1-RC1:1.5
	netbsd-4:1.5.0.12
	netbsd-4-base:1.5
	chap-midi-nbase:1.5
	netbsd-3-0-1-RELEASE:1.5
	chap-midi:1.5.0.6
	chap-midi-base:1.5
	netbsd-3-0:1.5.0.4
	netbsd-3-0-RELEASE:1.5
	netbsd-3-0-RC6:1.5
	netbsd-3-0-RC5:1.5
	netbsd-3-0-RC4:1.5
	netbsd-3-0-RC3:1.5
	netbsd-3-0-RC2:1.5
	netbsd-3-0-RC1:1.5
	netbsd-2-0-3-RELEASE:1.2.4.1
	netbsd-2-1:1.2.4.1.0.4
	netbsd-2-1-RELEASE:1.2.4.1
	netbsd-2-1-RC6:1.2.4.1
	netbsd-2-1-RC5:1.2.4.1
	netbsd-2-1-RC4:1.2.4.1
	netbsd-2-1-RC3:1.2.4.1
	netbsd-2-1-RC2:1.2.4.1
	netbsd-2-1-RC1:1.2.4.1
	netbsd-2-0-2-RELEASE:1.2.4.1
	netbsd-3:1.5.0.2
	netbsd-3-base:1.5
	netbsd-2-0-1-RELEASE:1.2.4.1
	netbsd-2:1.2.4.1.0.2
	netbsd-2-base:1.2.4.1
	netbsd-2-0-RELEASE:1.2.4.1
	netbsd-2-0-RC5:1.2.4.1
	netbsd-2-0-RC4:1.2.4.1
	netbsd-2-0-RC3:1.2.4.1
	netbsd-2-0-RC2:1.2.4.1
	netbsd-2-0-RC1:1.2.4.1
	netbsd-2-0:1.2.0.4
	netbsd-2-0-base:1.2
	netbsd-1-6-PATCH002-RELEASE:1.2
	netbsd-1-6-PATCH002:1.2
	netbsd-1-6-PATCH002-RC4:1.2
	netbsd-1-6-PATCH002-RC3:1.2
	netbsd-1-6-PATCH002-RC2:1.2
	netbsd-1-6-PATCH002-RC1:1.2
	netbsd-1-6-PATCH001:1.2
	netbsd-1-6-PATCH001-RELEASE:1.2
	netbsd-1-6-PATCH001-RC3:1.2
	netbsd-1-6-PATCH001-RC2:1.2
	netbsd-1-6-PATCH001-RC1:1.2
	fvdl_fs64_base:1.2
	netbsd-1-6-RELEASE:1.2
	netbsd-1-6-RC3:1.2
	netbsd-1-6-RC2:1.2
	netbsd-1-6-RC1:1.2
	netbsd-1-6:1.2.0.2
	netbsd-1-6-base:1.2;
locks; strict;
comment	@ * @;


1.13
date	2023.05.24.22.14.31;	author christos;	state Exp;
branches;
next	1.12;
commitid	xszB9JrWsQ7aygqE;

1.12
date	2023.05.24.21.58.19;	author lukem;	state Exp;
branches;
next	1.11;
commitid	l3XTAVeW1STfqgqE;

1.11
date	2021.12.07.20.24.07;	author rillig;	state Exp;
branches
	1.11.2.1;
next	1.10;
commitid	6mYn5gpw0pZl4LjD;

1.10
date	2016.01.16.16.59.18;	author christos;	state Exp;
branches;
next	1.9;
commitid	Vkv9xt0xkM9G6bRy;

1.9
date	2012.03.20.20.34.58;	author matt;	state Exp;
branches;
next	1.8;

1.8
date	2011.09.06.18.16.01;	author joerg;	state Exp;
branches
	1.8.2.1;
next	1.7;

1.7
date	2009.10.26.21.16.49;	author christos;	state Exp;
branches;
next	1.6;

1.6
date	2009.10.26.21.11.28;	author christos;	state Exp;
branches;
next	1.5;

1.5
date	2004.10.30.20.39.35;	author dsl;	state Exp;
branches;
next	1.4;

1.4
date	2004.06.20.22.20.15;	author jmc;	state Exp;
branches;
next	1.3;

1.3
date	2004.04.23.02.58.29;	author simonb;	state Exp;
branches;
next	1.2;

1.2
date	2002.01.21.21.49.57;	author tv;	state Exp;
branches
	1.2.4.1;
next	1.1;

1.1
date	2001.11.14.06.16.08;	author tv;	state Exp;
branches
	1.1.1.1;
next	;

1.11.2.1
date	2023.05.28.10.03.54;	author martin;	state Exp;
branches;
next	1.11.2.2;
commitid	E3UmuOwhllFEnIqE;

1.11.2.2
date	2023.05.28.10.07.11;	author martin;	state Exp;
branches;
next	;
commitid	8PgYi4ZAzXRKoIqE;

1.8.2.1
date	2012.04.17.00.09.35;	author yamt;	state Exp;
branches;
next	;

1.2.4.1
date	2004.06.22.07.16.11;	author tron;	state Exp;
branches;
next	;

1.1.1.1
date	2009.10.26.21.08.58;	author christos;	state Exp;
branches;
next	;


desc
@@


1.13
log
@use strchr
@
text
@/* $NetBSD: gnum4.c,v 1.12 2023/05/24 21:58:19 lukem Exp $ */
/* $OpenBSD: gnum4.c,v 1.39 2008/08/21 21:01:04 espie Exp $ */

/*
 * Copyright (c) 1999 Marc Espie
 *
 * 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.
 *
 * 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.
 */

/* 
 * functions needed to support gnu-m4 extensions, including a fake freezing
 */
#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#include <sys/cdefs.h>
__RCSID("$NetBSD: gnum4.c,v 1.12 2023/05/24 21:58:19 lukem Exp $");

#include <sys/param.h>
#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 <errno.h>
#include <unistd.h>
#include "mdef.h"
#include "stdd.h"
#include "extern.h"


int mimic_gnu = 0;
#ifndef SIZE_T_MAX
#define SIZE_T_MAX (size_t)~0ull
#endif

/*
 * Support for include path search
 * First search in the current directory.
 * If not found, and the path is not absolute, include path kicks in.
 * First, -I options, in the order found on the command line.
 * Then M4PATH env variable
 */

struct path_entry {
	char *name;
	struct path_entry *next;
} *first, *last;

static struct path_entry *new_path_entry(const char *);
static void ensure_m4path(void);
static struct input_file *dopath(struct input_file *, const char *);

static struct path_entry *
new_path_entry(const char *dirname)
{
	struct path_entry *n;

	n = malloc(sizeof(struct path_entry));
	if (!n)
		errx(1, "out of memory");
	n->name = strdup(dirname);
	if (!n->name)
		errx(1, "out of memory");
	n->next = 0;
	return n;
}
	
void 
addtoincludepath(const char *dirname)
{
	struct path_entry *n;

	n = new_path_entry(dirname);

	if (last) {
		last->next = n;
		last = n;
	}
	else
		last = first = n;
}

static void
ensure_m4path(void)
{
	static int envpathdone = 0;
	char *envpath;
	char *sweep;
	char *path;

	if (envpathdone)
		return;
	envpathdone = TRUE;
	envpath = getenv("M4PATH");
	if (!envpath) 
		return;
	/* for portability: getenv result is read-only */
	envpath = strdup(envpath);
	if (!envpath)
		errx(1, "out of memory");
	for (sweep = envpath; 
	    (path = strsep(&sweep, ":")) != NULL;)
	    addtoincludepath(path);
	free(envpath);
}

static
struct input_file *
dopath(struct input_file *i, const char *filename)
{
	char path[MAXPATHLEN];
	struct path_entry *pe;
	FILE *f;

	for (pe = first; pe; pe = pe->next) {
		snprintf(path, sizeof(path), "%s/%s", pe->name, filename);
		if ((f = fopen(path, "r")) != 0) {
			set_input(i, f, path);
			return i;
		}
	}
	return NULL;
}

struct input_file *
fopen_trypath(struct input_file *i, const char *filename)
{
	FILE *f;

	f = fopen(filename, "r");
	if (f != NULL) {
		set_input(i, f, filename);
		return i;
	}
	if (filename[0] == '/')
		return NULL;

	ensure_m4path();

	return dopath(i, filename);
}

void 
doindir(const char *argv[], int argc)
{
	ndptr n;
	struct macro_definition *p;

	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, is_traced(n));
}

void 
dobuiltin(const char *argv[], int argc)
{
	ndptr p;

	argv[1] = NULL;
	p = macro_getbuiltin(argv[2]);
	if (p != NULL)
		eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
	else
		m4errx(1, "unknown builtin %s.", argv[2]);
} 


/* We need some temporary buffer space, as pb pushes BACK and substitution
 * proceeds forward... */
static char *buffer;
static size_t bufsize = 0;
static size_t current = 0;

static void addchars(const char *, size_t);
static void addchar(int);
static char *twiddle(const char *);
static char *getstring(void);
static void exit_regerror(int, const char *, regex_t *) __dead;
static void do_subst(const char *, const char *, regex_t *, const char *,
    regmatch_t *);
static void do_regexpindex(const char *, const char *, regex_t *, regmatch_t *);
static void do_regexp(const char *, const char *, regex_t *, const char *, regmatch_t *);
static void add_sub(size_t, const char *, regex_t *, regmatch_t *);
static void add_replace(const char *, regex_t *, const char *, regmatch_t *);
#define addconstantstring(s) addchars((s), sizeof(s)-1)

static void 
addchars(const char *c, size_t n)
{
	if (n == 0)
		return;
	while (current + n > bufsize) {
		if (bufsize == 0)
			bufsize = 1024;
		else
			bufsize *= 2;
		buffer = xrealloc(buffer, bufsize, NULL);
	}
	memcpy(buffer+current, c, n);
	current += n;
}

static void 
addchar(int c)
{
	if (current +1 > bufsize) {
		if (bufsize == 0)
			bufsize = 1024;
		else
			bufsize *= 2;
		buffer = xrealloc(buffer, bufsize, NULL);
	}
	buffer[current++] = c;
}

static char *
getstring(void)
{
	addchar('\0');
	current = 0;
	return buffer;
}


static void 
exit_regerror(int er, const char *pat, regex_t *re)
{
	size_t 	errlen;
	char 	*errbuf;

	errlen = regerror(er, re, NULL, 0);
	errbuf = xalloc(errlen, 
	    "malloc in regerror: %lu", (unsigned long)errlen);
	regerror(er, re, errbuf, errlen);
	m4errx(1, "regular expression error: %s for: `%s'", errbuf, pat);
}

static void
add_sub(size_t n, const char *string, regex_t *re, regmatch_t *pm)
{
	if (n > re->re_nsub) {
		if (!quiet)
			warnx("No subexpression %zu", n);
		if (fatal_warnings)
			exit(EXIT_FAILURE);
	}
	/* Subexpressions that did not match are
	 * not an error.  */
	else if (pm[n].rm_so != -1 &&
	    pm[n].rm_eo != -1) {
		addchars(string + pm[n].rm_so,
			pm[n].rm_eo - pm[n].rm_so);
	}
}

/* Add replacement string to the output buffer, recognizing special
 * constructs and replacing them with substrings of the original string.
 */
static void 
add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
{
	const char *p;

	for (p = replace; *p != '\0'; p++) {
		if (*p == '&' && !mimic_gnu) {
			add_sub(0, string, re, pm);
			continue;
		}
		if (*p == '\\') {
			if (p[1] == '\\') {
				addchar(p[1]);
				p++;
				continue;
			}
			if (p[1] == '&') {
				if (mimic_gnu)
					add_sub(0, string, re, pm);
				else
					addchar(p[1]);
				p++;
				continue;
			}
			if (isdigit((unsigned char)p[1])) {
				add_sub(*(++p) - '0', string, re, pm);
				continue;
			}
		}
	    	addchar(*p);
	}
}

static void 
do_subst(const char *pat, const char *string, regex_t *re, const char *replace,
    regmatch_t *pm)
{
	int error;
	int flags = 0;
	const char *last_match = NULL;

	while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
		if (pm[0].rm_eo != 0) {
			if (string[pm[0].rm_eo-1] == '\n')
				flags = 0;
			else
				flags = REG_NOTBOL;
		}

		/* NULL length matches are special... We use the `vi-mode' 
		 * rule: don't allow a NULL-match at the last match
		 * position. 
		 */
		if (pm[0].rm_so == pm[0].rm_eo && 
		    string + pm[0].rm_so == last_match) {
			if (*string == '\0')
				return;
			addchar(*string);
			if (*string++ == '\n')
				flags = 0;
			else
				flags = REG_NOTBOL;
			continue;
		}
		last_match = string + pm[0].rm_so;
		addchars(string, pm[0].rm_so);
		add_replace(string, re, replace, pm);
		string += pm[0].rm_eo;
		buffer[current] = '\0';
	}
	while (*string)
		addchar(*string++);
	if (error != REG_NOMATCH)
		exit_regerror(error, pat, re);
	pbstr(string);
}

static void 
do_regexp(const char *pat, const char *string, regex_t *re, const char *replace,
    regmatch_t *pm)
{
	int error;

	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
	case 0: 
		add_replace(string, re, replace, pm);
		pbstr(getstring());
		break;
	case REG_NOMATCH:
		break;
	default:
		exit_regerror(error, pat, re);
	}
}

static void 
do_regexpindex(const char *pat, const char *string, regex_t *re, regmatch_t *pm)
{
	int error;

	switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
	case 0:
		pbunsigned(pm[0].rm_so);
		break;
	case REG_NOMATCH:
		pbnum(-1);
		break;
	default:
		exit_regerror(error, pat, re);
	}
}

/* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
 * says. So we twiddle with the regexp before passing it to regcomp.
 */
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 == '\\') {
			switch(p[1]) {
			case '(':
			case ')':
			case '|':
				addchar(p[1]);
				break;
			case 'w':
				addconstantstring("[_a-zA-Z0-9]");
				break;
			case 'W':
				addconstantstring("[^_a-zA-Z0-9]");
				break;
			case '<':
				addconstantstring("[[:<:]]");
				break;
			case '>':
				addconstantstring("[[:>:]]");
				break;
			default:
				addchars(p, 2);
				break;
			}
			p+=2;
			continue;
		}
		if (strchr("()|{}", *p) != NULL)
			addchar('\\');

		addchar(*p);
		p++;
	}
	return getstring();
}

static int
checkempty(const char *argv[], int argc)
{
	const char *s;
	size_t len;

	if (argc != 3 && argv[3][0] != '\0')
		return 0;

	if (argc == 3) {
		if (!quiet)
			warnx("Too few arguments to patsubst");
		if (fatal_warnings)
			exit(EXIT_FAILURE);
	}
			
	if (argv[4] && argc > 4) 
		len = strlen(argv[4]);
	else
		len = 0;
	for (s = argv[2]; *s != '\0'; s++) {
		addchars(argv[4], len);
		addchar(*s);
	}
	return 1;
}

/* patsubst(string, regexp, opt replacement) */
/* argv[2]: string
 * argv[3]: regexp
 * argv[4]: opt rep
 */
void
dopatsubst(const char *argv[], int argc)
{
	if (argc < 3) {
		if (!quiet)
			warnx("Too few arguments to patsubst");
		if (fatal_warnings)
			exit(EXIT_FAILURE);
		return;
	}
	/* special case: empty regexp */
	if (!checkempty(argv, argc)) {

		const char *pat;
		int error;
		regex_t re;
		regmatch_t *pmatch;
		int mode = REG_EXTENDED;
		size_t l = strlen(argv[3]);

		if (!mimic_gnu ||
		    (argv[3][0] == '^') || 
		    (l > 0 && argv[3][l-1] == '$'))
			mode |= REG_NEWLINE;

		pat = mimic_gnu ? twiddle(argv[3]) : argv[3];
		error = regcomp(&re, pat, mode);
		if (error != 0)
			exit_regerror(error, pat, &re);
		
		pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
		do_subst(pat, argv[2], &re, 
		    argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch);
		free(pmatch);
		regfree(&re);
	}
	pbstr(getstring());
}

void
doregexp(const char *argv[], int argc)
{
	int error;
	regex_t re;
	regmatch_t *pmatch;
	const char *pat;

	if (argc < 3) {
		if (!quiet)
			warnx("Too few arguments to regexp");
		if (fatal_warnings)
			exit(EXIT_FAILURE);
		return;
	}
	if (checkempty(argv, argc)) {
		return;
	}

	pat = mimic_gnu ? twiddle(argv[3]) : argv[3];
	error = regcomp(&re, pat, REG_EXTENDED);
	if (error != 0)
		exit_regerror(error, pat, &re);
	
	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
	if (argv[4] == NULL || argc == 4)
		do_regexpindex(pat, argv[2], &re, pmatch);
	else
		do_regexp(pat, argv[2], &re, argv[4], pmatch);
	free(pmatch);
	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;
	char temp[2];
	size_t 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 {
			char *eformat;
			width = strtol(format, &eformat, 10);
			format = eformat;
		}
		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 {
				char *eformat;
				extra = strtol(format, &eformat, 10);
				format = eformat;
			}
		} else {
			extra = SIZE_T_MAX;
		}
		if (pos >= argc)
			m4errx(1, "Format with too many format specifiers.");
		switch(*format) {
		case 's':
			thisarg = argv[pos++];
			break;
		case 'c':
			temp[0] = strtoul(argv[pos++], NULL, 10);
			temp[1] = 0;
			thisarg = temp;
			break;
		default:
			m4errx(1, "Unsupported format specification: %s.", 
			    argv[2]);
		}
		format++;
		l = strlen(thisarg);
		if (l > extra)
			l = extra;
		if (!left_padded) {
			while (l < (size_t)width--)
				addchar(' ');
		}
		addchars(thisarg, l);
		if (left_padded) {
			while (l < (size_t)width--)
				addchar(' ');
		}
	}
	pbstr(getstring());
}

void
doesyscmd(const char *cmd)
{
	int p[2];
	pid_t pid, cpid;
	const char *argv[4];
	int cc;
	int status;

	/* Follow gnu m4 documentation: first flush buffers. */
	fflush(NULL);

	argv[0] = "sh";
	argv[1] = "-c";
	argv[2] = cmd;
	argv[3] = NULL;

	/* Just set up standard output, share stderr and stdin with m4 */
	if (pipe(p) == -1)
		err(1, "bad pipe");
	switch(cpid = fork()) {
	case -1:
		err(1, "bad fork");
		/* NOTREACHED */
	case 0:
		(void) close(p[0]);
		(void) dup2(p[1], 1);
		(void) close(p[1]);
		execv(_PATH_BSHELL, __UNCONST(argv));
		exit(1);
	default:
		/* Read result in two stages, since m4's buffer is
		 * pushback-only. */
		(void) close(p[1]);
		do {
			char result[BUFSIZE];
			cc = read(p[0], result, sizeof result);
			if (cc > 0)
				addchars(result, cc);
		} while (cc > 0 || (cc == -1 && errno == EINTR));

		(void) close(p[0]);
		while ((pid = wait(&status)) != cpid && pid >= 0)
			continue;
		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);
}

#ifdef REAL_FREEZE
void
freeze_state(const char *fname)
{
	FILE *f;

	if ((f = fopen(fname, "wb")) == NULL)
		m4errx(EXIT_FAILURE, "Can't open output freeze file `%s' (%s)",
		    fname, strerror(errno));
	fprintf(f, "# This is a frozen state file generated by %s\nV1\n",
	    getprogname());
	fprintf(f, "Q%zu,%zu\n%s%s\n", strlen(lquote), strlen(rquote),
	    lquote, rquote);
	fprintf(f, "C%zu,%zu\n%s%s\n", strlen(scommt), strlen(ecommt),
	    scommt, ecommt);
	dump_state(f);
	/* XXX: diversions? */
	fprintf(f, "D-1,0\n");
	fprintf(f, "# End of frozen state file\n");
	fclose(f);
}

void
thaw_state(const char *fname)
{
	char *name = NULL;
	size_t nl, namelen = 0;
	char *defn = NULL;
	size_t dl, defnlen = 0;
	size_t lineno = 0;
	char line[1024], *ptr, type;
	FILE *f;

	if ((f = fopen(fname, "rb")) == NULL)
		m4errx(EXIT_FAILURE, "Can't open frozen file `%s' (%s)",
		    fname, strerror(errno));

#define GET() if (fgets(line, (int)sizeof(line), f) == NULL) goto out
#define GETSTR(s, l) if (fread(s, 1, l, f) != l) goto out; else s[l] = '\0'

	GET();	/* comment */
	GET();	/* version */
	if ((ptr = strrchr(line, '\n')) != NULL)
		*ptr = '\0';
	if (strcmp(line, "V1") != 0)
		m4errx(EXIT_FAILURE, "Bad frozen version `%s'", line);

	for (;;) {
		GET();
		lineno++;
		switch (*line) {
		case '\n':
			continue;
		case '#':
			free(name);
			free(defn);
			fclose(f);
			return;
		default:
			if (sscanf(line, "%c%zu,%zu\n", &type, &nl, &dl) != 3)
				m4errx(EXIT_FAILURE, "%s, %zu: Bad line `%s'",
				    fname, lineno, line);
			break;
		}

		switch (type) {
		case 'Q':
			if (nl >= sizeof(lquote) || dl >= sizeof(rquote))
				m4errx(EXIT_FAILURE, "%s, %zu: Quote too long",
				    fname, lineno);
			GETSTR(lquote, nl);
			GETSTR(rquote, dl);
			break;

		case 'C':
			if (nl >= sizeof(scommt) || dl >= sizeof(ecommt))
				m4errx(EXIT_FAILURE, "%s, %zu: Comment too long",
				    fname, lineno);
			GETSTR(scommt, nl);
			GETSTR(ecommt, dl);
			break;

		case 'T':
		case 'F':
			if (nl >= namelen)
				name = xrealloc(name, namelen = nl + 1,
					"name grow");
			if (dl >= defnlen)
				defn = xrealloc(defn, defnlen = dl + 1,
					"defn grow");
			GETSTR(name, nl);
			GETSTR(defn, dl);
			macro_pushdef(name, defn);
			break;

		case 'D':
			/* XXX: Not implemented */
			break;

		default:
			m4errx(EXIT_FAILURE, "%s, %zu: Unknown type %c",
			    fname, lineno,type);
		}
	}
out:
	m4errx(EXIT_FAILURE, "Unexpected end of file in `%s'", fname);
}
#endif
@


1.12
log
@m4 -g: { and } aren't supported in patsubst() or regexp()

When running in -g (GNU m4) emulation, patsubst() and regexp()
use the GNU m4 emacs-like regexes as implemented by gnulib,
which don't support {..} intervals.
When converting a GNU m4 regex to a POSIX ERE, escape raw { and }.

Autoconf relies on the GNU m4 regex behaviour.

See:
- https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Redefined-M4-Macros.html
- https://www.gnu.org/software/gnulib/manual/html_node/emacs-regular-expression-syntax.html

This fixes the tools/compat/configure regen.

PR toolchain/57431
@
text
@d1 1
a1 1
/* $NetBSD: gnum4.c,v 1.11 2021/12/07 20:24:07 rillig Exp $ */
d36 1
a36 1
__RCSID("$NetBSD: gnum4.c,v 1.11 2021/12/07 20:24:07 rillig Exp $");
d441 1
a441 1
		if (*p == '(' || *p == ')' || *p == '|' || *p == '{' || *p == '}')
@


1.11
log
@m4: fix typo in error message

To trigger the error message, compile m4 with -DREAL_FREEZE, then run
'./m4 -R /dev/null'.
@
text
@d1 1
a1 1
/* $NetBSD: gnum4.c,v 1.10 2016/01/16 16:59:18 christos Exp $ */
d36 1
a36 1
__RCSID("$NetBSD: gnum4.c,v 1.10 2016/01/16 16:59:18 christos Exp $");
d441 1
a441 1
		if (*p == '(' || *p == ')' || *p == '|')
@


1.11.2.1
log
@Pull up following revision(s) (requested by lukem in ticket #180):

	usr.bin/m4/gnum4.c: revision 1.12

m4 -g: { and } aren't supported in patsubst() or regexp()

When running in -g (GNU m4) emulation, patsubst() and regexp()
use the GNU m4 emacs-like regexes as implemented by gnulib,
which don't support {..} intervals.

When converting a GNU m4 regex to a POSIX ERE, escape raw { and }.
Autoconf relies on the GNU m4 regex behaviour.

See:
- https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Redefined-M4-Macros.html
- https://www.gnu.org/software/gnulib/manual/html_node/emacs-regular-expression-syntax.html

This fixes the tools/compat/configure regen.

PR toolchain/57431
@
text
@d1 1
a1 1
/* $NetBSD: gnum4.c,v 1.11 2021/12/07 20:24:07 rillig Exp $ */
d36 1
a36 1
__RCSID("$NetBSD: gnum4.c,v 1.11 2021/12/07 20:24:07 rillig Exp $");
d441 1
a441 1
		if (*p == '(' || *p == ')' || *p == '|' || *p == '{' || *p == '}')
@


1.11.2.2
log
@Pull up following revision(s) (requested by lukem in ticket #181):

	usr.bin/m4/gnum4.c: revision 1.13

use strchr
@
text
@d1 1
a1 1
/* $NetBSD: gnum4.c,v 1.11.2.1 2023/05/28 10:03:54 martin Exp $ */
d36 1
a36 1
__RCSID("$NetBSD: gnum4.c,v 1.11.2.1 2023/05/28 10:03:54 martin Exp $");
d441 1
a441 1
		if (strchr("()|{}", *p) != NULL)
@


1.10
log
@- don't eat the rest of the string when no match in patsubst
- include the pattern in the error printing of regex
- handle 3 argument and empty patterns the gnu way
- add support for freezing and thawing state (not working yet)
@
text
@d1 1
a1 1
/* $NetBSD: gnum4.c,v 1.9 2012/03/20 20:34:58 matt Exp $ */
d36 1
a36 1
__RCSID("$NetBSD: gnum4.c,v 1.9 2012/03/20 20:34:58 matt Exp $");
d815 1
a815 1
	m4errx(EXIT_FAILURE, "Unexprected end of file in `%s'", fname);
@


1.9
log
@Use C89 function definitions
@
text
@d1 1
a1 1
/* $NetBSD: gnum4.c,v 1.8 2011/09/06 18:16:01 joerg Exp $ */
d36 1
a36 1
__RCSID("$NetBSD: gnum4.c,v 1.8 2011/09/06 18:16:01 joerg Exp $");
d206 5
a210 4
static void exit_regerror(int, regex_t *) __dead;
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 *);
d254 1
a254 1
exit_regerror(int er, regex_t *re)
d263 1
a263 1
	m4errx(1, "regular expression error: %s.", errbuf);
d269 6
a274 2
	if (n > re->re_nsub)
		warnx("No subexpression %zu", n);
d321 2
a322 1
do_subst(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
d355 1
d357 2
d360 1
a360 1
		exit_regerror(error, re);
d365 2
a366 1
do_regexp(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
d378 1
a378 1
		exit_regerror(error, re);
d383 1
a383 1
do_regexpindex(const char *string, regex_t *re, regmatch_t *pm)
d395 1
a395 1
		exit_regerror(error, re);
d450 27
d485 5
a489 2
	if (argc <= 3) {
		warnx("Too few arguments to patsubst");
d493 3
a495 12
	if (argv[3][0] == '\0') {
		const char *s;
		size_t len;
		if (argv[4] && argc > 4) 
			len = strlen(argv[4]);
		else
			len = 0;
		for (s = argv[2]; *s != '\0'; s++) {
			addchars(argv[4], len);
			addchar(*s);
		}
	} else {
d507 2
a508 2
		error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3], 
		    mode);
d510 1
a510 1
			exit_regerror(error, &re);
d513 1
a513 1
		do_subst(argv[2], &re, 
d527 1
d529 5
a533 2
	if (argc <= 3) {
		warnx("Too few arguments to regexp");
d536 6
a541 2
	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3], 
	    REG_EXTENDED);
d543 1
a543 1
		exit_regerror(error, &re);
d547 1
a547 1
		do_regexpindex(argv[2], &re, pmatch);
d549 1
a549 1
		do_regexp(argv[2], &re, argv[4], pmatch);
d709 109
@


1.8
log
@Move usage and onintr to make them static. Add __dead as needed.
@
text
@d1 1
a1 1
/* $NetBSD: gnum4.c,v 1.7 2009/10/26 21:16:49 christos Exp $ */
d36 1
a36 1
__RCSID("$NetBSD: gnum4.c,v 1.7 2009/10/26 21:16:49 christos Exp $");
d109 1
a109 1
ensure_m4path()
d244 1
a244 1
getstring()
@


1.8.2.1
log
@sync with head
@
text
@d1 1
a1 1
/* $NetBSD$ */
d36 1
a36 1
__RCSID("$NetBSD$");
d109 1
a109 1
ensure_m4path(void)
d244 1
a244 1
getstring(void)
@


1.7
log
@fix the tools build
@
text
@d1 1
a1 1
/* $NetBSD: gnum4.c,v 1.6 2009/10/26 21:11:28 christos Exp $ */
d36 1
a36 1
__RCSID("$NetBSD: gnum4.c,v 1.6 2009/10/26 21:11:28 christos Exp $");
d206 1
a206 1
static void exit_regerror(int, regex_t *);
@


1.6
log
@resolve conflicts.
@
text
@d1 1
a1 1
/* $NetBSD: gnum4.c,v 1.39 2008/08/21 21:01:04 espie Exp $ */
d36 1
a36 1
__RCSID("$NetBSD$");
d57 3
@


1.5
log
@Add (unsigned char) cast to ctype functions
@
text
@d1 2
a2 2
/*	$NetBSD: gnum4.c,v 1.4 2004/06/20 22:20:15 jmc Exp $	*/
/* $OpenBSD: gnum4.c,v 1.15 2001/10/13 20:18:48 espie Exp $ */
d29 3
d35 2
a36 4

/* 
 * functions needed to support gnu-m4 extensions, including a fake freezing
 */
d42 1
a42 1
#include <errno.h>
d49 2
d71 3
a73 3
static struct path_entry *new_path_entry __P((const char *));
static void ensure_m4path __P((void));
static struct input_file *dopath __P((struct input_file *, const char *));
d76 1
a76 2
new_path_entry(dirname)
	const char *dirname;
d91 1
a91 2
addtoincludepath(dirname)
	const char *dirname;
d131 1
a131 3
dopath(i, filename)
	struct input_file *i;
	const char *filename;
d148 1
a148 3
fopen_trypath(i, filename)
	struct input_file *i;
	const char *filename;
d166 1
a166 3
doindir(argv, argc)
	const char *argv[];
	int argc;
d168 2
a169 1
	ndptr p;
d171 3
a173 3
	p = lookup(argv[2]);
	if (p == NULL)
		errx(1, "undefined macro %s", argv[2]);
d175 2
a176 1
	eval(argv+1, argc-1, p->type);
d180 1
a180 3
dobuiltin(argv, argc)
	const char *argv[];
	int argc;
d182 2
a183 1
	int n;
d185 3
a187 3
	n = builtin_type(argv[2]);
	if (n != -1)
		eval(argv+1, argc-1, n);
d189 1
a189 1
		errx(1, "unknown builtin %s", argv[2]);
d199 10
a208 10
static void addchars __P((const char *, size_t));
static void addchar __P((char));
static char *twiddle __P((const char *));
static char *getstring __P((void));
static void exit_regerror __P((int, regex_t *));
static void do_subst __P((const char *, regex_t *, const char *, regmatch_t *));
static void do_regexpindex __P((const char *, regex_t *, regmatch_t *));
static void do_regexp __P((const char *, regex_t *, const char *, regmatch_t *));
static void add_sub __P((int, const char *, regex_t *, regmatch_t *));
static void add_replace __P((const char *, regex_t *, const char *, regmatch_t *));
d212 1
a212 3
addchars(c, n)
	const char *c;
	size_t n;
d221 1
a221 3
		buffer = realloc(buffer, bufsize);
		if (buffer == NULL)
			errx(1, "out of memory");
d228 1
a228 2
addchar(c)
	char c;
d235 1
a235 3
		buffer = realloc(buffer, bufsize);
		if (buffer == NULL)
			errx(1, "out of memory");
d250 1
a250 3
exit_regerror(er, re)
	int er;
	regex_t *re;
d256 2
a257 1
	errbuf = xalloc(errlen);
d259 1
a259 1
	errx(1, "regular expression error: %s", errbuf);
d263 1
a263 5
add_sub(n, string, re, pm)
	int n;
	const char *string;
	regex_t *re;
	regmatch_t *pm;
d266 1
a266 1
		warnx("No subexpression %d", n);
d280 1
a280 5
add_replace(string, re, replace, pm)
	const char *string;
	regex_t *re;
	const char *replace;
	regmatch_t *pm;
d313 1
a313 5
do_subst(string, re, replace, pm)
	const char *string;
	regex_t *re;
	const char *replace;
	regmatch_t *pm;
d353 1
a353 5
do_regexp(string, re, replace, pm)
	const char *string;
	regex_t *re;
	const char *replace;
	regmatch_t *pm;
d370 1
a370 4
do_regexpindex(string, re, pm)
	const char *string;
	regex_t *re;
	regmatch_t *pm;
d390 1
a390 2
twiddle(p)
	const char *p;
d392 8
d443 1
a443 3
dopatsubst(argv, argc)
	const char *argv[];
	int argc;
a444 4
	int error;
	regex_t re;
	regmatch_t *pmatch;

d449 35
a483 8
	error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3], 
	    REG_NEWLINE | REG_EXTENDED);
	if (error != 0)
		exit_regerror(error, &re);
	
	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
	do_subst(argv[2], &re, 
	    argc != 4 && argv[4] != NULL ? argv[4] : "", pmatch);
a484 2
	free(pmatch);
	regfree(&re);
d488 1
a488 3
doregexp(argv, argc)
	const char *argv[];
	int argc;
d503 1
a503 1
	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
d513 94
a606 2
doesyscmd(cmd)
	const char *cmd;
d610 1
a610 1
	char *argv[4];
d619 1
a619 1
	argv[2] = (char *)cmd;
d633 1
a633 1
		execv(_PATH_BSHELL, argv);
d652 15
@


1.4
log
@Completely rework how tools/compat is done. Purge all uses/references to
_NETBSD_SOURCE as this makes cross building from older/newer versions of
NetBSD harder, not easier (and also makes the resulting tools 'different')

Wrap all required code with the inclusion of nbtool_config.h, attempt to
only use POSIX code in all places (or when reasonable test w. configure and
provide definitions: ala u_int, etc).

Reviewed by lukem. Tested on FreeBSD 4.9, Redhat Linux ES3, NetBSD 1.6.2 x86
NetBSD current (x86 and amd64) and Solaris 9.

Fixes PR's: PR#17762 PR#25944
@
text
@d1 1
a1 1
/*	$NetBSD: gnum4.c,v 1.3 2004/04/23 02:58:29 simonb Exp $	*/
d323 1
a323 1
			if (isdigit(p[1])) {
@


1.3
log
@s/the the/the/ (only in sources that aren't regularly imported from
elsewhere).
@
text
@d1 1
a1 1
/*	$NetBSD: gnum4.c,v 1.2 2002/01/21 21:49:57 tv Exp $	*/
d29 4
@


1.2
log
@Make compilable from src/tools/m4 on non-NetBSD hosts.
@
text
@d1 1
a1 1
/*	$NetBSD: gnum4.c,v 1.1 2001/11/14 06:16:08 tv Exp $	*/
d53 1
a53 1
 * First search in the the current directory.
@


1.2.4.1
log
@Pull up revision 1.4 (requested by jmc in ticket #527):
Completely rework how tools/compat is done. Purge all uses/references to
_NETBSD_SOURCE as this makes cross building from older/newer versions of
NetBSD harder, not easier (and also makes the resulting tools 'different')
Wrap all required code with the inclusion of nbtool_config.h, attempt to
only use POSIX code in all places (or when reasonable test w. configure and
provide definitions: ala u_int, etc).
Reviewed by lukem. Tested on FreeBSD 4.9, Redhat Linux ES3, NetBSD 1.6.2 x86
NetBSD current (x86 and amd64) and Solaris 9.
Fixes PR's: PR#17762 PR#25944
@
text
@d1 1
a1 1
/*	$NetBSD$	*/
a28 4
#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif

@


1.1
log
@Pull in various changes from OpenBSD, most from Marc Espie, including:
* Provide some GNUisms as extensions.
* Provide dynamically growable string space.
* Make define(defn(foo)) work correctly for builtins.
(The current version is supposed to be capable of satisfying autoconf.)

All still relevant NetBSD changes have been preserved in this version, and
formatting and style fixes have been applied in various places.

Thanks to Masao Uebayashi <uebayasi@@soum.co.jp> for pointing this out.
@
text
@d1 1
a1 1
/*	$NetBSD$	*/
a36 1
#include <err.h>
@


1.1.1.1
log
@Import new m4 from OpenBSD.
@
text
@d1 2
a2 1
/* $OpenBSD: gnum4.c,v 1.39 2008/08/21 21:01:04 espie Exp $ */
d38 1
a44 2
#include <errno.h>
#include <unistd.h>
d54 1
a54 1
 * First search in the current directory.
d65 3
a67 3
static struct path_entry *new_path_entry(const char *);
static void ensure_m4path(void);
static struct input_file *dopath(struct input_file *, const char *);
d70 2
a71 1
new_path_entry(const char *dirname)
d86 2
a87 1
addtoincludepath(const char *dirname)
d127 3
a129 1
dopath(struct input_file *i, const char *filename)
d146 3
a148 1
fopen_trypath(struct input_file *i, const char *filename)
d166 3
a168 1
doindir(const char *argv[], int argc)
d170 1
a170 2
	ndptr n;
	struct macro_definition *p;
d172 3
a174 3
	n = lookup(argv[2]);
	if (n == NULL || (p = macro_getdef(n)) == NULL)
		m4errx(1, "indir: undefined macro %s.", argv[2]);
d176 1
a176 2
	
	eval(argv+1, argc-1, p->type, is_traced(n));
d180 3
a182 1
dobuiltin(const char *argv[], int argc)
d184 1
a184 2
	ndptr p;

d186 3
a188 3
	p = macro_getbuiltin(argv[2]);
	if (p != NULL)
		eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p));
d190 1
a190 1
		m4errx(1, "unknown builtin %s.", argv[2]);
d200 10
a209 10
static void addchars(const char *, size_t);
static void addchar(int);
static char *twiddle(const char *);
static char *getstring(void);
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(int, const char *, regex_t *, regmatch_t *);
static void add_replace(const char *, regex_t *, const char *, regmatch_t *);
d213 3
a215 1
addchars(const char *c, size_t n)
d224 3
a226 1
		buffer = xrealloc(buffer, bufsize, NULL);
d233 2
a234 1
addchar(int c)
d241 3
a243 1
		buffer = xrealloc(buffer, bufsize, NULL);
d258 3
a260 1
exit_regerror(int er, regex_t *re)
d266 1
a266 2
	errbuf = xalloc(errlen, 
	    "malloc in regerror: %lu", (unsigned long)errlen);
d268 1
a268 1
	m4errx(1, "regular expression error: %s.", errbuf);
d272 5
a276 1
add_sub(int n, const char *string, regex_t *re, regmatch_t *pm)
d293 5
a297 1
add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
d330 5
a334 1
do_subst(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
d374 5
a378 1
do_regexp(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
d395 4
a398 1
do_regexpindex(const char *string, regex_t *re, regmatch_t *pm)
d418 2
a419 1
twiddle(const char *p)
a420 8
	/* + at start of regexp is a normal character for Gnu m4 */
	if (*p == '^') {
		addchar(*p);
		p++;
	}
	if (*p == '+') {
		addchar('\\');
	}
d464 3
a466 1
dopatsubst(const char *argv[], int argc)
d468 4
d476 8
a483 35
	/* special case: empty regexp */
	if (argv[3][0] == '\0') {
		const char *s;
		size_t len;
		if (argv[4] && argc > 4) 
			len = strlen(argv[4]);
		else
			len = 0;
		for (s = argv[2]; *s != '\0'; s++) {
			addchars(argv[4], len);
			addchar(*s);
		}
	} else {
		int error;
		regex_t re;
		regmatch_t *pmatch;
		int mode = REG_EXTENDED;
		size_t l = strlen(argv[3]);

		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);
	}
d485 2
d490 3
a492 1
doregexp(const char *argv[], int argc)
d507 1
a507 1
	pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL);
d517 2
a518 90
doformat(const char *argv[], int argc)
{
	const char *format = argv[2];
	int pos = 3;
	int left_padded;
	long width;
	size_t l;
	const char *thisarg;
	char temp[2];
	long extra;

	while (*format != 0) {
		if (*format != '%') {
			addchar(*format++);
			continue;
		}

		format++;
		if (*format == '%') {
			addchar(*format++);
			continue;
		}
		if (*format == 0) {
			addchar('%');
			break;
		}

		if (*format == '*') {
			format++;
			if (pos >= argc)
				m4errx(1, 
				    "Format with too many format specifiers.");
			width = strtol(argv[pos++], NULL, 10);
		} else {
			width = strtol(format, (char **)&format, 10);
		}
		if (width < 0) {
			left_padded = 1;
			width = -width;
		} else {
			left_padded = 0;
		}
		if (*format == '.') {
			format++;
			if (*format == '*') {
				format++;
				if (pos >= argc)
					m4errx(1, 
					    "Format with too many format specifiers.");
				extra = strtol(argv[pos++], NULL, 10);
			} else {
				extra = strtol(format, (char **)&format, 10);
			}
		} else {
			extra = LONG_MAX;
		}
		if (pos >= argc)
			m4errx(1, "Format with too many format specifiers.");
		switch(*format) {
		case 's':
			thisarg = argv[pos++];
			break;
		case 'c':
			temp[0] = strtoul(argv[pos++], NULL, 10);
			temp[1] = 0;
			thisarg = temp;
			break;
		default:
			m4errx(1, "Unsupported format specification: %s.", 
			    argv[2]);
		}
		format++;
		l = strlen(thisarg);
		if (l > extra)
			l = extra;
		if (!left_padded) {
			while (l < width--)
				addchar(' ');
		}
		addchars(thisarg, l);
		if (left_padded) {
			while (l < width--)
				addchar(' ');
		}
	}
	pbstr(getstring());
}

void
doesyscmd(const char *cmd)
a563 15

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);
}
@


