1 /* $OpenBSD: gnum4.c,v 1.42 2011/11/06 12:25:43 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.15 2012/11/17 01:54:24 svnexp Exp $
31 * functions needed to support gnu-m4 extensions, including a fake freezing
34 #include <sys/param.h>
35 #include <sys/types.h>
55 * Support for include path search
56 * First search in the current directory.
57 * If not found, and the path is not absolute, include path kicks in.
58 * First, -I options, in the order found on the command line.
59 * Then M4PATH env variable
62 static struct path_entry
{
64 struct path_entry
*next
;
67 static struct path_entry
*new_path_entry(const char *);
68 static void ensure_m4path(void);
69 static struct input_file
*dopath(struct input_file
*, const char *);
71 static struct path_entry
*
72 new_path_entry(const char *dirname
)
76 n
= malloc(sizeof(struct path_entry
));
78 errx(1, "out of memory");
79 n
->name
= strdup(dirname
);
81 errx(1, "out of memory");
87 addtoincludepath(const char *dirname
)
91 n
= new_path_entry(dirname
);
104 static int envpathdone
= 0;
112 envpath
= getenv("M4PATH");
115 /* for portability: getenv result is read-only */
116 envpath
= strdup(envpath
);
118 errx(1, "out of memory");
119 for (sweep
= envpath
;
120 (path
= strsep(&sweep
, ":")) != NULL
;)
121 addtoincludepath(path
);
125 static struct input_file
*
126 dopath(struct input_file
*i
, const char *filename
)
128 char path
[MAXPATHLEN
];
129 struct path_entry
*pe
;
132 for (pe
= first
; pe
; pe
= pe
->next
) {
133 snprintf(path
, sizeof(path
), "%s/%s", pe
->name
, filename
);
134 if ((f
= fopen(path
, "r")) != NULL
) {
135 set_input(i
, f
, path
);
143 fopen_trypath(struct input_file
*i
, const char *filename
)
147 f
= fopen(filename
, "r");
149 set_input(i
, f
, filename
);
152 if (filename
[0] == '/')
157 return dopath(i
, filename
);
161 doindir(const char *argv
[], int argc
)
164 struct macro_definition
*p
= NULL
;
167 if (n
== NULL
|| (p
= macro_getdef(n
)) == NULL
)
168 m4errx(1, "indir: undefined macro %s.", argv
[2]);
171 eval(argv
+ 1, argc
- 1, p
->type
, is_traced(n
));
175 dobuiltin(const char *argv
[], int argc
)
180 p
= macro_getbuiltin(argv
[2]);
182 eval(argv
+ 1, argc
- 1, macro_builtin_type(p
), is_traced(p
));
184 m4errx(1, "unknown builtin %s.", argv
[2]);
188 /* We need some temporary buffer space, as pb pushes BACK and substitution
189 * proceeds forward... */
191 static size_t bufsize
= 0;
192 static size_t current
= 0;
194 static void addchars(const char *, size_t);
195 static void addchar(int);
196 static char *twiddle(const char *);
197 static char *getstring(void);
198 static void exit_regerror(int, regex_t
*);
199 static void do_subst(const char *, regex_t
*, const char *, regmatch_t
*);
200 static void do_regexpindex(const char *, regex_t
*, regmatch_t
*);
201 static void do_regexp(const char *, regex_t
*, const char *, regmatch_t
*);
202 static void add_sub(int, const char *, regex_t
*, regmatch_t
*);
203 static void add_replace(const char *, regex_t
*, const char *, regmatch_t
*);
204 #define addconstantstring(s) addchars((s), sizeof(s)-1)
207 addchars(const char *c
, size_t n
)
211 while (current
+ n
> bufsize
) {
216 buffer
= xrealloc(buffer
, bufsize
, NULL
);
218 memcpy(buffer
+ current
, c
, n
);
225 if (current
+ 1 > bufsize
) {
230 buffer
= xrealloc(buffer
, bufsize
, NULL
);
232 buffer
[current
++] = c
;
245 exit_regerror(int er
, regex_t
*re
)
250 errlen
= regerror(er
, re
, NULL
, 0);
251 errbuf
= xalloc(errlen
,
252 "malloc in regerror: %lu", (unsigned long)errlen
);
253 regerror(er
, re
, errbuf
, errlen
);
254 m4errx(1, "regular expression error: %s.", errbuf
);
258 add_sub(int n
, const char *string
, regex_t
*re
, regmatch_t
*pm
)
260 if (n
> (int)re
->re_nsub
)
261 warnx("No subexpression %d", n
);
262 /* Subexpressions that did not match are
264 else if (pm
[n
].rm_so
!= -1 && pm
[n
].rm_eo
!= -1)
265 addchars(string
+ pm
[n
].rm_so
, pm
[n
].rm_eo
- pm
[n
].rm_so
);
268 /* Add replacement string to the output buffer, recognizing special
269 * constructs and replacing them with substrings of the original string.
272 add_replace(const char *string
, regex_t
*re
, const char *replace
, regmatch_t
*pm
)
276 for (p
= replace
; *p
!= '\0'; p
++) {
277 if (*p
== '&' && !mimic_gnu
) {
278 add_sub(0, string
, re
, pm
);
289 add_sub(0, string
, re
, pm
);
296 add_sub(*(++p
) - '0', string
, re
, pm
);
305 do_subst(const char *string
, regex_t
*re
, const char *replace
, regmatch_t
*pm
)
309 const char *last_match
= NULL
;
311 while ((error
= regexec(re
, string
, re
->re_nsub
+ 1, pm
, flags
)) == 0) {
312 if (pm
[0].rm_eo
!= 0) {
313 if (string
[pm
[0].rm_eo
- 1] == '\n')
319 /* NULL length matches are special... We use the `vi-mode'
320 * rule: don't allow a NULL-match at the last match
323 if (pm
[0].rm_so
== pm
[0].rm_eo
&&
324 string
+ pm
[0].rm_so
== last_match
) {
328 if (*string
++ == '\n')
334 last_match
= string
+ pm
[0].rm_so
;
335 addchars(string
, pm
[0].rm_so
);
336 add_replace(string
, re
, replace
, pm
);
337 string
+= pm
[0].rm_eo
;
339 if (error
!= REG_NOMATCH
)
340 exit_regerror(error
, re
);
345 do_regexp(const char *string
, regex_t
*re
, const char *replace
, regmatch_t
*pm
)
349 switch(error
= regexec(re
, string
, re
->re_nsub
+ 1, pm
, 0)) {
351 add_replace(string
, re
, replace
, pm
);
357 exit_regerror(error
, re
);
362 do_regexpindex(const char *string
, regex_t
*re
, regmatch_t
*pm
)
366 switch(error
= regexec(re
, string
, re
->re_nsub
+ 1, pm
, 0)) {
368 pbunsigned(pm
[0].rm_so
);
374 exit_regerror(error
, re
);
378 /* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2
379 * says. So we twiddle with the regexp before passing it to regcomp.
382 twiddle(const char *p
)
384 /* + at start of regexp is a normal character for Gnu m4 */
391 /* This could use strcspn for speed... */
401 addconstantstring("[_a-zA-Z0-9]");
404 addconstantstring("[^_a-zA-Z0-9]");
407 addconstantstring("[[:<:]]");
410 addconstantstring("[[:>:]]");
419 if (*p
== '(' || *p
== ')' || *p
== '|')
428 /* patsubst(string, regexp, opt replacement) */
434 dopatsubst(const char *argv
[], int argc
)
437 warnx("Too few arguments to patsubst");
440 /* special case: empty regexp */
441 if (argv
[3][0] == '\0') {
444 if (argc
> 4 && argv
[4])
445 len
= strlen(argv
[4]);
448 for (s
= argv
[2]; *s
!= '\0'; s
++) {
449 addchars(argv
[4], len
);
456 int mode
= REG_EXTENDED
;
457 size_t l
= strlen(argv
[3]);
460 (argv
[3][0] == '^') ||
461 (l
> 0 && argv
[3][l
-1] == '$'))
464 error
= regcomp(&re
, mimic_gnu
? twiddle(argv
[3]) : argv
[3],
467 exit_regerror(error
, &re
);
469 pmatch
= xalloc(sizeof(regmatch_t
) * (re
.re_nsub
+ 1), NULL
);
470 do_subst(argv
[2], &re
,
471 argc
> 4 && argv
[4] != NULL
? argv
[4] : "", pmatch
);
479 doregexp(const char *argv
[], int argc
)
486 warnx("Too few arguments to regexp");
489 /* special gnu case */
490 if (argv
[3][0] == '\0' && mimic_gnu
) {
491 if (argc
== 4 || argv
[4] == NULL
)
496 error
= regcomp(&re
, mimic_gnu
? twiddle(argv
[3]) : argv
[3],
497 REG_EXTENDED
|REG_NEWLINE
);
499 exit_regerror(error
, &re
);
501 pmatch
= xalloc(sizeof(regmatch_t
) * (re
.re_nsub
+ 1), NULL
);
502 if (argc
== 4 || argv
[4] == NULL
)
503 do_regexpindex(argv
[2], &re
, pmatch
);
505 do_regexp(argv
[2], &re
, argv
[4], pmatch
);
511 doformat(const char *argv
[], int argc
)
513 const char *format
= argv
[2];
518 const char *thisarg
= NULL
;
522 while (*format
!= 0) {
523 if (*format
!= '%') {
529 if (*format
== '%') {
538 if (*format
== '*') {
542 "Format with too many format specifiers.");
543 width
= strtol(argv
[pos
++], NULL
, 10);
545 width
= strtol(format
, __DECONST(char **, &format
), 10);
553 if (*format
== '.') {
555 if (*format
== '*') {
559 "Format with too many format specifiers.");
560 extra
= strtol(argv
[pos
++], NULL
, 10);
562 extra
= strtol(format
, __DECONST(char **, &format
), 10);
568 m4errx(1, "Format with too many format specifiers.");
571 thisarg
= argv
[pos
++];
574 temp
[0] = strtoul(argv
[pos
++], NULL
, 10);
579 m4errx(1, "Unsupported format specification: %s.",
587 while ((long)l
< width
--)
590 addchars(thisarg
, l
);
592 while ((long)l
< width
--)
600 doesyscmd(const char *cmd
)
608 /* Follow gnu m4 documentation: first flush buffers. */
611 argv
[0] = __DECONST(char *, "sh");
612 argv
[1] = __DECONST(char *, "-c");
613 argv
[2] = __DECONST(char *, cmd
);
616 /* Just set up standard output, share stderr and stdin with m4 */
619 switch(cpid
= fork()) {
627 execv(_PATH_BSHELL
, argv
);
630 /* Read result in two stages, since m4's buffer is
634 char result
[BUFSIZE
];
635 cc
= read(p
[0], result
, sizeof result
);
637 addchars(result
, cc
);
638 } while (cc
> 0 || (cc
== -1 && errno
== EINTR
));
641 while ((pid
= wait(&status
)) != cpid
&& pid
>= 0)
648 getdivfile(const char *name
)
653 f
= fopen(name
, "r");
657 while ((c
= getc(f
)) != EOF
)