| @@ -1,806 +1,811 @@ | | | @@ -1,806 +1,811 @@ |
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.45 2016/01/16 21:12:27 christos Exp $ */ | | 2 | /* $NetBSD: main.c,v 1.46 2016/01/23 14:24:43 christos 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.45 2016/01/16 21:12:27 christos Exp $"); | | 45 | __RCSID("$NetBSD: main.c,v 1.46 2016/01/23 14:24:43 christos 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 | | | 231 | |
232 | setprogname(argv[0]); | | 232 | setprogname(argv[0]); |
233 | | | 233 | |
234 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) | | 234 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) |
235 | signal(SIGINT, onintr); | | 235 | signal(SIGINT, onintr); |
236 | | | 236 | |
237 | init_macros(); | | 237 | init_macros(); |
238 | initspaces(); | | 238 | initspaces(); |
239 | STACKMAX = INITSTACKMAX; | | 239 | STACKMAX = INITSTACKMAX; |
240 | | | 240 | |
241 | mstack = (stae *)xalloc(sizeof(stae) * STACKMAX, NULL); | | 241 | mstack = (stae *)xalloc(sizeof(stae) * STACKMAX, NULL); |
242 | sstack = (char *)xalloc(STACKMAX, NULL); | | 242 | sstack = (char *)xalloc(STACKMAX, NULL); |
243 | | | 243 | |
244 | maxout = 0; | | 244 | maxout = 0; |
245 | outfile = NULL; | | 245 | outfile = NULL; |
246 | resizedivs(MAXOUT); | | 246 | resizedivs(MAXOUT); |
247 | | | 247 | |
248 | while ((c = getopt_long(argc, argv, "D:d:e:EF:GgI:iL:o:PR:Qst:U:v", | | 248 | while ((c = getopt_long(argc, argv, "D:d:e:EF:GgI:iL:o:PR:Qst:U:v", |
249 | longopts, NULL)) != -1) | | 249 | longopts, NULL)) != -1) |
250 | switch(c) { | | 250 | switch(c) { |
251 | case 'D': /* define something..*/ | | 251 | case 'D': /* define something..*/ |
252 | for (p = optarg; *p; p++) | | 252 | for (p = optarg; *p; p++) |
253 | if (*p == '=') | | 253 | if (*p == '=') |
254 | break; | | 254 | break; |
255 | if (*p) | | 255 | if (*p) |
256 | *p++ = EOS; | | 256 | *p++ = EOS; |
257 | dodefine(optarg, p); | | 257 | dodefine(optarg, p); |
258 | break; | | 258 | break; |
259 | case 'd': | | 259 | case 'd': |
260 | set_trace_flags(optarg); | | 260 | set_trace_flags(optarg); |
261 | break; | | 261 | break; |
262 | case 'E': | | 262 | case 'E': |
263 | fatal_warnings++; | | 263 | fatal_warnings++; |
264 | break; | | 264 | break; |
265 | case 'e': | | 265 | case 'e': |
266 | if (freopen(optarg, "w+", stderr) == NULL) | | 266 | if (freopen(optarg, "w+", stderr) == NULL) |
267 | err(EXIT_FAILURE, "Can't redirect errors to `%s'", | | 267 | err(EXIT_FAILURE, "Can't redirect errors to `%s'", |
268 | optarg); | | 268 | optarg); |
269 | break; | | 269 | break; |
270 | case 'F': | | 270 | case 'F': |
271 | freeze = optarg; | | 271 | freeze = optarg; |
272 | #ifndef REAL_FREEZE | | 272 | #ifndef REAL_FREEZE |
273 | if ((freezef = fopen(freeze, "w")) == NULL) | | 273 | if ((freezef = fopen(freeze, "w")) == NULL) |
274 | err(EXIT_FAILURE, "Can't open `%s'", freeze); | | 274 | err(EXIT_FAILURE, "Can't open `%s'", freeze); |
275 | #endif | | 275 | #endif |
276 | break; | | 276 | break; |
277 | case 'I': | | 277 | case 'I': |
278 | addtoincludepath(optarg); | | 278 | addtoincludepath(optarg); |
279 | break; | | 279 | break; |
280 | case 'i': | | 280 | case 'i': |
281 | setvbuf(stdout, NULL, _IONBF, 0); | | 281 | setvbuf(stdout, NULL, _IONBF, 0); |
282 | signal(SIGINT, SIG_IGN); | | 282 | signal(SIGINT, SIG_IGN); |
283 | break; | | 283 | break; |
284 | case 'G': | | 284 | case 'G': |
285 | mimic_gnu = 0; | | 285 | mimic_gnu = 0; |
286 | break; | | 286 | break; |
287 | case 'g': | | 287 | case 'g': |
288 | mimic_gnu = 1; | | 288 | mimic_gnu = 1; |
289 | break; | | 289 | break; |
290 | case 'L': | | 290 | case 'L': |
291 | nesting_limit = atoi(optarg); | | 291 | nesting_limit = atoi(optarg); |
292 | break; | | 292 | break; |
293 | case 'o': | | 293 | case 'o': |
294 | trace_file(optarg); | | 294 | trace_file(optarg); |
295 | break; | | 295 | break; |
296 | case 'P': | | 296 | case 'P': |
297 | prefix_builtins = 1; | | 297 | prefix_builtins = 1; |
298 | break; | | 298 | break; |
299 | case 'Q': | | 299 | case 'Q': |
300 | quiet++; | | 300 | quiet++; |
301 | break; | | 301 | break; |
302 | case 'R': | | 302 | case 'R': |
303 | reload = optarg; | | 303 | reload = optarg; |
304 | break; | | 304 | break; |
305 | case 's': | | 305 | case 's': |
306 | synch_lines = 1; | | 306 | synch_lines = 1; |
307 | break; | | 307 | break; |
308 | case 't': | | 308 | case 't': |
309 | mark_traced(optarg, 1); | | 309 | mark_traced(optarg, 1); |
310 | break; | | 310 | break; |
311 | case 'U': /* undefine... */ | | 311 | case 'U': /* undefine... */ |
312 | macro_popdef(optarg); | | 312 | macro_popdef(optarg); |
313 | break; | | 313 | break; |
314 | case 'v': | | 314 | case 'v': |
315 | fprintf(stderr, "%s version %d\n", getprogname(), | | 315 | fprintf(stderr, "%s version %d\n", getprogname(), |
316 | VERSION); | | 316 | VERSION); |
317 | return EXIT_SUCCESS; | | 317 | return EXIT_SUCCESS; |
318 | case OPT_HELP: | | 318 | case OPT_HELP: |
319 | help(); | | 319 | help(); |
320 | return EXIT_SUCCESS; | | 320 | return EXIT_SUCCESS; |
321 | case '?': | | 321 | case '?': |
322 | default: | | 322 | default: |
323 | usage(stderr); | | 323 | usage(stderr); |
324 | return EXIT_FAILURE; | | 324 | return EXIT_FAILURE; |
325 | } | | 325 | } |
326 | | | 326 | |
327 | #ifdef REDIRECT | | 327 | #ifdef REDIRECT |
| | | 328 | /* |
| | | 329 | * This is meant only for debugging; it makes all output |
| | | 330 | * go to a known file, even if the command line options |
| | | 331 | * send it elsewhere. It should not be turned of in production code. |
| | | 332 | */ |
328 | if (freopen("/tmp/m4", "w+", stderr) == NULL) | | 333 | if (freopen("/tmp/m4", "w+", stderr) == NULL) |
329 | err(EXIT_FAILURE, "Can't redirect errors to `%s'", | | 334 | err(EXIT_FAILURE, "Can't redirect errors to `%s'", |
330 | "/tmp/m4"); | | 335 | "/tmp/m4"); |
331 | #endif | | 336 | #endif |
332 | argc -= optind; | | 337 | argc -= optind; |
333 | argv += optind; | | 338 | argv += optind; |
334 | | | 339 | |
335 | | | 340 | |
336 | initkwds(); | | 341 | initkwds(); |
337 | if (mimic_gnu) | | 342 | if (mimic_gnu) |
338 | setup_builtin("format", FORMATTYPE); | | 343 | setup_builtin("format", FORMATTYPE); |
339 | | | 344 | |
340 | active = stdout; /* default active output */ | | 345 | active = stdout; /* default active output */ |
341 | bbase[0] = bufbase; | | 346 | bbase[0] = bufbase; |
342 | | | 347 | |
343 | if (reload) { | | 348 | if (reload) { |
344 | #ifdef REAL_FREEZE | | 349 | #ifdef REAL_FREEZE |
345 | thaw_state(reload); | | 350 | thaw_state(reload); |
346 | #else | | 351 | #else |
347 | if (fopen_trypath(infile, reload) == NULL) | | 352 | if (fopen_trypath(infile, reload) == NULL) |
348 | err(1, "Can't open `%s'", reload); | | 353 | err(1, "Can't open `%s'", reload); |
349 | sp = -1; | | 354 | sp = -1; |
350 | fp = 0; | | 355 | fp = 0; |
351 | thawing = 1; | | 356 | thawing = 1; |
352 | macro(); | | 357 | macro(); |
353 | thawing = 0; | | 358 | thawing = 0; |
354 | release_input(infile); | | 359 | release_input(infile); |
355 | #endif | | 360 | #endif |
356 | } | | 361 | } |
357 | | | 362 | |
358 | if (!argc) { | | 363 | if (!argc) { |
359 | sp = -1; /* stack pointer initialized */ | | 364 | sp = -1; /* stack pointer initialized */ |
360 | fp = 0; /* frame pointer initialized */ | | 365 | fp = 0; /* frame pointer initialized */ |
361 | set_input(infile+0, stdin, "stdin"); | | 366 | set_input(infile+0, stdin, "stdin"); |
362 | /* default input (naturally) */ | | 367 | /* default input (naturally) */ |
363 | macro(); | | 368 | macro(); |
364 | } else | | 369 | } else |
365 | for (; argc--; ++argv) { | | 370 | for (; argc--; ++argv) { |
366 | p = *argv; | | 371 | p = *argv; |
367 | if (p[0] == '-' && p[1] == EOS) | | 372 | if (p[0] == '-' && p[1] == EOS) |
368 | set_input(infile, stdin, "stdin"); | | 373 | set_input(infile, stdin, "stdin"); |
369 | else if (fopen_trypath(infile, p) == NULL) | | 374 | else if (fopen_trypath(infile, p) == NULL) |
370 | err(1, "%s", p); | | 375 | err(1, "%s", p); |
371 | sp = -1; | | 376 | sp = -1; |
372 | fp = 0; | | 377 | fp = 0; |
373 | macro(); | | 378 | macro(); |
374 | release_input(infile); | | 379 | release_input(infile); |
375 | } | | 380 | } |
376 | | | 381 | |
377 | if (wrapindex) { | | 382 | if (wrapindex) { |
378 | int i; | | 383 | int i; |
379 | | | 384 | |
380 | ilevel = 0; /* in case m4wrap includes.. */ | | 385 | ilevel = 0; /* in case m4wrap includes.. */ |
381 | bufbase = bp = buf; /* use the entire buffer */ | | 386 | bufbase = bp = buf; /* use the entire buffer */ |
382 | if (mimic_gnu) { | | 387 | if (mimic_gnu) { |
383 | while (wrapindex != 0) { | | 388 | while (wrapindex != 0) { |
384 | for (i = 0; i < wrapindex; i++) | | 389 | for (i = 0; i < wrapindex; i++) |
385 | pbstr(m4wraps[i]); | | 390 | pbstr(m4wraps[i]); |
386 | wrapindex =0; | | 391 | wrapindex =0; |
387 | macro(); | | 392 | macro(); |
388 | } | | 393 | } |
389 | } else { | | 394 | } else { |
390 | for (i = 0; i < wrapindex; i++) { | | 395 | for (i = 0; i < wrapindex; i++) { |
391 | pbstr(m4wraps[i]); | | 396 | pbstr(m4wraps[i]); |
392 | macro(); | | 397 | macro(); |
393 | } | | 398 | } |
394 | } | | 399 | } |
395 | } | | 400 | } |
396 | | | 401 | |
397 | if (active != stdout) | | 402 | if (active != stdout) |
398 | active = stdout; /* reset output just in case */ | | 403 | active = stdout; /* reset output just in case */ |
399 | for (n = 1; n < maxout; n++) /* default wrap-up: undivert */ | | 404 | for (n = 1; n < maxout; n++) /* default wrap-up: undivert */ |
400 | if (outfile[n] != NULL) | | 405 | if (outfile[n] != NULL) |
401 | getdiv(n); | | 406 | getdiv(n); |
402 | /* remove bitbucket if used */ | | 407 | /* remove bitbucket if used */ |
403 | if (outfile[0] != NULL) { | | 408 | if (outfile[0] != NULL) { |
404 | (void) fclose(outfile[0]); | | 409 | (void) fclose(outfile[0]); |
405 | } | | 410 | } |
406 | | | 411 | |
407 | #ifdef REAL_FREEZE | | 412 | #ifdef REAL_FREEZE |
408 | if (freeze) | | 413 | if (freeze) |
409 | freeze_state(freeze); | | 414 | freeze_state(freeze); |
410 | #else | | 415 | #else |
411 | if (freezef) | | 416 | if (freezef) |
412 | fclose(freezef); | | 417 | fclose(freezef); |
413 | #endif | | 418 | #endif |
414 | | | 419 | |
415 | return 0; | | 420 | return 0; |
416 | } | | 421 | } |
417 | | | 422 | |
418 | /* | | 423 | /* |
419 | * Look ahead for `token'. | | 424 | * Look ahead for `token'. |
420 | * (on input `t == token[0]') | | 425 | * (on input `t == token[0]') |
421 | * Used for comment and quoting delimiters. | | 426 | * Used for comment and quoting delimiters. |
422 | * Returns 1 if `token' present; copied to output. | | 427 | * Returns 1 if `token' present; copied to output. |
423 | * 0 if `token' not found; all characters pushed back | | 428 | * 0 if `token' not found; all characters pushed back |
424 | */ | | 429 | */ |
425 | static int | | 430 | static int |
426 | do_look_ahead(int t, const char *token) | | 431 | do_look_ahead(int t, const char *token) |
427 | { | | 432 | { |
428 | int i; | | 433 | int i; |
429 | | | 434 | |
430 | assert((unsigned char)t == (unsigned char)token[0]); | | 435 | assert((unsigned char)t == (unsigned char)token[0]); |
431 | | | 436 | |
432 | for (i = 1; *++token; i++) { | | 437 | for (i = 1; *++token; i++) { |
433 | t = gpbc(); | | 438 | t = gpbc(); |
434 | if (t == EOF || (unsigned char)t != (unsigned char)*token) { | | 439 | if (t == EOF || (unsigned char)t != (unsigned char)*token) { |
435 | pushback(t); | | 440 | pushback(t); |
436 | while (--i) | | 441 | while (--i) |
437 | pushback(*--token); | | 442 | pushback(*--token); |
438 | return 0; | | 443 | return 0; |
439 | } | | 444 | } |
440 | } | | 445 | } |
441 | return 1; | | 446 | return 1; |
442 | } | | 447 | } |
443 | | | 448 | |
444 | #define LOOK_AHEAD(t, token) (t != EOF && \ | | 449 | #define LOOK_AHEAD(t, token) (t != EOF && \ |
445 | (unsigned char)(t)==(unsigned char)(token)[0] && \ | | 450 | (unsigned char)(t)==(unsigned char)(token)[0] && \ |
446 | do_look_ahead(t,token)) | | 451 | do_look_ahead(t,token)) |
447 | | | 452 | |
448 | /* | | 453 | /* |
449 | * macro - the work horse.. | | 454 | * macro - the work horse.. |
450 | */ | | 455 | */ |
451 | static void | | 456 | static void |
452 | macro(void) | | 457 | macro(void) |
453 | { | | 458 | { |
454 | char token[MAXTOK+1]; | | 459 | char token[MAXTOK+1]; |
455 | int t, l; | | 460 | int t, l; |
456 | ndptr p; | | 461 | ndptr p; |
457 | int nlpar; | | 462 | int nlpar; |
458 | | | 463 | |
459 | cycle { | | 464 | cycle { |
460 | t = gpbc(); | | 465 | t = gpbc(); |
461 | | | 466 | |
462 | if (LOOK_AHEAD(t,lquote)) { /* strip quotes */ | | 467 | if (LOOK_AHEAD(t,lquote)) { /* strip quotes */ |
463 | nlpar = 0; | | 468 | nlpar = 0; |
464 | record(quotes, nlpar++); | | 469 | record(quotes, nlpar++); |
465 | /* | | 470 | /* |
466 | * Opening quote: scan forward until matching | | 471 | * Opening quote: scan forward until matching |
467 | * closing quote has been found. | | 472 | * closing quote has been found. |
468 | */ | | 473 | */ |
469 | do { | | 474 | do { |
470 | | | 475 | |
471 | l = gpbc(); | | 476 | l = gpbc(); |
472 | if (LOOK_AHEAD(l,rquote)) { | | 477 | if (LOOK_AHEAD(l,rquote)) { |
473 | if (--nlpar > 0) | | 478 | if (--nlpar > 0) |
474 | outputstr(rquote); | | 479 | outputstr(rquote); |
475 | } else if (LOOK_AHEAD(l,lquote)) { | | 480 | } else if (LOOK_AHEAD(l,lquote)) { |
476 | record(quotes, nlpar++); | | 481 | record(quotes, nlpar++); |
477 | outputstr(lquote); | | 482 | outputstr(lquote); |
478 | } else if (l == EOF) { | | 483 | } else if (l == EOF) { |
479 | if (!quiet) { | | 484 | if (!quiet) { |
480 | if (nlpar == 1) | | 485 | if (nlpar == 1) |
481 | warnx("unclosed quote:"); | | 486 | warnx("unclosed quote:"); |
482 | else | | 487 | else |
483 | warnx( | | 488 | warnx( |
484 | "%d unclosed quotes:", | | 489 | "%d unclosed quotes:", |
485 | nlpar); | | 490 | nlpar); |
486 | dump_stack(quotes, nlpar); | | 491 | dump_stack(quotes, nlpar); |
487 | } | | 492 | } |
488 | exit(EXIT_FAILURE); | | 493 | exit(EXIT_FAILURE); |
489 | } else { | | 494 | } else { |
490 | if (nlpar > 0) { | | 495 | if (nlpar > 0) { |
491 | if (sp < 0) | | 496 | if (sp < 0) |
492 | reallyputchar(l); | | 497 | reallyputchar(l); |
493 | else | | 498 | else |
494 | CHRSAVE(l); | | 499 | CHRSAVE(l); |
495 | } | | 500 | } |
496 | } | | 501 | } |
497 | } | | 502 | } |
498 | while (nlpar != 0); | | 503 | while (nlpar != 0); |
499 | } else if (sp < 0 && LOOK_AHEAD(t, scommt)) { | | 504 | } else if (sp < 0 && LOOK_AHEAD(t, scommt)) { |
500 | reallyoutputstr(scommt); | | 505 | reallyoutputstr(scommt); |
501 | | | 506 | |
502 | for(;;) { | | 507 | for(;;) { |
503 | t = gpbc(); | | 508 | t = gpbc(); |
504 | if (LOOK_AHEAD(t, ecommt)) { | | 509 | if (LOOK_AHEAD(t, ecommt)) { |
505 | reallyoutputstr(ecommt); | | 510 | reallyoutputstr(ecommt); |
506 | break; | | 511 | break; |
507 | } | | 512 | } |
508 | if (t == EOF) | | 513 | if (t == EOF) |
509 | break; | | 514 | break; |
510 | reallyputchar(t); | | 515 | reallyputchar(t); |
511 | } | | 516 | } |
512 | } else if (t == '_' || isalpha(t)) { | | 517 | } else if (t == '_' || isalpha(t)) { |
513 | p = inspect(t, token); | | 518 | p = inspect(t, token); |
514 | if (p != NULL) | | 519 | if (p != NULL) |
515 | pushback(l = gpbc()); | | 520 | pushback(l = gpbc()); |
516 | if (p == NULL || (l != LPAREN && | | 521 | if (p == NULL || (l != LPAREN && |
517 | (macro_getdef(p)->type & NEEDARGS) != 0)) | | 522 | (macro_getdef(p)->type & NEEDARGS) != 0)) |
518 | outputstr(token); | | 523 | outputstr(token); |
519 | else { | | 524 | else { |
520 | /* | | 525 | /* |
521 | * real thing.. First build a call frame: | | 526 | * real thing.. First build a call frame: |
522 | */ | | 527 | */ |
523 | pushf(fp); /* previous call frm */ | | 528 | pushf(fp); /* previous call frm */ |
524 | pushf(macro_getdef(p)->type); /* type of the call */ | | 529 | pushf(macro_getdef(p)->type); /* type of the call */ |
525 | pushf(is_traced(p)); | | 530 | pushf(is_traced(p)); |
526 | pushf(0); /* parenthesis level */ | | 531 | pushf(0); /* parenthesis level */ |
527 | fp = sp; /* new frame pointer */ | | 532 | fp = sp; /* new frame pointer */ |
528 | /* | | 533 | /* |
529 | * now push the string arguments: | | 534 | * now push the string arguments: |
530 | */ | | 535 | */ |
531 | pushs1(macro_getdef(p)->defn); /* defn string */ | | 536 | pushs1(macro_getdef(p)->defn); /* defn string */ |
532 | pushs1((char *)macro_name(p)); /* macro name */ | | 537 | pushs1((char *)macro_name(p)); /* macro name */ |
533 | pushs(ep); /* start next..*/ | | 538 | pushs(ep); /* start next..*/ |
534 | | | 539 | |
535 | if (l != LPAREN && PARLEV == 0) { | | 540 | if (l != LPAREN && PARLEV == 0) { |
536 | /* no bracks */ | | 541 | /* no bracks */ |
537 | chrsave(EOS); | | 542 | chrsave(EOS); |
538 | | | 543 | |
539 | if ((size_t)sp == STACKMAX) | | 544 | if ((size_t)sp == STACKMAX) |
540 | errx(1, "internal stack overflow"); | | 545 | errx(1, "internal stack overflow"); |
541 | eval((const char **) mstack+fp+1, 2, | | 546 | eval((const char **) mstack+fp+1, 2, |
542 | CALTYP, TRACESTATUS); | | 547 | CALTYP, TRACESTATUS); |
543 | | | 548 | |
544 | ep = PREVEP; /* flush strspace */ | | 549 | ep = PREVEP; /* flush strspace */ |
545 | sp = PREVSP; /* previous sp.. */ | | 550 | sp = PREVSP; /* previous sp.. */ |
546 | fp = PREVFP; /* rewind stack...*/ | | 551 | fp = PREVFP; /* rewind stack...*/ |
547 | } | | 552 | } |
548 | } | | 553 | } |
549 | } else if (t == EOF) { | | 554 | } else if (t == EOF) { |
550 | if (sp > -1 && ilevel <= 0) { | | 555 | if (sp > -1 && ilevel <= 0) { |
551 | if (!quiet) { | | 556 | if (!quiet) { |
552 | warnx("unexpected end of input, " | | 557 | warnx("unexpected end of input, " |
553 | "unclosed parenthesis:"); | | 558 | "unclosed parenthesis:"); |
554 | dump_stack(paren, PARLEV); | | 559 | dump_stack(paren, PARLEV); |
555 | } | | 560 | } |
556 | exit(EXIT_FAILURE); | | 561 | exit(EXIT_FAILURE); |
557 | } | | 562 | } |
558 | if (ilevel <= 0) | | 563 | if (ilevel <= 0) |
559 | break; /* all done thanks.. */ | | 564 | break; /* all done thanks.. */ |
560 | release_input(infile+ilevel--); | | 565 | release_input(infile+ilevel--); |
561 | emit_synchline(); | | 566 | emit_synchline(); |
562 | bufbase = bbase[ilevel]; | | 567 | bufbase = bbase[ilevel]; |
563 | continue; | | 568 | continue; |
564 | } else if (sp < 0) { /* not in a macro at all */ | | 569 | } else if (sp < 0) { /* not in a macro at all */ |
565 | reallyputchar(t); /* output directly.. */ | | 570 | reallyputchar(t); /* output directly.. */ |
566 | } | | 571 | } |
567 | | | 572 | |
568 | else switch(t) { | | 573 | else switch(t) { |
569 | | | 574 | |
570 | case LPAREN: | | 575 | case LPAREN: |
571 | if (PARLEV > 0) | | 576 | if (PARLEV > 0) |
572 | chrsave(t); | | 577 | chrsave(t); |
573 | while (isspace(l = gpbc())) /* skip blank, tab, nl.. */ | | 578 | while (isspace(l = gpbc())) /* skip blank, tab, nl.. */ |
574 | if (PARLEV > 0) | | 579 | if (PARLEV > 0) |
575 | chrsave(l); | | 580 | chrsave(l); |
576 | pushback(l); | | 581 | pushback(l); |
577 | record(paren, PARLEV++); | | 582 | record(paren, PARLEV++); |
578 | break; | | 583 | break; |
579 | | | 584 | |
580 | case RPAREN: | | 585 | case RPAREN: |
581 | if (--PARLEV > 0) | | 586 | if (--PARLEV > 0) |
582 | chrsave(t); | | 587 | chrsave(t); |
583 | else { /* end of argument list */ | | 588 | else { /* end of argument list */ |
584 | chrsave(EOS); | | 589 | chrsave(EOS); |
585 | | | 590 | |
586 | if ((size_t)sp == STACKMAX) | | 591 | if ((size_t)sp == STACKMAX) |
587 | errx(1, "internal stack overflow"); | | 592 | errx(1, "internal stack overflow"); |
588 | | | 593 | |
589 | eval((const char **) mstack+fp+1, sp-fp, | | 594 | eval((const char **) mstack+fp+1, sp-fp, |
590 | CALTYP, TRACESTATUS); | | 595 | CALTYP, TRACESTATUS); |
591 | | | 596 | |
592 | ep = PREVEP; /* flush strspace */ | | 597 | ep = PREVEP; /* flush strspace */ |
593 | sp = PREVSP; /* previous sp.. */ | | 598 | sp = PREVSP; /* previous sp.. */ |
594 | fp = PREVFP; /* rewind stack...*/ | | 599 | fp = PREVFP; /* rewind stack...*/ |
595 | } | | 600 | } |
596 | break; | | 601 | break; |
597 | | | 602 | |
598 | case COMMA: | | 603 | case COMMA: |
599 | if (PARLEV == 1) { | | 604 | if (PARLEV == 1) { |
600 | chrsave(EOS); /* new argument */ | | 605 | chrsave(EOS); /* new argument */ |
601 | while (isspace(l = gpbc())) | | 606 | while (isspace(l = gpbc())) |
602 | ; | | 607 | ; |
603 | pushback(l); | | 608 | pushback(l); |
604 | pushs(ep); | | 609 | pushs(ep); |
605 | } else | | 610 | } else |
606 | chrsave(t); | | 611 | chrsave(t); |
607 | break; | | 612 | break; |
608 | | | 613 | |
609 | default: | | 614 | default: |
610 | if (LOOK_AHEAD(t, scommt)) { | | 615 | if (LOOK_AHEAD(t, scommt)) { |
611 | char *q; | | 616 | char *q; |
612 | for (q = scommt; *q; q++) | | 617 | for (q = scommt; *q; q++) |
613 | chrsave(*q); | | 618 | chrsave(*q); |
614 | for(;;) { | | 619 | for(;;) { |
615 | t = gpbc(); | | 620 | t = gpbc(); |
616 | if (LOOK_AHEAD(t, ecommt)) { | | 621 | if (LOOK_AHEAD(t, ecommt)) { |
617 | for (q = ecommt; *q; q++) | | 622 | for (q = ecommt; *q; q++) |
618 | chrsave(*q); | | 623 | chrsave(*q); |
619 | break; | | 624 | break; |
620 | } | | 625 | } |
621 | if (t == EOF) | | 626 | if (t == EOF) |
622 | break; | | 627 | break; |
623 | CHRSAVE(t); | | 628 | CHRSAVE(t); |
624 | } | | 629 | } |
625 | } else | | 630 | } else |
626 | CHRSAVE(t); /* stack the char */ | | 631 | CHRSAVE(t); /* stack the char */ |
627 | break; | | 632 | break; |
628 | } | | 633 | } |
629 | } | | 634 | } |
630 | } | | 635 | } |
631 | | | 636 | |
632 | /* | | 637 | /* |
633 | * output string directly, without pushing it for reparses. | | 638 | * output string directly, without pushing it for reparses. |
634 | */ | | 639 | */ |
635 | void | | 640 | void |
636 | outputstr(const char *s) | | 641 | outputstr(const char *s) |
637 | { | | 642 | { |
638 | if (sp < 0) | | 643 | if (sp < 0) |
639 | reallyoutputstr(s); | | 644 | reallyoutputstr(s); |
640 | else | | 645 | else |
641 | while (*s) | | 646 | while (*s) |
642 | CHRSAVE(*s++); | | 647 | CHRSAVE(*s++); |
643 | } | | 648 | } |
644 | | | 649 | |
645 | void | | 650 | void |
646 | reallyoutputstr(const char *s) | | 651 | reallyoutputstr(const char *s) |
647 | { | | 652 | { |
648 | if (synch_lines) { | | 653 | if (synch_lines) { |
649 | while (*s) { | | 654 | while (*s) { |
650 | fputc(*s, active); | | 655 | fputc(*s, active); |
651 | if (*s++ == '\n') { | | 656 | if (*s++ == '\n') { |
652 | infile[ilevel].synch_lineno++; | | 657 | infile[ilevel].synch_lineno++; |
653 | if (infile[ilevel].synch_lineno != | | 658 | if (infile[ilevel].synch_lineno != |
654 | infile[ilevel].lineno) | | 659 | infile[ilevel].lineno) |
655 | do_emit_synchline(); | | 660 | do_emit_synchline(); |
656 | } | | 661 | } |
657 | } | | 662 | } |
658 | } else | | 663 | } else |
659 | fputs(s, active); | | 664 | fputs(s, active); |
660 | } | | 665 | } |
661 | | | 666 | |
662 | void | | 667 | void |
663 | reallyputchar(int c) | | 668 | reallyputchar(int c) |
664 | { | | 669 | { |
665 | putc(c, active); | | 670 | putc(c, active); |
666 | if (synch_lines && c == '\n') { | | 671 | if (synch_lines && c == '\n') { |
667 | infile[ilevel].synch_lineno++; | | 672 | infile[ilevel].synch_lineno++; |
668 | if (infile[ilevel].synch_lineno != infile[ilevel].lineno) | | 673 | if (infile[ilevel].synch_lineno != infile[ilevel].lineno) |
669 | do_emit_synchline(); | | 674 | do_emit_synchline(); |
670 | } | | 675 | } |
671 | } | | 676 | } |
672 | | | 677 | |
673 | /* | | 678 | /* |
674 | * build an input token.. | | 679 | * build an input token.. |
675 | * consider only those starting with _ or A-Za-z. | | 680 | * consider only those starting with _ or A-Za-z. |
676 | */ | | 681 | */ |
677 | static ndptr | | 682 | static ndptr |
678 | inspect(int c, char *tp) | | 683 | inspect(int c, char *tp) |
679 | { | | 684 | { |
680 | char *name = tp; | | 685 | char *name = tp; |
681 | char *etp = tp+MAXTOK; | | 686 | char *etp = tp+MAXTOK; |
682 | ndptr p; | | 687 | ndptr p; |
683 | | | 688 | |
684 | *tp++ = c; | | 689 | *tp++ = c; |
685 | | | 690 | |
686 | while ((isalnum(c = gpbc()) || c == '_') && tp < etp) | | 691 | while ((isalnum(c = gpbc()) || c == '_') && tp < etp) |
687 | *tp++ = c; | | 692 | *tp++ = c; |
688 | if (c != EOF) | | 693 | if (c != EOF) |
689 | PUSHBACK(c); | | 694 | PUSHBACK(c); |
690 | *tp = EOS; | | 695 | *tp = EOS; |
691 | /* token is too long, it won't match anything, but it can still | | 696 | /* token is too long, it won't match anything, but it can still |
692 | * be output. */ | | 697 | * be output. */ |
693 | if (tp == ep) { | | 698 | if (tp == ep) { |
694 | outputstr(name); | | 699 | outputstr(name); |
695 | while (isalnum(c = gpbc()) || c == '_') { | | 700 | while (isalnum(c = gpbc()) || c == '_') { |
696 | if (sp < 0) | | 701 | if (sp < 0) |
697 | reallyputchar(c); | | 702 | reallyputchar(c); |
698 | else | | 703 | else |
699 | CHRSAVE(c); | | 704 | CHRSAVE(c); |
700 | } | | 705 | } |
701 | *name = EOS; | | 706 | *name = EOS; |
702 | return NULL; | | 707 | return NULL; |
703 | } | | 708 | } |
704 | | | 709 | |
705 | p = ohash_find(¯os, ohash_qlookupi(¯os, name, (void *)&tp)); | | 710 | p = ohash_find(¯os, ohash_qlookupi(¯os, name, (void *)&tp)); |
706 | if (p == NULL) | | 711 | if (p == NULL) |
707 | return NULL; | | 712 | return NULL; |
708 | if (macro_getdef(p) == NULL) | | 713 | if (macro_getdef(p) == NULL) |
709 | return NULL; | | 714 | return NULL; |
710 | return p; | | 715 | return p; |
711 | } | | 716 | } |
712 | | | 717 | |
713 | /* | | 718 | /* |
714 | * initkwds - initialise m4 keywords as fast as possible. | | 719 | * initkwds - initialise m4 keywords as fast as possible. |
715 | * This very similar to install, but without certain overheads, | | 720 | * This very similar to install, but without certain overheads, |
716 | * such as calling lookup. Malloc is not used for storing the | | 721 | * such as calling lookup. Malloc is not used for storing the |
717 | * keyword strings, since we simply use the static pointers | | 722 | * keyword strings, since we simply use the static pointers |
718 | * within keywrds block. | | 723 | * within keywrds block. |
719 | */ | | 724 | */ |
720 | static void | | 725 | static void |
721 | initkwds(void) | | 726 | initkwds(void) |
722 | { | | 727 | { |
723 | unsigned int type; | | 728 | unsigned int type; |
724 | size_t i; | | 729 | size_t i; |
725 | | | 730 | |
726 | for (i = 0; i < MAXKEYS; i++) { | | 731 | for (i = 0; i < MAXKEYS; i++) { |
727 | type = keywrds[i].ktyp; | | 732 | type = keywrds[i].ktyp; |
728 | if ((keywrds[i].ktyp & NOARGS) == 0) | | 733 | if ((keywrds[i].ktyp & NOARGS) == 0) |
729 | type |= NEEDARGS; | | 734 | type |= NEEDARGS; |
730 | setup_builtin(keywrds[i].knam, type); | | 735 | setup_builtin(keywrds[i].knam, type); |
731 | } | | 736 | } |
732 | } | | 737 | } |
733 | | | 738 | |
734 | static void | | 739 | static void |
735 | record(struct position *t, int lev) | | 740 | record(struct position *t, int lev) |
736 | { | | 741 | { |
737 | if (lev < MAXRECORD) { | | 742 | if (lev < MAXRECORD) { |
738 | t[lev].name = CURRENT_NAME; | | 743 | t[lev].name = CURRENT_NAME; |
739 | t[lev].line = CURRENT_LINE; | | 744 | t[lev].line = CURRENT_LINE; |
740 | } | | 745 | } |
741 | } | | 746 | } |
742 | | | 747 | |
743 | static void | | 748 | static void |
744 | dump_stack(struct position *t, int lev) | | 749 | dump_stack(struct position *t, int lev) |
745 | { | | 750 | { |
746 | int i; | | 751 | int i; |
747 | | | 752 | |
748 | for (i = 0; i < lev; i++) { | | 753 | for (i = 0; i < lev; i++) { |
749 | if (i == MAXRECORD) { | | 754 | if (i == MAXRECORD) { |
750 | fprintf(stderr, " ...\n"); | | 755 | fprintf(stderr, " ...\n"); |
751 | break; | | 756 | break; |
752 | } | | 757 | } |
753 | fprintf(stderr, " %s at line %lu\n", | | 758 | fprintf(stderr, " %s at line %lu\n", |
754 | t[i].name, t[i].line); | | 759 | t[i].name, t[i].line); |
755 | } | | 760 | } |
756 | } | | 761 | } |
757 | | | 762 | |
758 | | | 763 | |
759 | static void | | 764 | static void |
760 | enlarge_stack(void) | | 765 | enlarge_stack(void) |
761 | { | | 766 | { |
762 | STACKMAX += STACKMAX/2; | | 767 | STACKMAX += STACKMAX/2; |
763 | mstack = xrealloc(mstack, sizeof(stae) * STACKMAX, | | 768 | mstack = xrealloc(mstack, sizeof(stae) * STACKMAX, |
764 | "Evaluation stack overflow (%lu)", | | 769 | "Evaluation stack overflow (%lu)", |
765 | (unsigned long)STACKMAX); | | 770 | (unsigned long)STACKMAX); |
766 | sstack = xrealloc(sstack, STACKMAX, | | 771 | sstack = xrealloc(sstack, STACKMAX, |
767 | "Evaluation stack overflow (%lu)", | | 772 | "Evaluation stack overflow (%lu)", |
768 | (unsigned long)STACKMAX); | | 773 | (unsigned long)STACKMAX); |
769 | } | | 774 | } |
770 | | | 775 | |
771 | static const struct { | | 776 | static const struct { |
772 | const char *n; | | 777 | const char *n; |
773 | const char *d; | | 778 | const char *d; |
774 | } nd [] = { | | 779 | } nd [] = { |
775 | { "-d, --debug[=flags]", "set debug flags" }, | | 780 | { "-d, --debug[=flags]", "set debug flags" }, |
776 | { "-D, --define=name[=value]", "define macro" }, | | 781 | { "-D, --define=name[=value]", "define macro" }, |
777 | { "-e, --error-output=file", "send error output to file" }, | | 782 | { "-e, --error-output=file", "send error output to file" }, |
778 | { "-E, --fatal-warnings", "exit on warnings" }, | | 783 | { "-E, --fatal-warnings", "exit on warnings" }, |
779 | { "-F, --freeze-state=file", "save state to file" }, | | 784 | { "-F, --freeze-state=file", "save state to file" }, |
780 | { "-g, --gnu", "enable gnu extensions" }, | | 785 | { "-g, --gnu", "enable gnu extensions" }, |
781 | { " --help", "print this message and exit" }, | | 786 | { " --help", "print this message and exit" }, |
782 | { "-I, --include=file", "include file" }, | | 787 | { "-I, --include=file", "include file" }, |
783 | { "-i, --interactive", "unbuffer output, ignore tty signals" }, | | 788 | { "-i, --interactive", "unbuffer output, ignore tty signals" }, |
784 | { "-L, --nesting-limit=num", "macro expansion nesting limit (unimpl)" }, | | 789 | { "-L, --nesting-limit=num", "macro expansion nesting limit (unimpl)" }, |
785 | { "-P, --prefix-builtins", "prefix builtins with m4_" }, | | 790 | { "-P, --prefix-builtins", "prefix builtins with m4_" }, |
786 | { "-Q, --quiet", "don't print warnings" }, | | 791 | { "-Q, --quiet", "don't print warnings" }, |
787 | { "-R, --reload-state=file", "restore state from file" }, | | 792 | { "-R, --reload-state=file", "restore state from file" }, |
788 | { "-Q, --silent", "don't print warnings" }, | | 793 | { "-Q, --silent", "don't print warnings" }, |
789 | { "-s, --synclines", "output line directives for cpp(1)" }, | | 794 | { "-s, --synclines", "output line directives for cpp(1)" }, |
790 | { "-t, --trace=macro", "trace the named macro" }, | | 795 | { "-t, --trace=macro", "trace the named macro" }, |
791 | { "-G, --traditional", "disable gnu extensions" }, | | 796 | { "-G, --traditional", "disable gnu extensions" }, |
792 | { "-U, --undefine=name", "undefine the named macro" }, | | 797 | { "-U, --undefine=name", "undefine the named macro" }, |
793 | { "-v, --version", "print the version number and exit" }, | | 798 | { "-v, --version", "print the version number and exit" }, |
794 | }; | | 799 | }; |
795 | | | 800 | |
796 | static void | | 801 | static void |
797 | help(void) | | 802 | help(void) |
798 | { | | 803 | { |
799 | size_t i; | | 804 | size_t i; |
800 | fprintf(stdout, "%s version %d\n\n", getprogname(), VERSION); | | 805 | fprintf(stdout, "%s version %d\n\n", getprogname(), VERSION); |
801 | usage(stdout); | | 806 | usage(stdout); |
802 | | | 807 | |
803 | fprintf(stdout, "\nThe long options are:\n"); | | 808 | fprintf(stdout, "\nThe long options are:\n"); |
804 | for (i = 0; i < __arraycount(nd); i++) | | 809 | for (i = 0; i < __arraycount(nd); i++) |
805 | fprintf(stdout, "\t%-25.25s\t%s\n", nd[i].n, nd[i].d); | | 810 | fprintf(stdout, "\t%-25.25s\t%s\n", nd[i].n, nd[i].d); |
806 | } | | 811 | } |