| @@ -1,825 +1,836 @@ | | | @@ -1,825 +1,836 @@ |
1 | /* $OpenBSD: main.c,v 1.77 2009/10/14 17:19:47 sthen Exp $ */ | | 1 | /* $OpenBSD: main.c,v 1.77 2009/10/14 17:19:47 sthen Exp $ */ |
2 | /* $NetBSD: main.c,v 1.48 2019/03/26 16:41:06 christos Exp $ */ | | 2 | /* $NetBSD: main.c,v 1.49 2020/06/24 16:49:30 uwe Exp $ */ |
3 | | | 3 | |
4 | /*- | | 4 | /*- |
5 | * Copyright (c) 1989, 1993 | | 5 | * Copyright (c) 1989, 1993 |
6 | * The Regents of the University of California. All rights reserved. | | 6 | * The Regents of the University of California. All rights reserved. |
7 | * | | 7 | * |
8 | * This code is derived from software contributed to Berkeley by | | 8 | * This code is derived from software contributed to Berkeley by |
9 | * Ozan Yigit at York University. | | 9 | * Ozan Yigit at York University. |
10 | * | | 10 | * |
11 | * Redistribution and use in source and binary forms, with or without | | 11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions | | 12 | * modification, are permitted provided that the following conditions |
13 | * are met: | | 13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright | | 14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. | | 15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright | | 16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the | | 17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. | | 18 | * documentation and/or other materials provided with the distribution. |
19 | * 3. Neither the name of the University nor the names of its contributors | | 19 | * 3. Neither the name of the University nor the names of its contributors |
20 | * may be used to endorse or promote products derived from this software | | 20 | * may be used to endorse or promote products derived from this software |
21 | * without specific prior written permission. | | 21 | * without specific prior written permission. |
22 | * | | 22 | * |
23 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 23 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 | * SUCH DAMAGE. | | 33 | * SUCH DAMAGE. |
34 | */ | | 34 | */ |
35 | | | 35 | |
36 | /* | | 36 | /* |
37 | * main.c | | 37 | * main.c |
38 | * Facility: m4 macro processor | | 38 | * Facility: m4 macro processor |
39 | * by: oz | | 39 | * by: oz |
40 | */ | | 40 | */ |
41 | #if HAVE_NBTOOL_CONFIG_H | | 41 | #if HAVE_NBTOOL_CONFIG_H |
42 | #include "nbtool_config.h" | | 42 | #include "nbtool_config.h" |
43 | #endif | | 43 | #endif |
44 | #include <sys/cdefs.h> | | 44 | #include <sys/cdefs.h> |
45 | __RCSID("$NetBSD: main.c,v 1.48 2019/03/26 16:41:06 christos Exp $"); | | 45 | __RCSID("$NetBSD: main.c,v 1.49 2020/06/24 16:49:30 uwe Exp $"); |
46 | #include <assert.h> | | 46 | #include <assert.h> |
47 | #include <signal.h> | | 47 | #include <signal.h> |
48 | #include <getopt.h> | | 48 | #include <getopt.h> |
49 | #include <err.h> | | 49 | #include <err.h> |
50 | #include <errno.h> | | 50 | #include <errno.h> |
51 | #include <unistd.h> | | 51 | #include <unistd.h> |
52 | #include <stdio.h> | | 52 | #include <stdio.h> |
53 | #include <ctype.h> | | 53 | #include <ctype.h> |
54 | #include <string.h> | | 54 | #include <string.h> |
55 | #include <stddef.h> | | 55 | #include <stddef.h> |
56 | #include <stdint.h> | | 56 | #include <stdint.h> |
57 | #include <stdlib.h> | | 57 | #include <stdlib.h> |
58 | #include <ohash.h> | | 58 | #include <ohash.h> |
59 | #include "mdef.h" | | 59 | #include "mdef.h" |
60 | #include "stdd.h" | | 60 | #include "stdd.h" |
61 | #include "extern.h" | | 61 | #include "extern.h" |
62 | #include "pathnames.h" | | 62 | #include "pathnames.h" |
63 | | | 63 | |
64 | ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ | | 64 | ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ |
65 | stae *mstack; /* stack of m4 machine */ | | 65 | stae *mstack; /* stack of m4 machine */ |
66 | char *sstack; /* shadow stack, for string space extension */ | | 66 | char *sstack; /* shadow stack, for string space extension */ |
67 | static size_t STACKMAX; /* current maximum size of stack */ | | 67 | static size_t STACKMAX; /* current maximum size of stack */ |
68 | int sp; /* current m4 stack pointer */ | | 68 | int sp; /* current m4 stack pointer */ |
69 | int fp; /* m4 call frame pointer */ | | 69 | int fp; /* m4 call frame pointer */ |
70 | struct input_file infile[MAXINP];/* input file stack (0=stdin) */ | | 70 | struct input_file infile[MAXINP];/* input file stack (0=stdin) */ |
71 | FILE **outfile; /* diversion array(0=bitbucket)*/ | | 71 | FILE **outfile; /* diversion array(0=bitbucket)*/ |
72 | int maxout; | | 72 | int maxout; |
73 | FILE *active; /* active output file pointer */ | | 73 | FILE *active; /* active output file pointer */ |
74 | int ilevel = 0; /* input file stack pointer */ | | 74 | int ilevel = 0; /* input file stack pointer */ |
75 | int oindex = 0; /* diversion index.. */ | | 75 | int oindex = 0; /* diversion index.. */ |
76 | const char *null = ""; /* as it says.. just a null.. */ | | 76 | const char *null = ""; /* as it says.. just a null.. */ |
77 | char **m4wraps = NULL; /* m4wraps array. */ | | 77 | char **m4wraps = NULL; /* m4wraps array. */ |
78 | int maxwraps = 0; /* size of m4wraps array */ | | 78 | int maxwraps = 0; /* size of m4wraps array */ |
79 | int wrapindex = 0; /* current offset in m4wraps */ | | 79 | int wrapindex = 0; /* current offset in m4wraps */ |
80 | char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */ | | 80 | char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */ |
81 | char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */ | | 81 | char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */ |
82 | char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */ | | 82 | char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */ |
83 | char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */ | | 83 | char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */ |
84 | int synch_lines = 0; /* line synchronisation for C preprocessor */ | | 84 | int synch_lines = 0; /* line synchronisation for C preprocessor */ |
85 | int prefix_builtins = 0; /* -P option to prefix builtin keywords */ | | 85 | int prefix_builtins = 0; /* -P option to prefix builtin keywords */ |
86 | int fatal_warnings = 0; /* -E option to exit on warnings */ | | 86 | int fatal_warnings = 0; /* -E option to exit on warnings */ |
87 | int quiet = 0; /* -Q option to silence warnings */ | | 87 | int quiet = 0; /* -Q option to silence warnings */ |
88 | int nesting_limit = -1; /* -L for nesting limit */ | | 88 | int nesting_limit = -1; /* -L for nesting limit */ |
89 | const char *freeze = NULL; /* -F to freeze state */ | | 89 | const char *freeze = NULL; /* -F to freeze state */ |
90 | const char *reload = NULL; /* -R to reload state */ | | 90 | const char *reload = NULL; /* -R to reload state */ |
91 | #ifndef REAL_FREEZE | | 91 | #ifndef REAL_FREEZE |
92 | FILE *freezef = NULL; | | 92 | FILE *freezef = NULL; |
93 | int thawing = 0; | | 93 | int thawing = 0; |
94 | #endif | | 94 | #endif |
95 | | | 95 | |
96 | struct keyblk { | | 96 | struct keyblk { |
97 | const char *knam; /* keyword name */ | | 97 | const char *knam; /* keyword name */ |
98 | int ktyp; /* keyword type */ | | 98 | int ktyp; /* keyword type */ |
99 | }; | | 99 | }; |
100 | | | 100 | |
101 | struct keyblk keywrds[] = { /* m4 keywords to be installed */ | | 101 | struct keyblk keywrds[] = { /* m4 keywords to be installed */ |
102 | { "include", INCLTYPE }, | | 102 | { "include", INCLTYPE }, |
103 | { "sinclude", SINCTYPE }, | | 103 | { "sinclude", SINCTYPE }, |
104 | { "define", DEFITYPE }, | | 104 | { "define", DEFITYPE }, |
105 | { "defn", DEFNTYPE }, | | 105 | { "defn", DEFNTYPE }, |
106 | { "divert", DIVRTYPE | NOARGS }, | | 106 | { "divert", DIVRTYPE | NOARGS }, |
107 | { "expr", EXPRTYPE }, | | 107 | { "expr", EXPRTYPE }, |
108 | { "eval", EXPRTYPE }, | | 108 | { "eval", EXPRTYPE }, |
109 | { "substr", SUBSTYPE }, | | 109 | { "substr", SUBSTYPE }, |
110 | { "ifelse", IFELTYPE }, | | 110 | { "ifelse", IFELTYPE }, |
111 | { "ifdef", IFDFTYPE }, | | 111 | { "ifdef", IFDFTYPE }, |
112 | { "len", LENGTYPE }, | | 112 | { "len", LENGTYPE }, |
113 | { "incr", INCRTYPE }, | | 113 | { "incr", INCRTYPE }, |
114 | { "decr", DECRTYPE }, | | 114 | { "decr", DECRTYPE }, |
115 | { "dnl", DNLNTYPE | NOARGS }, | | 115 | { "dnl", DNLNTYPE | NOARGS }, |
116 | { "changequote", CHNQTYPE | NOARGS }, | | 116 | { "changequote", CHNQTYPE | NOARGS }, |
117 | { "changecom", CHNCTYPE | NOARGS }, | | 117 | { "changecom", CHNCTYPE | NOARGS }, |
118 | { "index", INDXTYPE }, | | 118 | { "index", INDXTYPE }, |
119 | #ifdef EXTENDED | | 119 | #ifdef EXTENDED |
120 | { "paste", PASTTYPE }, | | 120 | { "paste", PASTTYPE }, |
121 | { "spaste", SPASTYPE }, | | 121 | { "spaste", SPASTYPE }, |
122 | /* Newer extensions, needed to handle gnu-m4 scripts */ | | 122 | /* Newer extensions, needed to handle gnu-m4 scripts */ |
123 | { "indir", INDIRTYPE}, | | 123 | { "indir", INDIRTYPE}, |
124 | { "builtin", BUILTINTYPE}, | | 124 | { "builtin", BUILTINTYPE}, |
125 | { "patsubst", PATSTYPE}, | | 125 | { "patsubst", PATSTYPE}, |
126 | { "regexp", REGEXPTYPE}, | | 126 | { "regexp", REGEXPTYPE}, |
127 | { "esyscmd", ESYSCMDTYPE}, | | 127 | { "esyscmd", ESYSCMDTYPE}, |
128 | { "__file__", FILENAMETYPE | NOARGS}, | | 128 | { "__file__", FILENAMETYPE | NOARGS}, |
129 | { "__line__", LINETYPE | NOARGS}, | | 129 | { "__line__", LINETYPE | NOARGS}, |
130 | #endif | | 130 | #endif |
131 | { "popdef", POPDTYPE }, | | 131 | { "popdef", POPDTYPE }, |
132 | { "pushdef", PUSDTYPE }, | | 132 | { "pushdef", PUSDTYPE }, |
133 | { "dumpdef", DUMPTYPE | NOARGS }, | | 133 | { "dumpdef", DUMPTYPE | NOARGS }, |
134 | { "shift", SHIFTYPE | NOARGS }, | | 134 | { "shift", SHIFTYPE | NOARGS }, |
135 | { "translit", TRNLTYPE }, | | 135 | { "translit", TRNLTYPE }, |
136 | { "undefine", UNDFTYPE }, | | 136 | { "undefine", UNDFTYPE }, |
137 | { "undivert", UNDVTYPE | NOARGS }, | | 137 | { "undivert", UNDVTYPE | NOARGS }, |
138 | { "divnum", DIVNTYPE | NOARGS }, | | 138 | { "divnum", DIVNTYPE | NOARGS }, |
139 | { "maketemp", MKTMTYPE }, | | 139 | { "maketemp", MKTMTYPE }, |
140 | { "errprint", ERRPTYPE | NOARGS }, | | 140 | { "errprint", ERRPTYPE | NOARGS }, |
141 | { "m4wrap", M4WRTYPE | NOARGS }, | | 141 | { "m4wrap", M4WRTYPE | NOARGS }, |
142 | { "m4exit", EXITTYPE | NOARGS }, | | 142 | { "m4exit", EXITTYPE | NOARGS }, |
143 | { "syscmd", SYSCTYPE }, | | 143 | { "syscmd", SYSCTYPE }, |
144 | { "sysval", SYSVTYPE | NOARGS }, | | 144 | { "sysval", SYSVTYPE | NOARGS }, |
145 | { "traceon", TRACEONTYPE | NOARGS }, | | 145 | { "traceon", TRACEONTYPE | NOARGS }, |
146 | { "traceoff", TRACEOFFTYPE | NOARGS }, | | 146 | { "traceoff", TRACEOFFTYPE | NOARGS }, |
147 | | | 147 | |
148 | #if defined(unix) || defined(__unix__) | | 148 | #if defined(unix) || defined(__unix__) |
149 | { "unix", SELFTYPE | NOARGS }, | | 149 | { "unix", SELFTYPE | NOARGS }, |
150 | #else | | 150 | #else |
151 | #ifdef vms | | 151 | #ifdef vms |
152 | { "vms", SELFTYPE | NOARGS }, | | 152 | { "vms", SELFTYPE | NOARGS }, |
153 | #endif | | 153 | #endif |
154 | #endif | | 154 | #endif |
155 | }; | | 155 | }; |
156 | | | 156 | |
157 | #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) | | 157 | #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) |
158 | | | 158 | |
159 | #define MAXRECORD 50 | | 159 | #define MAXRECORD 50 |
160 | static struct position { | | 160 | static struct position { |
161 | char *name; | | 161 | char *name; |
162 | unsigned long line; | | 162 | unsigned long line; |
163 | } quotes[MAXRECORD], paren[MAXRECORD]; | | 163 | } quotes[MAXRECORD], paren[MAXRECORD]; |
164 | | | 164 | |
165 | static void record(struct position *, int); | | 165 | static void record(struct position *, int); |
166 | static void dump_stack(struct position *, int); | | 166 | static void dump_stack(struct position *, int); |
167 | | | 167 | |
168 | static void macro(void); | | 168 | static void macro(void); |
169 | static void initkwds(void); | | 169 | static void initkwds(void); |
170 | static ndptr inspect(int, char *); | | 170 | static ndptr inspect(int, char *); |
171 | static int do_look_ahead(int, const char *); | | 171 | static int do_look_ahead(int, const char *); |
172 | static void reallyoutputstr(const char *); | | 172 | static void reallyoutputstr(const char *); |
173 | static void reallyputchar(int); | | 173 | static void reallyputchar(int); |
174 | | | 174 | |
175 | static void enlarge_stack(void); | | 175 | static void enlarge_stack(void); |
176 | static void help(void); | | 176 | static void help(void); |
177 | | | 177 | |
178 | static void | | 178 | static void |
179 | usage(FILE *f) | | 179 | usage(FILE *f) |
180 | { | | 180 | { |
181 | fprintf(f, "Usage: %s [-EGgiPQsv] [-Dname[=value]] [-d flags] " | | 181 | fprintf(f, "Usage: %s [-EGgiPQsv] [-Dname[=value]] [-d flags] " |
182 | "[-I dirname] [-o filename] [-L limit]\n" | | 182 | "[-I dirname] [-o filename] [-L limit]\n" |
183 | "\t[-t macro] [-Uname] [file ...]\n", getprogname()); | | 183 | "\t[-t macro] [-Uname] [file ...]\n", getprogname()); |
184 | } | | 184 | } |
185 | | | 185 | |
186 | __dead static void | | 186 | __dead static void |
187 | onintr(int signo) | | 187 | onintr(int signo) |
188 | { | | 188 | { |
189 | char intrmessage[] = "m4: interrupted.\n"; | | 189 | char intrmessage[] = "m4: interrupted.\n"; |
190 | write(STDERR_FILENO, intrmessage, sizeof(intrmessage)-1); | | 190 | write(STDERR_FILENO, intrmessage, sizeof(intrmessage)-1); |
191 | _exit(1); | | 191 | _exit(1); |
192 | } | | 192 | } |
193 | | | 193 | |
194 | #define OPT_HELP 1 | | 194 | #define OPT_HELP 1 |
195 | | | 195 | |
196 | struct option longopts[] = { | | 196 | struct option longopts[] = { |
197 | { "debug", optional_argument, 0, 'd' }, | | 197 | { "debug", optional_argument, 0, 'd' }, |
198 | { "define", required_argument, 0, 'D' }, | | 198 | { "define", required_argument, 0, 'D' }, |
199 | { "error-output", required_argument, 0, 'e' }, | | 199 | { "error-output", required_argument, 0, 'e' }, |
200 | { "fatal-warnings", no_argument, 0, 'E' }, | | 200 | { "fatal-warnings", no_argument, 0, 'E' }, |
201 | { "freeze-state", required_argument, 0, 'F' }, | | 201 | { "freeze-state", required_argument, 0, 'F' }, |
202 | { "gnu", no_argument, 0, 'g' }, | | 202 | { "gnu", no_argument, 0, 'g' }, |
203 | { "help", no_argument, 0, OPT_HELP }, | | 203 | { "help", no_argument, 0, OPT_HELP }, |
204 | { "include", required_argument, 0, 'I' }, | | 204 | { "include", required_argument, 0, 'I' }, |
205 | { "interactive", no_argument, 0, 'i' }, | | 205 | { "interactive", no_argument, 0, 'i' }, |
206 | { "nesting-limit", required_argument, 0, 'L' }, | | 206 | { "nesting-limit", required_argument, 0, 'L' }, |
207 | { "prefix-builtins", no_argument, 0, 'P' }, | | 207 | { "prefix-builtins", no_argument, 0, 'P' }, |
208 | { "quiet", no_argument, 0, 'Q' }, | | 208 | { "quiet", no_argument, 0, 'Q' }, |
209 | { "reload-state", required_argument, 0, 'R' }, | | 209 | { "reload-state", required_argument, 0, 'R' }, |
210 | { "silent", no_argument, 0, 'Q' }, | | 210 | { "silent", no_argument, 0, 'Q' }, |
211 | { "synclines", no_argument, 0, 's' }, | | 211 | { "synclines", no_argument, 0, 's' }, |
212 | { "trace", required_argument, 0, 't' }, | | 212 | { "trace", required_argument, 0, 't' }, |
213 | { "traditional", no_argument, 0, 'G' }, | | 213 | { "traditional", no_argument, 0, 'G' }, |
214 | { "undefine", required_argument, 0, 'U' }, | | 214 | { "undefine", required_argument, 0, 'U' }, |
215 | { "version", no_argument, 0, 'v' }, | | 215 | { "version", no_argument, 0, 'v' }, |
216 | #ifdef notyet | | 216 | #ifdef notyet |
217 | { "arglength", required_argument, 0, 'l' }, | | 217 | { "arglength", required_argument, 0, 'l' }, |
218 | { "debugfile", optional_argument, 0, OPT_DEBUGFILE }, | | 218 | { "debugfile", optional_argument, 0, OPT_DEBUGFILE }, |
219 | { "hashsize", required_argument, 0, 'H' }, | | 219 | { "hashsize", required_argument, 0, 'H' }, |
220 | { "warn-macro-sequence",optional_argument, 0, OPT_WARN_SEQUENCE }, | | 220 | { "warn-macro-sequence",optional_argument, 0, OPT_WARN_SEQUENCE }, |
221 | #endif | | 221 | #endif |
222 | { 0, 0, 0, 0 }, | | 222 | { 0, 0, 0, 0 }, |
223 | }; | | 223 | }; |
224 | | | 224 | |
225 | int | | 225 | int |
226 | main(int argc, char *argv[]) | | 226 | main(int argc, char *argv[]) |
227 | { | | 227 | { |
228 | int c; | | 228 | int c; |
229 | int n; | | 229 | int n; |
230 | char *p; | | 230 | char *p; |
231 | FILE *sfp; | | 231 | FILE *sfp; |
232 | | | 232 | |
233 | setprogname(argv[0]); | | 233 | setprogname(argv[0]); |
234 | | | 234 | |
235 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) | | 235 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) |
236 | signal(SIGINT, onintr); | | 236 | signal(SIGINT, onintr); |
237 | | | 237 | |
238 | init_macros(); | | 238 | init_macros(); |
239 | initspaces(); | | 239 | initspaces(); |
240 | STACKMAX = INITSTACKMAX; | | 240 | STACKMAX = INITSTACKMAX; |
241 | | | 241 | |
242 | mstack = (stae *)xalloc(sizeof(stae) * STACKMAX, NULL); | | 242 | mstack = (stae *)xalloc(sizeof(stae) * STACKMAX, NULL); |
243 | sstack = (char *)xalloc(STACKMAX, NULL); | | 243 | sstack = (char *)xalloc(STACKMAX, NULL); |
244 | | | 244 | |
245 | maxout = 0; | | 245 | maxout = 0; |
246 | outfile = NULL; | | 246 | outfile = NULL; |
247 | resizedivs(MAXOUT); | | 247 | resizedivs(MAXOUT); |
248 | | | 248 | |
249 | while ((c = getopt_long(argc, argv, "D:d:e:EF:GgI:iL:o:PR:Qst:U:v", | | 249 | while ((c = getopt_long(argc, argv, "D:d:e:EF:GgI:iL:o:PR:Qst:U:v", |
250 | longopts, NULL)) != -1) | | 250 | longopts, NULL)) != -1) |
251 | switch(c) { | | 251 | switch(c) { |
252 | case 'D': /* define something..*/ | | 252 | case 'D': /* define something..*/ |
253 | for (p = optarg; *p; p++) | | 253 | for (p = optarg; *p; p++) |
254 | if (*p == '=') | | 254 | if (*p == '=') |
255 | break; | | 255 | break; |
256 | if (*p) | | 256 | if (*p) |
257 | *p++ = EOS; | | 257 | *p++ = EOS; |
258 | dodefine(optarg, p); | | 258 | dodefine(optarg, p); |
259 | break; | | 259 | break; |
260 | case 'd': | | 260 | case 'd': |
261 | set_trace_flags(optarg); | | 261 | set_trace_flags(optarg); |
262 | break; | | 262 | break; |
263 | case 'E': | | 263 | case 'E': |
264 | fatal_warnings++; | | 264 | fatal_warnings++; |
265 | break; | | 265 | break; |
266 | case 'e': | | 266 | case 'e': |
267 | /* | | 267 | /* |
268 | * Don't use freopen here because if it fails | | 268 | * Don't use freopen here because if it fails |
269 | * we lose stderr, instead trash it. | | 269 | * we lose stderr, instead trash it. |
270 | */ | | 270 | */ |
271 | if ((sfp = fopen(optarg, "w+")) == NULL) { | | 271 | if ((sfp = fopen(optarg, "w+")) == NULL) { |
272 | warn("Can't redirect errors to `%s'", optarg); | | 272 | warn("Can't redirect errors to `%s'", optarg); |
273 | break; | | 273 | break; |
274 | } | | 274 | } |
275 | fclose(stderr); | | 275 | fclose(stderr); |
276 | memcpy(stderr, sfp, sizeof(*sfp)); | | 276 | memcpy(stderr, sfp, sizeof(*sfp)); |
| | | 277 | /* |
| | | 278 | * XXX: try to avoid the trap set up by the |
| | | 279 | * kludge above. When exit flushes and closes |
| | | 280 | * open streams it may close sfp first and |
| | | 281 | * when it comes about to flush and close |
| | | 282 | * stderr, the descriptor is already gone and |
| | | 283 | * we lose any buffered output. This actually |
| | | 284 | * happens on some hosts, breaking autoconf |
| | | 285 | * tracing. |
| | | 286 | */ |
| | | 287 | setvbuf(stderr, (char *)NULL, _IOLBF, 0); |
277 | break; | | 288 | break; |
278 | case 'F': | | 289 | case 'F': |
279 | freeze = optarg; | | 290 | freeze = optarg; |
280 | #ifndef REAL_FREEZE | | 291 | #ifndef REAL_FREEZE |
281 | if ((freezef = fopen(freeze, "w")) == NULL) | | 292 | if ((freezef = fopen(freeze, "w")) == NULL) |
282 | err(EXIT_FAILURE, "Can't open `%s'", freeze); | | 293 | err(EXIT_FAILURE, "Can't open `%s'", freeze); |
283 | #endif | | 294 | #endif |
284 | break; | | 295 | break; |
285 | case 'I': | | 296 | case 'I': |
286 | addtoincludepath(optarg); | | 297 | addtoincludepath(optarg); |
287 | break; | | 298 | break; |
288 | case 'i': | | 299 | case 'i': |
289 | setvbuf(stdout, NULL, _IONBF, 0); | | 300 | setvbuf(stdout, NULL, _IONBF, 0); |
290 | signal(SIGINT, SIG_IGN); | | 301 | signal(SIGINT, SIG_IGN); |
291 | break; | | 302 | break; |
292 | case 'G': | | 303 | case 'G': |
293 | mimic_gnu = 0; | | 304 | mimic_gnu = 0; |
294 | break; | | 305 | break; |
295 | case 'g': | | 306 | case 'g': |
296 | mimic_gnu = 1; | | 307 | mimic_gnu = 1; |
297 | break; | | 308 | break; |
298 | case 'L': | | 309 | case 'L': |
299 | nesting_limit = atoi(optarg); | | 310 | nesting_limit = atoi(optarg); |
300 | break; | | 311 | break; |
301 | case 'o': | | 312 | case 'o': |
302 | trace_file(optarg); | | 313 | trace_file(optarg); |
303 | break; | | 314 | break; |
304 | case 'P': | | 315 | case 'P': |
305 | prefix_builtins = 1; | | 316 | prefix_builtins = 1; |
306 | break; | | 317 | break; |
307 | case 'Q': | | 318 | case 'Q': |
308 | quiet++; | | 319 | quiet++; |
309 | break; | | 320 | break; |
310 | case 'R': | | 321 | case 'R': |
311 | reload = optarg; | | 322 | reload = optarg; |
312 | break; | | 323 | break; |
313 | case 's': | | 324 | case 's': |
314 | synch_lines = 1; | | 325 | synch_lines = 1; |
315 | break; | | 326 | break; |
316 | case 't': | | 327 | case 't': |
317 | mark_traced(optarg, 1); | | 328 | mark_traced(optarg, 1); |
318 | break; | | 329 | break; |
319 | case 'U': /* undefine... */ | | 330 | case 'U': /* undefine... */ |
320 | macro_popdef(optarg); | | 331 | macro_popdef(optarg); |
321 | break; | | 332 | break; |
322 | case 'v': | | 333 | case 'v': |
323 | fprintf(stderr, "%s version %d\n", getprogname(), | | 334 | fprintf(stderr, "%s version %d\n", getprogname(), |
324 | VERSION); | | 335 | VERSION); |
325 | return EXIT_SUCCESS; | | 336 | return EXIT_SUCCESS; |
326 | case OPT_HELP: | | 337 | case OPT_HELP: |
327 | help(); | | 338 | help(); |
328 | return EXIT_SUCCESS; | | 339 | return EXIT_SUCCESS; |
329 | case '?': | | 340 | case '?': |
330 | default: | | 341 | default: |
331 | usage(stderr); | | 342 | usage(stderr); |
332 | return EXIT_FAILURE; | | 343 | return EXIT_FAILURE; |
333 | } | | 344 | } |
334 | | | 345 | |
335 | #ifdef REDIRECT | | 346 | #ifdef REDIRECT |
336 | /* | | 347 | /* |
337 | * This is meant only for debugging; it makes all output | | 348 | * This is meant only for debugging; it makes all output |
338 | * go to a known file, even if the command line options | | 349 | * go to a known file, even if the command line options |
339 | * send it elsewhere. It should not be turned of in production code. | | 350 | * send it elsewhere. It should not be turned of in production code. |
340 | */ | | 351 | */ |
341 | if (freopen("/tmp/m4", "w+", stderr) == NULL) | | 352 | if (freopen("/tmp/m4", "w+", stderr) == NULL) |
342 | err(EXIT_FAILURE, "Can't redirect errors to `%s'", | | 353 | err(EXIT_FAILURE, "Can't redirect errors to `%s'", |
343 | "/tmp/m4"); | | 354 | "/tmp/m4"); |
344 | #endif | | 355 | #endif |
345 | argc -= optind; | | 356 | argc -= optind; |
346 | argv += optind; | | 357 | argv += optind; |
347 | | | 358 | |
348 | | | 359 | |
349 | initkwds(); | | 360 | initkwds(); |
350 | if (mimic_gnu) | | 361 | if (mimic_gnu) |
351 | setup_builtin("format", FORMATTYPE); | | 362 | setup_builtin("format", FORMATTYPE); |
352 | | | 363 | |
353 | active = stdout; /* default active output */ | | 364 | active = stdout; /* default active output */ |
354 | bbase[0] = bufbase; | | 365 | bbase[0] = bufbase; |
355 | | | 366 | |
356 | if (reload) { | | 367 | if (reload) { |
357 | #ifdef REAL_FREEZE | | 368 | #ifdef REAL_FREEZE |
358 | thaw_state(reload); | | 369 | thaw_state(reload); |
359 | #else | | 370 | #else |
360 | if (fopen_trypath(infile, reload) == NULL) | | 371 | if (fopen_trypath(infile, reload) == NULL) |
361 | err(1, "Can't open `%s'", reload); | | 372 | err(1, "Can't open `%s'", reload); |
362 | sp = -1; | | 373 | sp = -1; |
363 | fp = 0; | | 374 | fp = 0; |
364 | thawing = 1; | | 375 | thawing = 1; |
365 | macro(); | | 376 | macro(); |
366 | thawing = 0; | | 377 | thawing = 0; |
367 | release_input(infile); | | 378 | release_input(infile); |
368 | #endif | | 379 | #endif |
369 | } | | 380 | } |
370 | | | 381 | |
371 | if (!argc) { | | 382 | if (!argc) { |
372 | sp = -1; /* stack pointer initialized */ | | 383 | sp = -1; /* stack pointer initialized */ |
373 | fp = 0; /* frame pointer initialized */ | | 384 | fp = 0; /* frame pointer initialized */ |
374 | set_input(infile+0, stdin, "stdin"); | | 385 | set_input(infile+0, stdin, "stdin"); |
375 | /* default input (naturally) */ | | 386 | /* default input (naturally) */ |
376 | macro(); | | 387 | macro(); |
377 | } else | | 388 | } else |
378 | for (; argc--; ++argv) { | | 389 | for (; argc--; ++argv) { |
379 | p = *argv; | | 390 | p = *argv; |
380 | if (p[0] == '-' && p[1] == EOS) | | 391 | if (p[0] == '-' && p[1] == EOS) |
381 | set_input(infile, stdin, "stdin"); | | 392 | set_input(infile, stdin, "stdin"); |
382 | else if (fopen_trypath(infile, p) == NULL) | | 393 | else if (fopen_trypath(infile, p) == NULL) |
383 | err(1, "%s", p); | | 394 | err(1, "%s", p); |
384 | sp = -1; | | 395 | sp = -1; |
385 | fp = 0; | | 396 | fp = 0; |
386 | macro(); | | 397 | macro(); |
387 | release_input(infile); | | 398 | release_input(infile); |
388 | } | | 399 | } |
389 | | | 400 | |
390 | if (wrapindex) { | | 401 | if (wrapindex) { |
391 | int i; | | 402 | int i; |
392 | | | 403 | |
393 | ilevel = 0; /* in case m4wrap includes.. */ | | 404 | ilevel = 0; /* in case m4wrap includes.. */ |
394 | bufbase = bp = buf; /* use the entire buffer */ | | 405 | bufbase = bp = buf; /* use the entire buffer */ |
395 | if (mimic_gnu) { | | 406 | if (mimic_gnu) { |
396 | while (wrapindex != 0) { | | 407 | while (wrapindex != 0) { |
397 | for (i = 0; i < wrapindex; i++) | | 408 | for (i = 0; i < wrapindex; i++) |
398 | pbstr(m4wraps[i]); | | 409 | pbstr(m4wraps[i]); |
399 | wrapindex =0; | | 410 | wrapindex =0; |
400 | macro(); | | 411 | macro(); |
401 | } | | 412 | } |
402 | } else { | | 413 | } else { |
403 | for (i = 0; i < wrapindex; i++) { | | 414 | for (i = 0; i < wrapindex; i++) { |
404 | pbstr(m4wraps[i]); | | 415 | pbstr(m4wraps[i]); |
405 | macro(); | | 416 | macro(); |
406 | } | | 417 | } |
407 | } | | 418 | } |
408 | } | | 419 | } |
409 | | | 420 | |
410 | if (active != stdout) | | 421 | if (active != stdout) |
411 | active = stdout; /* reset output just in case */ | | 422 | active = stdout; /* reset output just in case */ |
412 | for (n = 1; n < maxout; n++) /* default wrap-up: undivert */ | | 423 | for (n = 1; n < maxout; n++) /* default wrap-up: undivert */ |
413 | if (outfile[n] != NULL) | | 424 | if (outfile[n] != NULL) |
414 | getdiv(n); | | 425 | getdiv(n); |
415 | /* remove bitbucket if used */ | | 426 | /* remove bitbucket if used */ |
416 | if (outfile[0] != NULL) { | | 427 | if (outfile[0] != NULL) { |
417 | (void) fclose(outfile[0]); | | 428 | (void) fclose(outfile[0]); |
418 | } | | 429 | } |
419 | | | 430 | |
420 | #ifdef REAL_FREEZE | | 431 | #ifdef REAL_FREEZE |
421 | if (freeze) | | 432 | if (freeze) |
422 | freeze_state(freeze); | | 433 | freeze_state(freeze); |
423 | #else | | 434 | #else |
424 | if (freezef) | | 435 | if (freezef) |
425 | fclose(freezef); | | 436 | fclose(freezef); |
426 | #endif | | 437 | #endif |
427 | | | 438 | |
428 | return 0; | | 439 | return 0; |
429 | } | | 440 | } |
430 | | | 441 | |
431 | /* | | 442 | /* |
432 | * Look ahead for `token'. | | 443 | * Look ahead for `token'. |
433 | * (on input `t == token[0]') | | 444 | * (on input `t == token[0]') |
434 | * Used for comment and quoting delimiters. | | 445 | * Used for comment and quoting delimiters. |
435 | * Returns 1 if `token' present; copied to output. | | 446 | * Returns 1 if `token' present; copied to output. |
436 | * 0 if `token' not found; all characters pushed back | | 447 | * 0 if `token' not found; all characters pushed back |
437 | */ | | 448 | */ |
438 | static int | | 449 | static int |
439 | do_look_ahead(int t, const char *token) | | 450 | do_look_ahead(int t, const char *token) |
440 | { | | 451 | { |
441 | int i; | | 452 | int i; |
442 | | | 453 | |
443 | assert((unsigned char)t == (unsigned char)token[0]); | | 454 | assert((unsigned char)t == (unsigned char)token[0]); |
444 | | | 455 | |
445 | for (i = 1; *++token; i++) { | | 456 | for (i = 1; *++token; i++) { |
446 | t = gpbc(); | | 457 | t = gpbc(); |
447 | if (t == EOF || (unsigned char)t != (unsigned char)*token) { | | 458 | if (t == EOF || (unsigned char)t != (unsigned char)*token) { |
448 | pushback(t); | | 459 | pushback(t); |
449 | while (--i) | | 460 | while (--i) |
450 | pushback(*--token); | | 461 | pushback(*--token); |
451 | return 0; | | 462 | return 0; |
452 | } | | 463 | } |
453 | } | | 464 | } |
454 | return 1; | | 465 | return 1; |
455 | } | | 466 | } |
456 | | | 467 | |
457 | #define LOOK_AHEAD(t, token) (t != EOF && \ | | 468 | #define LOOK_AHEAD(t, token) (t != EOF && \ |
458 | (unsigned char)(t)==(unsigned char)(token)[0] && \ | | 469 | (unsigned char)(t)==(unsigned char)(token)[0] && \ |
459 | do_look_ahead(t,token)) | | 470 | do_look_ahead(t,token)) |
460 | | | 471 | |
461 | /* | | 472 | /* |
462 | * macro - the work horse.. | | 473 | * macro - the work horse.. |
463 | */ | | 474 | */ |
464 | static void | | 475 | static void |
465 | macro(void) | | 476 | macro(void) |
466 | { | | 477 | { |
467 | char token[MAXTOK+1]; | | 478 | char token[MAXTOK+1]; |
468 | int t, l; | | 479 | int t, l; |
469 | ndptr p; | | 480 | ndptr p; |
470 | int nlpar; | | 481 | int nlpar; |
471 | | | 482 | |
472 | cycle { | | 483 | cycle { |
473 | t = gpbc(); | | 484 | t = gpbc(); |
474 | | | 485 | |
475 | if (LOOK_AHEAD(t,lquote)) { /* strip quotes */ | | 486 | if (LOOK_AHEAD(t,lquote)) { /* strip quotes */ |
476 | nlpar = 0; | | 487 | nlpar = 0; |
477 | record(quotes, nlpar++); | | 488 | record(quotes, nlpar++); |
478 | /* | | 489 | /* |
479 | * Opening quote: scan forward until matching | | 490 | * Opening quote: scan forward until matching |
480 | * closing quote has been found. | | 491 | * closing quote has been found. |
481 | */ | | 492 | */ |
482 | do { | | 493 | do { |
483 | | | 494 | |
484 | l = gpbc(); | | 495 | l = gpbc(); |
485 | if (LOOK_AHEAD(l,rquote)) { | | 496 | if (LOOK_AHEAD(l,rquote)) { |
486 | if (--nlpar > 0) | | 497 | if (--nlpar > 0) |
487 | outputstr(rquote); | | 498 | outputstr(rquote); |
488 | } else if (LOOK_AHEAD(l,lquote)) { | | 499 | } else if (LOOK_AHEAD(l,lquote)) { |
489 | record(quotes, nlpar++); | | 500 | record(quotes, nlpar++); |
490 | outputstr(lquote); | | 501 | outputstr(lquote); |
491 | } else if (l == EOF) { | | 502 | } else if (l == EOF) { |
492 | if (!quiet) { | | 503 | if (!quiet) { |
493 | if (nlpar == 1) | | 504 | if (nlpar == 1) |
494 | warnx("unclosed quote:"); | | 505 | warnx("unclosed quote:"); |
495 | else | | 506 | else |
496 | warnx( | | 507 | warnx( |
497 | "%d unclosed quotes:", | | 508 | "%d unclosed quotes:", |
498 | nlpar); | | 509 | nlpar); |
499 | dump_stack(quotes, nlpar); | | 510 | dump_stack(quotes, nlpar); |
500 | } | | 511 | } |
501 | exit(EXIT_FAILURE); | | 512 | exit(EXIT_FAILURE); |
502 | } else { | | 513 | } else { |
503 | if (nlpar > 0) { | | 514 | if (nlpar > 0) { |
504 | if (sp < 0) | | 515 | if (sp < 0) |
505 | reallyputchar(l); | | 516 | reallyputchar(l); |
506 | else | | 517 | else |
507 | CHRSAVE(l); | | 518 | CHRSAVE(l); |
508 | } | | 519 | } |
509 | } | | 520 | } |
510 | } | | 521 | } |
511 | while (nlpar != 0); | | 522 | while (nlpar != 0); |
512 | } else if (sp < 0 && LOOK_AHEAD(t, scommt)) { | | 523 | } else if (sp < 0 && LOOK_AHEAD(t, scommt)) { |
513 | reallyoutputstr(scommt); | | 524 | reallyoutputstr(scommt); |
514 | | | 525 | |
515 | for(;;) { | | 526 | for(;;) { |
516 | t = gpbc(); | | 527 | t = gpbc(); |
517 | if (LOOK_AHEAD(t, ecommt)) { | | 528 | if (LOOK_AHEAD(t, ecommt)) { |
518 | reallyoutputstr(ecommt); | | 529 | reallyoutputstr(ecommt); |
519 | break; | | 530 | break; |
520 | } | | 531 | } |
521 | if (t == EOF) | | 532 | if (t == EOF) |
522 | break; | | 533 | break; |
523 | reallyputchar(t); | | 534 | reallyputchar(t); |
524 | } | | 535 | } |
525 | } else if (t == '_' || isalpha(t)) { | | 536 | } else if (t == '_' || isalpha(t)) { |
526 | p = inspect(t, token); | | 537 | p = inspect(t, token); |
527 | if (p != NULL) | | 538 | if (p != NULL) |
528 | pushback(l = gpbc()); | | 539 | pushback(l = gpbc()); |
529 | if (p == NULL || (l != LPAREN && | | 540 | if (p == NULL || (l != LPAREN && |
530 | (macro_getdef(p)->type & NEEDARGS) != 0)) | | 541 | (macro_getdef(p)->type & NEEDARGS) != 0)) |
531 | outputstr(token); | | 542 | outputstr(token); |
532 | else { | | 543 | else { |
533 | /* | | 544 | /* |
534 | * real thing.. First build a call frame: | | 545 | * real thing.. First build a call frame: |
535 | */ | | 546 | */ |
536 | pushf(fp); /* previous call frm */ | | 547 | pushf(fp); /* previous call frm */ |
537 | pushf(macro_getdef(p)->type); /* type of the call */ | | 548 | pushf(macro_getdef(p)->type); /* type of the call */ |
538 | pushf(is_traced(p)); | | 549 | pushf(is_traced(p)); |
539 | pushf(0); /* parenthesis level */ | | 550 | pushf(0); /* parenthesis level */ |
540 | fp = sp; /* new frame pointer */ | | 551 | fp = sp; /* new frame pointer */ |
541 | /* | | 552 | /* |
542 | * now push the string arguments: | | 553 | * now push the string arguments: |
543 | * XXX: Copy the macro definition. This leaks, but too | | 554 | * XXX: Copy the macro definition. This leaks, but too |
544 | * lazy to fix properly. | | 555 | * lazy to fix properly. |
545 | * The problem is that if we evaluate a pushdef'ed | | 556 | * The problem is that if we evaluate a pushdef'ed |
546 | * macro and then popdef it while it the definition | | 557 | * macro and then popdef it while it the definition |
547 | * is still on the stack we are going to reference | | 558 | * is still on the stack we are going to reference |
548 | * free memory. | | 559 | * free memory. |
549 | */ | | 560 | */ |
550 | pushs1(xstrdup(macro_getdef(p)->defn)); /* defn string */ | | 561 | pushs1(xstrdup(macro_getdef(p)->defn)); /* defn string */ |
551 | pushs1((char *)macro_name(p)); /* macro name */ | | 562 | pushs1((char *)macro_name(p)); /* macro name */ |
552 | pushs(ep); /* start next..*/ | | 563 | pushs(ep); /* start next..*/ |
553 | | | 564 | |
554 | if (l != LPAREN && PARLEV == 0) { | | 565 | if (l != LPAREN && PARLEV == 0) { |
555 | /* no bracks */ | | 566 | /* no bracks */ |
556 | chrsave(EOS); | | 567 | chrsave(EOS); |
557 | | | 568 | |
558 | if ((size_t)sp == STACKMAX) | | 569 | if ((size_t)sp == STACKMAX) |
559 | errx(1, "internal stack overflow"); | | 570 | errx(1, "internal stack overflow"); |
560 | eval((const char **) mstack+fp+1, 2, | | 571 | eval((const char **) mstack+fp+1, 2, |
561 | CALTYP, TRACESTATUS); | | 572 | CALTYP, TRACESTATUS); |
562 | | | 573 | |
563 | ep = PREVEP; /* flush strspace */ | | 574 | ep = PREVEP; /* flush strspace */ |
564 | sp = PREVSP; /* previous sp.. */ | | 575 | sp = PREVSP; /* previous sp.. */ |
565 | fp = PREVFP; /* rewind stack...*/ | | 576 | fp = PREVFP; /* rewind stack...*/ |
566 | } | | 577 | } |
567 | } | | 578 | } |
568 | } else if (t == EOF) { | | 579 | } else if (t == EOF) { |
569 | if (sp > -1 && ilevel <= 0) { | | 580 | if (sp > -1 && ilevel <= 0) { |
570 | if (!quiet) { | | 581 | if (!quiet) { |
571 | warnx("unexpected end of input, " | | 582 | warnx("unexpected end of input, " |
572 | "unclosed parenthesis:"); | | 583 | "unclosed parenthesis:"); |
573 | dump_stack(paren, PARLEV); | | 584 | dump_stack(paren, PARLEV); |
574 | } | | 585 | } |
575 | exit(EXIT_FAILURE); | | 586 | exit(EXIT_FAILURE); |
576 | } | | 587 | } |
577 | if (ilevel <= 0) | | 588 | if (ilevel <= 0) |
578 | break; /* all done thanks.. */ | | 589 | break; /* all done thanks.. */ |
579 | release_input(infile+ilevel--); | | 590 | release_input(infile+ilevel--); |
580 | emit_synchline(); | | 591 | emit_synchline(); |
581 | bufbase = bbase[ilevel]; | | 592 | bufbase = bbase[ilevel]; |
582 | continue; | | 593 | continue; |
583 | } else if (sp < 0) { /* not in a macro at all */ | | 594 | } else if (sp < 0) { /* not in a macro at all */ |
584 | reallyputchar(t); /* output directly.. */ | | 595 | reallyputchar(t); /* output directly.. */ |
585 | } | | 596 | } |
586 | | | 597 | |
587 | else switch(t) { | | 598 | else switch(t) { |
588 | | | 599 | |
589 | case LPAREN: | | 600 | case LPAREN: |
590 | if (PARLEV > 0) | | 601 | if (PARLEV > 0) |
591 | chrsave(t); | | 602 | chrsave(t); |
592 | while (isspace(l = gpbc())) /* skip blank, tab, nl.. */ | | 603 | while (isspace(l = gpbc())) /* skip blank, tab, nl.. */ |
593 | if (PARLEV > 0) | | 604 | if (PARLEV > 0) |
594 | chrsave(l); | | 605 | chrsave(l); |
595 | pushback(l); | | 606 | pushback(l); |
596 | record(paren, PARLEV++); | | 607 | record(paren, PARLEV++); |
597 | break; | | 608 | break; |
598 | | | 609 | |
599 | case RPAREN: | | 610 | case RPAREN: |
600 | if (--PARLEV > 0) | | 611 | if (--PARLEV > 0) |
601 | chrsave(t); | | 612 | chrsave(t); |
602 | else { /* end of argument list */ | | 613 | else { /* end of argument list */ |
603 | chrsave(EOS); | | 614 | chrsave(EOS); |
604 | | | 615 | |
605 | if ((size_t)sp == STACKMAX) | | 616 | if ((size_t)sp == STACKMAX) |
606 | errx(1, "internal stack overflow"); | | 617 | errx(1, "internal stack overflow"); |
607 | | | 618 | |
608 | eval((const char **) mstack+fp+1, sp-fp, | | 619 | eval((const char **) mstack+fp+1, sp-fp, |
609 | CALTYP, TRACESTATUS); | | 620 | CALTYP, TRACESTATUS); |
610 | | | 621 | |
611 | ep = PREVEP; /* flush strspace */ | | 622 | ep = PREVEP; /* flush strspace */ |
612 | sp = PREVSP; /* previous sp.. */ | | 623 | sp = PREVSP; /* previous sp.. */ |
613 | fp = PREVFP; /* rewind stack...*/ | | 624 | fp = PREVFP; /* rewind stack...*/ |
614 | } | | 625 | } |
615 | break; | | 626 | break; |
616 | | | 627 | |
617 | case COMMA: | | 628 | case COMMA: |
618 | if (PARLEV == 1) { | | 629 | if (PARLEV == 1) { |
619 | chrsave(EOS); /* new argument */ | | 630 | chrsave(EOS); /* new argument */ |
620 | while (isspace(l = gpbc())) | | 631 | while (isspace(l = gpbc())) |
621 | ; | | 632 | ; |
622 | pushback(l); | | 633 | pushback(l); |
623 | pushs(ep); | | 634 | pushs(ep); |
624 | } else | | 635 | } else |
625 | chrsave(t); | | 636 | chrsave(t); |
626 | break; | | 637 | break; |
627 | | | 638 | |
628 | default: | | 639 | default: |
629 | if (LOOK_AHEAD(t, scommt)) { | | 640 | if (LOOK_AHEAD(t, scommt)) { |
630 | char *q; | | 641 | char *q; |
631 | for (q = scommt; *q; q++) | | 642 | for (q = scommt; *q; q++) |
632 | chrsave(*q); | | 643 | chrsave(*q); |
633 | for(;;) { | | 644 | for(;;) { |
634 | t = gpbc(); | | 645 | t = gpbc(); |
635 | if (LOOK_AHEAD(t, ecommt)) { | | 646 | if (LOOK_AHEAD(t, ecommt)) { |
636 | for (q = ecommt; *q; q++) | | 647 | for (q = ecommt; *q; q++) |
637 | chrsave(*q); | | 648 | chrsave(*q); |
638 | break; | | 649 | break; |
639 | } | | 650 | } |
640 | if (t == EOF) | | 651 | if (t == EOF) |
641 | break; | | 652 | break; |
642 | CHRSAVE(t); | | 653 | CHRSAVE(t); |
643 | } | | 654 | } |
644 | } else | | 655 | } else |
645 | CHRSAVE(t); /* stack the char */ | | 656 | CHRSAVE(t); /* stack the char */ |
646 | break; | | 657 | break; |
647 | } | | 658 | } |
648 | } | | 659 | } |
649 | } | | 660 | } |
650 | | | 661 | |
651 | /* | | 662 | /* |
652 | * output string directly, without pushing it for reparses. | | 663 | * output string directly, without pushing it for reparses. |
653 | */ | | 664 | */ |
654 | void | | 665 | void |
655 | outputstr(const char *s) | | 666 | outputstr(const char *s) |
656 | { | | 667 | { |
657 | if (sp < 0) | | 668 | if (sp < 0) |
658 | reallyoutputstr(s); | | 669 | reallyoutputstr(s); |
659 | else | | 670 | else |
660 | while (*s) | | 671 | while (*s) |
661 | CHRSAVE(*s++); | | 672 | CHRSAVE(*s++); |
662 | } | | 673 | } |
663 | | | 674 | |
664 | void | | 675 | void |
665 | reallyoutputstr(const char *s) | | 676 | reallyoutputstr(const char *s) |
666 | { | | 677 | { |
667 | if (synch_lines) { | | 678 | if (synch_lines) { |
668 | while (*s) { | | 679 | while (*s) { |
669 | fputc(*s, active); | | 680 | fputc(*s, active); |
670 | if (*s++ == '\n') { | | 681 | if (*s++ == '\n') { |
671 | infile[ilevel].synch_lineno++; | | 682 | infile[ilevel].synch_lineno++; |
672 | if (infile[ilevel].synch_lineno != | | 683 | if (infile[ilevel].synch_lineno != |
673 | infile[ilevel].lineno) | | 684 | infile[ilevel].lineno) |
674 | do_emit_synchline(); | | 685 | do_emit_synchline(); |
675 | } | | 686 | } |
676 | } | | 687 | } |
677 | } else | | 688 | } else |
678 | fputs(s, active); | | 689 | fputs(s, active); |
679 | } | | 690 | } |
680 | | | 691 | |
681 | void | | 692 | void |
682 | reallyputchar(int c) | | 693 | reallyputchar(int c) |
683 | { | | 694 | { |
684 | putc(c, active); | | 695 | putc(c, active); |
685 | if (synch_lines && c == '\n') { | | 696 | if (synch_lines && c == '\n') { |
686 | infile[ilevel].synch_lineno++; | | 697 | infile[ilevel].synch_lineno++; |
687 | if (infile[ilevel].synch_lineno != infile[ilevel].lineno) | | 698 | if (infile[ilevel].synch_lineno != infile[ilevel].lineno) |
688 | do_emit_synchline(); | | 699 | do_emit_synchline(); |
689 | } | | 700 | } |
690 | } | | 701 | } |
691 | | | 702 | |
692 | /* | | 703 | /* |
693 | * build an input token.. | | 704 | * build an input token.. |
694 | * consider only those starting with _ or A-Za-z. | | 705 | * consider only those starting with _ or A-Za-z. |
695 | */ | | 706 | */ |
696 | static ndptr | | 707 | static ndptr |
697 | inspect(int c, char *tp) | | 708 | inspect(int c, char *tp) |
698 | { | | 709 | { |
699 | char *name = tp; | | 710 | char *name = tp; |
700 | char *etp = tp+MAXTOK; | | 711 | char *etp = tp+MAXTOK; |
701 | ndptr p; | | 712 | ndptr p; |
702 | | | 713 | |
703 | *tp++ = c; | | 714 | *tp++ = c; |
704 | | | 715 | |
705 | while ((isalnum(c = gpbc()) || c == '_') && tp < etp) | | 716 | while ((isalnum(c = gpbc()) || c == '_') && tp < etp) |
706 | *tp++ = c; | | 717 | *tp++ = c; |
707 | if (c != EOF) | | 718 | if (c != EOF) |
708 | PUSHBACK(c); | | 719 | PUSHBACK(c); |
709 | *tp = EOS; | | 720 | *tp = EOS; |
710 | /* token is too long, it won't match anything, but it can still | | 721 | /* token is too long, it won't match anything, but it can still |
711 | * be output. */ | | 722 | * be output. */ |
712 | if (tp == ep) { | | 723 | if (tp == ep) { |
713 | outputstr(name); | | 724 | outputstr(name); |
714 | while (isalnum(c = gpbc()) || c == '_') { | | 725 | while (isalnum(c = gpbc()) || c == '_') { |
715 | if (sp < 0) | | 726 | if (sp < 0) |
716 | reallyputchar(c); | | 727 | reallyputchar(c); |
717 | else | | 728 | else |
718 | CHRSAVE(c); | | 729 | CHRSAVE(c); |
719 | } | | 730 | } |
720 | *name = EOS; | | 731 | *name = EOS; |
721 | return NULL; | | 732 | return NULL; |
722 | } | | 733 | } |
723 | | | 734 | |
724 | p = ohash_find(¯os, ohash_qlookupi(¯os, name, (void *)&tp)); | | 735 | p = ohash_find(¯os, ohash_qlookupi(¯os, name, (void *)&tp)); |
725 | if (p == NULL) | | 736 | if (p == NULL) |
726 | return NULL; | | 737 | return NULL; |
727 | if (macro_getdef(p) == NULL) | | 738 | if (macro_getdef(p) == NULL) |
728 | return NULL; | | 739 | return NULL; |
729 | return p; | | 740 | return p; |
730 | } | | 741 | } |
731 | | | 742 | |
732 | /* | | 743 | /* |
733 | * initkwds - initialise m4 keywords as fast as possible. | | 744 | * initkwds - initialise m4 keywords as fast as possible. |
734 | * This very similar to install, but without certain overheads, | | 745 | * This very similar to install, but without certain overheads, |
735 | * such as calling lookup. Malloc is not used for storing the | | 746 | * such as calling lookup. Malloc is not used for storing the |
736 | * keyword strings, since we simply use the static pointers | | 747 | * keyword strings, since we simply use the static pointers |
737 | * within keywrds block. | | 748 | * within keywrds block. |
738 | */ | | 749 | */ |
739 | static void | | 750 | static void |
740 | initkwds(void) | | 751 | initkwds(void) |
741 | { | | 752 | { |
742 | unsigned int type; | | 753 | unsigned int type; |
743 | size_t i; | | 754 | size_t i; |
744 | | | 755 | |
745 | for (i = 0; i < MAXKEYS; i++) { | | 756 | for (i = 0; i < MAXKEYS; i++) { |
746 | type = keywrds[i].ktyp; | | 757 | type = keywrds[i].ktyp; |
747 | if ((keywrds[i].ktyp & NOARGS) == 0) | | 758 | if ((keywrds[i].ktyp & NOARGS) == 0) |
748 | type |= NEEDARGS; | | 759 | type |= NEEDARGS; |
749 | setup_builtin(keywrds[i].knam, type); | | 760 | setup_builtin(keywrds[i].knam, type); |
750 | } | | 761 | } |
751 | } | | 762 | } |
752 | | | 763 | |
753 | static void | | 764 | static void |
754 | record(struct position *t, int lev) | | 765 | record(struct position *t, int lev) |
755 | { | | 766 | { |
756 | if (lev < MAXRECORD) { | | 767 | if (lev < MAXRECORD) { |
757 | t[lev].name = CURRENT_NAME; | | 768 | t[lev].name = CURRENT_NAME; |
758 | t[lev].line = CURRENT_LINE; | | 769 | t[lev].line = CURRENT_LINE; |
759 | } | | 770 | } |
760 | } | | 771 | } |
761 | | | 772 | |
762 | static void | | 773 | static void |
763 | dump_stack(struct position *t, int lev) | | 774 | dump_stack(struct position *t, int lev) |
764 | { | | 775 | { |
765 | int i; | | 776 | int i; |
766 | | | 777 | |
767 | for (i = 0; i < lev; i++) { | | 778 | for (i = 0; i < lev; i++) { |
768 | if (i == MAXRECORD) { | | 779 | if (i == MAXRECORD) { |
769 | fprintf(stderr, " ...\n"); | | 780 | fprintf(stderr, " ...\n"); |
770 | break; | | 781 | break; |
771 | } | | 782 | } |
772 | fprintf(stderr, " %s at line %lu\n", | | 783 | fprintf(stderr, " %s at line %lu\n", |
773 | t[i].name, t[i].line); | | 784 | t[i].name, t[i].line); |
774 | } | | 785 | } |
775 | } | | 786 | } |
776 | | | 787 | |
777 | | | 788 | |
778 | static void | | 789 | static void |
779 | enlarge_stack(void) | | 790 | enlarge_stack(void) |
780 | { | | 791 | { |
781 | STACKMAX += STACKMAX/2; | | 792 | STACKMAX += STACKMAX/2; |
782 | mstack = xrealloc(mstack, sizeof(stae) * STACKMAX, | | 793 | mstack = xrealloc(mstack, sizeof(stae) * STACKMAX, |
783 | "Evaluation stack overflow (%lu)", | | 794 | "Evaluation stack overflow (%lu)", |
784 | (unsigned long)STACKMAX); | | 795 | (unsigned long)STACKMAX); |
785 | sstack = xrealloc(sstack, STACKMAX, | | 796 | sstack = xrealloc(sstack, STACKMAX, |
786 | "Evaluation stack overflow (%lu)", | | 797 | "Evaluation stack overflow (%lu)", |
787 | (unsigned long)STACKMAX); | | 798 | (unsigned long)STACKMAX); |
788 | } | | 799 | } |
789 | | | 800 | |
790 | static const struct { | | 801 | static const struct { |
791 | const char *n; | | 802 | const char *n; |
792 | const char *d; | | 803 | const char *d; |
793 | } nd [] = { | | 804 | } nd [] = { |
794 | { "-d, --debug[=flags]", "set debug flags" }, | | 805 | { "-d, --debug[=flags]", "set debug flags" }, |
795 | { "-D, --define=name[=value]", "define macro" }, | | 806 | { "-D, --define=name[=value]", "define macro" }, |
796 | { "-e, --error-output=file", "send error output to file" }, | | 807 | { "-e, --error-output=file", "send error output to file" }, |
797 | { "-E, --fatal-warnings", "exit on warnings" }, | | 808 | { "-E, --fatal-warnings", "exit on warnings" }, |
798 | { "-F, --freeze-state=file", "save state to file" }, | | 809 | { "-F, --freeze-state=file", "save state to file" }, |
799 | { "-g, --gnu", "enable gnu extensions" }, | | 810 | { "-g, --gnu", "enable gnu extensions" }, |
800 | { " --help", "print this message and exit" }, | | 811 | { " --help", "print this message and exit" }, |
801 | { "-I, --include=file", "include file" }, | | 812 | { "-I, --include=file", "include file" }, |
802 | { "-i, --interactive", "unbuffer output, ignore tty signals" }, | | 813 | { "-i, --interactive", "unbuffer output, ignore tty signals" }, |
803 | { "-L, --nesting-limit=num", "macro expansion nesting limit (unimpl)" }, | | 814 | { "-L, --nesting-limit=num", "macro expansion nesting limit (unimpl)" }, |
804 | { "-P, --prefix-builtins", "prefix builtins with m4_" }, | | 815 | { "-P, --prefix-builtins", "prefix builtins with m4_" }, |
805 | { "-Q, --quiet", "don't print warnings" }, | | 816 | { "-Q, --quiet", "don't print warnings" }, |
806 | { "-R, --reload-state=file", "restore state from file" }, | | 817 | { "-R, --reload-state=file", "restore state from file" }, |
807 | { "-Q, --silent", "don't print warnings" }, | | 818 | { "-Q, --silent", "don't print warnings" }, |
808 | { "-s, --synclines", "output line directives for cpp(1)" }, | | 819 | { "-s, --synclines", "output line directives for cpp(1)" }, |
809 | { "-t, --trace=macro", "trace the named macro" }, | | 820 | { "-t, --trace=macro", "trace the named macro" }, |
810 | { "-G, --traditional", "disable gnu extensions" }, | | 821 | { "-G, --traditional", "disable gnu extensions" }, |
811 | { "-U, --undefine=name", "undefine the named macro" }, | | 822 | { "-U, --undefine=name", "undefine the named macro" }, |
812 | { "-v, --version", "print the version number and exit" }, | | 823 | { "-v, --version", "print the version number and exit" }, |
813 | }; | | 824 | }; |
814 | | | 825 | |
815 | static void | | 826 | static void |
816 | help(void) | | 827 | help(void) |
817 | { | | 828 | { |
818 | size_t i; | | 829 | size_t i; |
819 | fprintf(stdout, "%s version %d\n\n", getprogname(), VERSION); | | 830 | fprintf(stdout, "%s version %d\n\n", getprogname(), VERSION); |
820 | usage(stdout); | | 831 | usage(stdout); |
821 | | | 832 | |
822 | fprintf(stdout, "\nThe long options are:\n"); | | 833 | fprintf(stdout, "\nThe long options are:\n"); |
823 | for (i = 0; i < __arraycount(nd); i++) | | 834 | for (i = 0; i < __arraycount(nd); i++) |
824 | fprintf(stdout, "\t%-25.25s\t%s\n", nd[i].n, nd[i].d); | | 835 | fprintf(stdout, "\t%-25.25s\t%s\n", nd[i].n, nd[i].d); |
825 | } | | 836 | } |