m4: Sync with FreeBSD.
[dragonfly.git] / usr.bin / m4 / gnum4.c
index b337151..ab8ef00 100644 (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
  * 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"
@@ -53,13 +53,13 @@ int mimic_gnu = 0;
 
 /*
  * 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;
@@ -74,10 +74,10 @@ new_path_entry(const char *dirname)
        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;
@@ -93,9 +93,9 @@ addtoincludepath(const char *dirname)
        if (last) {
                last->next = n;
                last = n;
-       }
-       else
+       } else {
                last = first = n;
+       }
 }
 
 static void
@@ -114,16 +114,15 @@ ensure_m4path(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];
@@ -161,25 +160,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]);
 }
 
 
@@ -197,9 +199,9 @@ 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)
+#define        addconstantstring(s) addchars((s), sizeof(s)-1)
 
 static void
 addchars(const char *c, size_t n)
@@ -211,25 +213,21 @@ 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;
 }
@@ -246,27 +244,25 @@ 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 &&
-           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
@@ -301,7 +297,7 @@ add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm
                                continue;
                        }
                }
-               addchar(*p);
+               addchar(*p);
        }
 }
 
@@ -312,9 +308,9 @@ do_subst(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
        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;
@@ -350,7 +346,7 @@ do_regexp(const char *string, regex_t *re, const char *replace, regmatch_t *pm)
 {
        int error;
 
-       switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) {
+       switch(error = regexec(re, string, re->re_nsub + 1, pm, 0)) {
        case 0:
                add_replace(string, re, replace, pm);
                pbstr(getstring());
@@ -367,7 +363,7 @@ do_regexpindex(const char *string, regex_t *re, regmatch_t *pm)
 {
        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;
@@ -385,6 +381,13 @@ 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 == '\\') {
@@ -410,7 +413,7 @@ twiddle(const char *p)
                                addchars(p, 2);
                                break;
                        }
-                       p+=2;
+                       p += 2;
                        continue;
                }
                if (*p == '(' || *p == ')' || *p == '|')
@@ -430,25 +433,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);
-
-       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
@@ -462,13 +486,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,16 +508,111 @@ doregexp(const char *argv[], int argc)
 }
 
 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");
@@ -495,15 +621,15 @@ doesyscmd(const char *cmd)
                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);
@@ -511,9 +637,24 @@ doesyscmd(const char *cmd)
                                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);
+}