1 /* $NetBSD: gnum4.c,v 1.7 2009/10/26 21:16:49 christos Exp $ */
2 /* $OpenBSD: gnum4.c,v 1.39 2008/08/21 21:01:04 espie Exp $ */
5 * Copyright (c) 1999 Marc Espie
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * functions needed to support gnu-m4 extensions, including a fake freezing
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
35 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: gnum4.c,v 1.7 2009/10/26 21:16:49 christos Exp $");
38 #include <sys/param.h>
39 #include <sys/types.h>
58 #define SIZE_T_MAX (size_t)~0ull
62 * Support for include path search
63 * First search in the current directory.
64 * If not found, and the path is not absolute, include path kicks in.
65 * First, -I options, in the order found on the command line.
66 * Then M4PATH env variable
71 struct path_entry
*next
;
74 static struct path_entry
*new_path_entry(const char *);
75 static void ensure_m4path(void);
76 static struct input_file
*dopath(struct input_file
*, const char *);
78 static struct path_entry
*
79 new_path_entry(const char *dirname
)
83 n
= malloc(sizeof(struct path_entry
));
85 errx(1, "out of memory");
86 n
->name
= strdup(dirname
);
88 errx(1, "out of memory");
94 addtoincludepath(const char *dirname
)
98 n
= new_path_entry(dirname
);
111 static int envpathdone
= 0;
119 envpath
= getenv("M4PATH");
122 /* for portability: getenv result is read-only */
123 envpath
= strdup(envpath
);
125 errx(1, "out of memory");
126 for (sweep
= envpath
;
127 (path
= strsep(&sweep
, ":")) != NULL
;)
128 addtoincludepath(path
);
134 dopath(struct input_file
*i
, const char *filename
)
136 char path
[MAXPATHLEN
];
137 struct path_entry
*pe
;
140 for (pe
= first
; pe
; pe
= pe
->next
) {
141 snprintf(path
, sizeof(path
), "%s/%s", pe
->name
, filename
);
142 if ((f
= fopen(path
, "r")) != 0) {
143 set_input(i
, f
, path
);
151 fopen_trypath(struct input_file
*i
, const char *filename
)
155 f
= fopen(filename
, "r");
157 set_input(i
, f
, filename
);
160 if (filename
[0] == '/')
165 return dopath(i
, filename
);
169 doindir(const char *argv
[], int argc
)
172 struct macro_definition
*p
;
175 if (n
== NULL
|| (p
= macro_getdef(n
)) == NULL
)
176 m4errx(1, "indir: undefined macro %s.", argv
[2]);
179 eval(argv
+1, argc
-1, p
->type
, is_traced(n
));
183 dobuiltin(const char *argv
[], int argc
)
188 p
= macro_getbuiltin(argv
[2]);
190 eval(argv
+1, argc
-1, macro_builtin_type(p
), is_traced(p
));
192 m4errx(1, "unknown builtin %s.", argv
[2]);
196 /* We need some temporary buffer space, as pb pushes BACK and substitution
197 * proceeds forward... */
199 static size_t bufsize
= 0;
200 static size_t current
= 0;
202 static void addchars(const char *, size_t);
203 static void addchar(int);
204 static char *twiddle(const char *);
205 static char *getstring(void);
206 static void exit_regerror(int, regex_t
*);
207 static void do_subst(const char *, regex_t
*, const char *, regmatch_t
*);
208 static void do_regexpindex(const char *, regex_t
*, regmatch_t
*);
209 static void do_regexp(const char *, regex_t
*, const char *, regmatch_t
*);
210 static void add_sub(size_t, const char *, regex_t
*, regmatch_t
*);
211 static void add_replace(const char *, regex_t
*, const char *, regmatch_t
*);
212 #define addconstantstring(s) addchars((s), sizeof(s)-1)
215 addchars(const char *c
, size_t n
)
219 while (current
+ n
> bufsize
) {
224 buffer
= xrealloc(buffer
, bufsize
, NULL
);
226 memcpy(buffer
+current
, c
, n
);
233 if (current
+1 > bufsize
) {
238 buffer
= xrealloc(buffer
, bufsize
, NULL
);
240 buffer
[current
++] = c
;
253 exit_regerror(int er
, regex_t
*re
)
258 errlen
= regerror(er
, re
, NULL
, 0);
259 errbuf
= xalloc(errlen
,
260 "malloc in regerror: %lu", (unsigned long)errlen
);
261 regerror(er
, re
, errbuf
, errlen
);
262 m4errx(1, "regular expression error: %s.", errbuf
);
266 add_sub(size_t n
, const char *string
, regex_t
*re
, regmatch_t
*pm
)
269 warnx("No subexpression %zu", n
);
270 /* Subexpressions that did not match are
272 else if (pm
[n
].rm_so
!= -1 &&
274 addchars(string
+ pm
[n
].rm_so
,
275 pm
[n
].rm_eo
- pm
[n
].rm_so
);
279 /* Add replacement string to the output buffer, recognizing special
280 * constructs and replacing them with substrings of the original string.
283 add_replace(const char *string
, regex_t
*re
, const char *replace
, regmatch_t
*pm
)
287 for (p
= replace
; *p
!= '\0'; p
++) {
288 if (*p
== '&' && !mimic_gnu
) {
289 add_sub(0, string
, re
, pm
);
300 add_sub(0, string
, re
, pm
);
306 if (isdigit((unsigned char)p
[1])) {
307 add_sub(*(++p
) - '0', string
, re
, pm
);
316 do_subst(const char *string
, regex_t
*re
, const char *replace
, regmatch_t
*pm
)
320 const char *last_match
= NULL
;
322 while ((error
= regexec(re
, string
, re
->re_nsub
+1, pm
, flags
)) == 0) {
323 if (pm
[0].rm_eo
!= 0) {
324 if (string
[pm
[0].rm_eo
-1] == '\n')
330 /* NULL length matches are special... We use the `vi-mode'
331 * rule: don't allow a NULL-match at the last match
334 if (pm
[0].rm_so
== pm
[0].rm_eo
&&
335 string
+ pm
[0].rm_so
== last_match
) {
339 if (*string
++ == '\n')
345 last_match
= string
+ pm
[0].rm_so
;
346 addchars(string
, pm
[0].rm_so
);
347 add_replace(string
, re
, replace
, pm
);
348 string
+= pm
[0].rm_eo
;
350 if (error
!= REG_NOMATCH
)
351 exit_regerror(error
, re
);
356 do_regexp(const char *string
, regex_t
*re
, const char *replace
, regmatch_t
*pm
)
360 switch(error
= regexec(re
, string
, re
->re_nsub
+1, pm
, 0)) {
362 add_replace(string
, re
, replace
, pm
);
368 exit_regerror(error
, re
);
373 do_regexpindex(const char *string
, regex_t
*re
, regmatch_t
*pm
)
377 switch(error
= regexec(re
, string
, re
->re_nsub
+1, pm
, 0)) {
379 pbunsigned(pm
[0].rm_so
);
385 exit_regerror(error
, re
);
389 /* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
390 * says. So we twiddle with the regexp before passing it to regcomp.
393 twiddle(const char *p
)
395 /* + at start of regexp is a normal character for Gnu m4 */
403 /* This could use strcspn for speed... */
413 addconstantstring("[_a-zA-Z0-9]");
416 addconstantstring("[^_a-zA-Z0-9]");
419 addconstantstring("[[:<:]]");
422 addconstantstring("[[:>:]]");
431 if (*p
== '(' || *p
== ')' || *p
== '|')
440 /* patsubst(string, regexp, opt replacement) */
446 dopatsubst(const char *argv
[], int argc
)
449 warnx("Too few arguments to patsubst");
452 /* special case: empty regexp */
453 if (argv
[3][0] == '\0') {
456 if (argv
[4] && argc
> 4)
457 len
= strlen(argv
[4]);
460 for (s
= argv
[2]; *s
!= '\0'; s
++) {
461 addchars(argv
[4], len
);
468 int mode
= REG_EXTENDED
;
469 size_t l
= strlen(argv
[3]);
472 (argv
[3][0] == '^') ||
473 (l
> 0 && argv
[3][l
-1] == '$'))
476 error
= regcomp(&re
, mimic_gnu
? twiddle(argv
[3]) : argv
[3],
479 exit_regerror(error
, &re
);
481 pmatch
= xalloc(sizeof(regmatch_t
) * (re
.re_nsub
+1), NULL
);
482 do_subst(argv
[2], &re
,
483 argc
> 4 && argv
[4] != NULL
? argv
[4] : "", pmatch
);
491 doregexp(const char *argv
[], int argc
)
498 warnx("Too few arguments to regexp");
501 error
= regcomp(&re
, mimic_gnu
? twiddle(argv
[3]) : argv
[3],
504 exit_regerror(error
, &re
);
506 pmatch
= xalloc(sizeof(regmatch_t
) * (re
.re_nsub
+1), NULL
);
507 if (argv
[4] == NULL
|| argc
== 4)
508 do_regexpindex(argv
[2], &re
, pmatch
);
510 do_regexp(argv
[2], &re
, argv
[4], pmatch
);
516 doformat(const char *argv
[], int argc
)
518 const char *format
= argv
[2];
527 while (*format
!= 0) {
528 if (*format
!= '%') {
534 if (*format
== '%') {
543 if (*format
== '*') {
547 "Format with too many format specifiers.");
548 width
= strtol(argv
[pos
++], NULL
, 10);
551 width
= strtol(format
, &eformat
, 10);
560 if (*format
== '.') {
562 if (*format
== '*') {
566 "Format with too many format specifiers.");
567 extra
= strtol(argv
[pos
++], NULL
, 10);
570 extra
= strtol(format
, &eformat
, 10);
577 m4errx(1, "Format with too many format specifiers.");
580 thisarg
= argv
[pos
++];
583 temp
[0] = strtoul(argv
[pos
++], NULL
, 10);
588 m4errx(1, "Unsupported format specification: %s.",
596 while (l
< (size_t)width
--)
599 addchars(thisarg
, l
);
601 while (l
< (size_t)width
--)
609 doesyscmd(const char *cmd
)
617 /* Follow gnu m4 documentation: first flush buffers. */
625 /* Just set up standard output, share stderr and stdin with m4 */
628 switch(cpid
= fork()) {
634 (void) dup2(p
[1], 1);
636 execv(_PATH_BSHELL
, __UNCONST(argv
));
639 /* Read result in two stages, since m4's buffer is
643 char result
[BUFSIZE
];
644 cc
= read(p
[0], result
, sizeof result
);
646 addchars(result
, cc
);
647 } while (cc
> 0 || (cc
== -1 && errno
== EINTR
));
650 while ((pid
= wait(&status
)) != cpid
&& pid
>= 0)
657 getdivfile(const char *name
)
662 f
= fopen(name
, "r");
666 while ((c
= getc(f
))!= EOF
)