1 /* $OpenBSD: gnum4.c,v 1.18 2002/04/26 16:15:16 espie Exp $ */
4 * Copyright (c) 1999 Marc Espie
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * $FreeBSD: src/usr.bin/m4/gnum4.c,v 1.9 2004/05/18 15:53:58 stefanf Exp $
28 * $DragonFly: src/usr.bin/m4/gnum4.c,v 1.3 2006/12/27 21:29:02 pavalos Exp $
32 * functions needed to support gnu-m4 extensions, including a fake freezing
35 #include <sys/param.h>
36 #include <sys/types.h>
56 * Support for include path search
57 * First search in the the current directory.
58 * If not found, and the path is not absolute, include path kicks in.
59 * First, -I options, in the order found on the command line.
60 * Then M4PATH env variable
65 struct path_entry
*next
;
68 static struct path_entry
*new_path_entry(const char *);
69 static void ensure_m4path(void);
70 static struct input_file
*dopath(struct input_file
*, const char *);
72 static struct path_entry
*
73 new_path_entry(const char *dirname
)
77 n
= malloc(sizeof(struct path_entry
));
79 errx(1, "out of memory");
80 n
->name
= strdup(dirname
);
82 errx(1, "out of memory");
88 addtoincludepath(const char *dirname
)
92 n
= new_path_entry(dirname
);
105 static int envpathdone
= 0;
113 envpath
= getenv("M4PATH");
116 /* for portability: getenv result is read-only */
117 envpath
= strdup(envpath
);
119 errx(1, "out of memory");
120 for (sweep
= envpath
;
121 (path
= strsep(&sweep
, ":")) != NULL
;)
122 addtoincludepath(path
);
128 dopath(struct input_file
*i
, const char *filename
)
130 char path
[MAXPATHLEN
];
131 struct path_entry
*pe
;
134 for (pe
= first
; pe
; pe
= pe
->next
) {
135 snprintf(path
, sizeof(path
), "%s/%s", pe
->name
, filename
);
136 if ((f
= fopen(path
, "r")) != 0) {
137 set_input(i
, f
, path
);
145 fopen_trypath(struct input_file
*i
, const char *filename
)
149 f
= fopen(filename
, "r");
151 set_input(i
, f
, filename
);
154 if (filename
[0] == '/')
159 return dopath(i
, filename
);
163 doindir(const char *argv
[], int argc
)
169 errx(1, "undefined macro %s", argv
[2]);
171 eval(argv
+1, argc
-1, p
->type
);
175 dobuiltin(const char *argv
[], int argc
)
179 n
= builtin_type(argv
[2]);
181 eval(argv
+1, argc
-1, n
);
183 errx(1, "unknown builtin %s", argv
[2]);
187 /* We need some temporary buffer space, as pb pushes BACK and substitution
188 * proceeds forward... */
190 static size_t bufsize
= 0;
191 static size_t current
= 0;
193 static void addchars(const char *, size_t);
194 static void addchar(int);
195 static char *twiddle(const char *);
196 static char *getstring(void);
197 static void exit_regerror(int, regex_t
*);
198 static void do_subst(const char *, regex_t
*, const char *, regmatch_t
*);
199 static void do_regexpindex(const char *, regex_t
*, regmatch_t
*);
200 static void do_regexp(const char *, regex_t
*, const char *, regmatch_t
*);
201 static void add_sub(size_t, const char *, regex_t
*, regmatch_t
*);
202 static void add_replace(const char *, regex_t
*, const char *, regmatch_t
*);
203 #define addconstantstring(s) addchars((s), sizeof(s)-1)
206 addchars(const char *c
, size_t n
)
210 while (current
+ n
> bufsize
) {
215 buffer
= realloc(buffer
, bufsize
);
217 errx(1, "out of memory");
219 memcpy(buffer
+current
, c
, n
);
226 if (current
+1 > bufsize
) {
231 buffer
= realloc(buffer
, bufsize
);
233 errx(1, "out of memory");
235 buffer
[current
++] = c
;
248 exit_regerror(int er
, regex_t
*re
)
253 errlen
= regerror(er
, re
, NULL
, 0);
254 errbuf
= xalloc(errlen
);
255 regerror(er
, re
, errbuf
, errlen
);
256 errx(1, "regular expression error: %s", errbuf
);
260 add_sub(size_t n
, const char *string
, regex_t
*re
, regmatch_t
*pm
)
263 warnx("No subexpression %zu", n
);
264 /* Subexpressions that did not match are
266 else if (pm
[n
].rm_so
!= -1 &&
268 addchars(string
+ pm
[n
].rm_so
,
269 pm
[n
].rm_eo
- pm
[n
].rm_so
);
273 /* Add replacement string to the output buffer, recognizing special
274 * constructs and replacing them with substrings of the original string.
277 add_replace(const char *string
, regex_t
*re
, const char *replace
, regmatch_t
*pm
)
281 for (p
= replace
; *p
!= '\0'; p
++) {
282 if (*p
== '&' && !mimic_gnu
) {
283 add_sub(0, string
, re
, pm
);
294 add_sub(0, string
, re
, pm
);
301 add_sub(*(++p
) - '0', string
, re
, pm
);
310 do_subst(const char *string
, regex_t
*re
, const char *replace
, regmatch_t
*pm
)
314 const char *last_match
= NULL
;
316 while ((error
= regexec(re
, string
, re
->re_nsub
+1, pm
, flags
)) == 0) {
317 if (pm
[0].rm_eo
!= 0) {
318 if (string
[pm
[0].rm_eo
-1] == '\n')
324 /* NULL length matches are special... We use the `vi-mode'
325 * rule: don't allow a NULL-match at the last match
328 if (pm
[0].rm_so
== pm
[0].rm_eo
&&
329 string
+ pm
[0].rm_so
== last_match
) {
333 if (*string
++ == '\n')
339 last_match
= string
+ pm
[0].rm_so
;
340 addchars(string
, pm
[0].rm_so
);
341 add_replace(string
, re
, replace
, pm
);
342 string
+= pm
[0].rm_eo
;
344 if (error
!= REG_NOMATCH
)
345 exit_regerror(error
, re
);
350 do_regexp(const char *string
, regex_t
*re
, const char *replace
, regmatch_t
*pm
)
354 switch(error
= regexec(re
, string
, re
->re_nsub
+1, pm
, 0)) {
356 add_replace(string
, re
, replace
, pm
);
362 exit_regerror(error
, re
);
367 do_regexpindex(const char *string
, regex_t
*re
, regmatch_t
*pm
)
371 switch(error
= regexec(re
, string
, re
->re_nsub
+1, pm
, 0)) {
373 pbunsigned(pm
[0].rm_so
);
379 exit_regerror(error
, re
);
383 /* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
384 * says. So we twiddle with the regexp before passing it to regcomp.
387 twiddle(const char *p
)
389 /* This could use strcspn for speed... */
399 addconstantstring("[_a-zA-Z0-9]");
402 addconstantstring("[^_a-zA-Z0-9]");
405 addconstantstring("[[:<:]]");
408 addconstantstring("[[:>:]]");
417 if (*p
== '(' || *p
== ')' || *p
== '|')
426 /* patsubst(string, regexp, opt replacement) */
432 dopatsubst(const char *argv
[], int argc
)
439 warnx("Too few arguments to patsubst");
442 error
= regcomp(&re
, mimic_gnu
? twiddle(argv
[3]) : argv
[3],
443 REG_NEWLINE
| REG_EXTENDED
);
445 exit_regerror(error
, &re
);
447 pmatch
= xalloc(sizeof(regmatch_t
) * (re
.re_nsub
+1));
448 do_subst(argv
[2], &re
,
449 argc
!= 4 && argv
[4] != NULL
? argv
[4] : "", pmatch
);
456 doregexp(const char *argv
[], int argc
)
463 warnx("Too few arguments to regexp");
466 error
= regcomp(&re
, mimic_gnu
? twiddle(argv
[3]) : argv
[3],
469 exit_regerror(error
, &re
);
471 pmatch
= xalloc(sizeof(regmatch_t
) * (re
.re_nsub
+1));
472 if (argv
[4] == NULL
|| argc
== 4)
473 do_regexpindex(argv
[2], &re
, pmatch
);
475 do_regexp(argv
[2], &re
, argv
[4], pmatch
);
481 doesyscmd(const char *cmd
)
488 /* Follow gnu m4 documentation: first flush buffers. */
491 /* Just set up standard output, share stderr and stdin with m4 */
494 switch(cpid
= fork()) {
500 (void) dup2(p
[1], 1);
502 execl(_PATH_BSHELL
, "sh", "-c", cmd
, NULL
);
505 /* Read result in two stages, since m4's buffer is
509 char result
[BUFSIZE
];
510 cc
= read(p
[0], result
, sizeof result
);
512 addchars(result
, cc
);
513 } while (cc
> 0 || (cc
== -1 && errno
== EINTR
));
516 while ((pid
= wait(&status
)) != cpid
&& pid
>= 0)