-/* $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
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: src/usr.bin/m4/gnum4.c,v 1.9 2004/05/18 15:53:58 stefanf Exp $
+ * $FreeBSD: src/usr.bin/m4/gnum4.c,v 1.15 2012/11/17 01:54:24 svnexp Exp $
*/
-/*
+/*
* functions needed to support gnu-m4 extensions, including a fake freezing
*/
#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"
/*
* Support for include path search
- * First search in the the current directory.
+ * First search in the current directory.
* If not found, and the path is not absolute, include path kicks in.
* First, -I options, in the order found on the command line.
* Then M4PATH env variable
*/
-struct path_entry {
+static struct path_entry {
char *name;
struct path_entry *next;
} *first, *last;
struct path_entry *n;
n = malloc(sizeof(struct path_entry));
- if (!n)
+ if (n == NULL)
errx(1, "out of memory");
n->name = strdup(dirname);
- if (!n->name)
+ if (n->name == NULL)
errx(1, "out of memory");
n->next = NULL;
return n;
if (last) {
last->next = n;
last = n;
- }
- else
+ } else {
last = first = n;
+ }
}
static void
return;
/* for portability: getenv result is read-only */
envpath = strdup(envpath);
- if (!envpath)
+ if (envpath == NULL)
errx(1, "out of memory");
for (sweep = envpath;
(path = strsep(&sweep, ":")) != NULL;)
- addtoincludepath(path);
+ addtoincludepath(path);
free(envpath);
}
-static
-struct input_file *
+static struct input_file *
dopath(struct input_file *i, const char *filename)
{
char path[MAXPATHLEN];
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]);
}
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)
+#define addconstantstring(s) addchars((s), sizeof(s)-1)
static void
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);
+ memcpy(buffer + current, c, n);
current += n;
}
static void
addchar(int c)
{
- if (current +1 > bufsize) {
+ if (current + 1 > bufsize) {
if (bufsize == 0)
bufsize = 1024;
else
bufsize *= 2;
- buffer = realloc(buffer, bufsize);
- if (buffer == NULL)
- errx(1, "out of memory");
+ buffer = xrealloc(buffer, bufsize, NULL);
}
buffer[current++] = c;
}
static 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 &&
- pm[n].rm_eo != -1) {
- addchars(string + pm[n].rm_so,
- pm[n].rm_eo - pm[n].rm_so);
- }
+ 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
continue;
}
}
- addchar(*p);
+ addchar(*p);
}
}
int flags = 0;
const char *last_match = NULL;
- while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) {
+ 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')
+ if (string[pm[0].rm_eo - 1] == '\n')
flags = 0;
else
flags = REG_NOTBOL;
{
int error;
- switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
+ switch(error = regexec(re, string, re->re_nsub + 1, pm, 0)) {
case 0:
add_replace(string, re, replace, pm);
pbstr(getstring());
{
int error;
- switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
+ switch(error = regexec(re, string, re->re_nsub + 1, pm, 0)) {
case 0:
pbunsigned(pm[0].rm_so);
break;
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 == '\\') {
addchars(p, 2);
break;
}
- p+=2;
+ p += 2;
continue;
}
if (*p == '(' || *p == ')' || *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);
-
- pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1));
- do_subst(argv[2], &re,
- argc != 4 && argv[4] != NULL ? argv[4] : "", pmatch);
+ /* 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]);
+
+ 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
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);
}
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");
err(1, "bad fork");
/* NOTREACHED */
case 0:
- (void) close(p[0]);
- (void) dup2(p[1], 1);
- (void) close(p[1]);
- execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
+ close(p[0]);
+ dup2(p[1], 1);
+ close(p[1]);
+ execv(_PATH_BSHELL, argv);
exit(1);
default:
/* Read result in two stages, since m4's buffer is
* pushback-only. */
- (void) close(p[1]);
+ close(p[1]);
do {
char result[BUFSIZE];
cc = read(p[0], result, sizeof result);
addchars(result, cc);
} while (cc > 0 || (cc == -1 && errno == EINTR));
- (void) close(p[0]);
+ 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);
+ fclose(f);
+}