Wed Jun 24 16:49:30 2020 UTC ()
Try not to lose error output with --error-output.

Try to avoid the trap we set up ourselves while avoiding freopen(3).
When exit flushes and closes open streams it may close sfp first and
when it comes about to flush and close stderr, the descriptor is
already gone and we lose any buffered error output.  This actually
happens on some hosts, breaking --trace output used by autoconf.


(uwe)
diff -r1.48 -r1.49 src/usr.bin/m4/main.c

cvs diff -r1.48 -r1.49 src/usr.bin/m4/main.c (switch to unified diff)

--- src/usr.bin/m4/main.c 2019/03/26 16:41:06 1.48
+++ src/usr.bin/m4/main.c 2020/06/24 16:49:30 1.49
@@ -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
64ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ 64ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */
65stae *mstack; /* stack of m4 machine */ 65stae *mstack; /* stack of m4 machine */
66char *sstack; /* shadow stack, for string space extension */ 66char *sstack; /* shadow stack, for string space extension */
67static size_t STACKMAX; /* current maximum size of stack */ 67static size_t STACKMAX; /* current maximum size of stack */
68int sp; /* current m4 stack pointer */ 68int sp; /* current m4 stack pointer */
69int fp; /* m4 call frame pointer */ 69int fp; /* m4 call frame pointer */
70struct input_file infile[MAXINP];/* input file stack (0=stdin) */ 70struct input_file infile[MAXINP];/* input file stack (0=stdin) */
71FILE **outfile; /* diversion array(0=bitbucket)*/ 71FILE **outfile; /* diversion array(0=bitbucket)*/
72int maxout; 72int maxout;
73FILE *active; /* active output file pointer */ 73FILE *active; /* active output file pointer */
74int ilevel = 0; /* input file stack pointer */ 74int ilevel = 0; /* input file stack pointer */
75int oindex = 0; /* diversion index.. */ 75int oindex = 0; /* diversion index.. */
76const char *null = ""; /* as it says.. just a null.. */ 76const char *null = ""; /* as it says.. just a null.. */
77char **m4wraps = NULL; /* m4wraps array. */ 77char **m4wraps = NULL; /* m4wraps array. */
78int maxwraps = 0; /* size of m4wraps array */ 78int maxwraps = 0; /* size of m4wraps array */
79int wrapindex = 0; /* current offset in m4wraps */ 79int wrapindex = 0; /* current offset in m4wraps */
80char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */ 80char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */
81char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */ 81char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */
82char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */ 82char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */
83char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */ 83char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */
84int synch_lines = 0; /* line synchronisation for C preprocessor */ 84int synch_lines = 0; /* line synchronisation for C preprocessor */
85int prefix_builtins = 0; /* -P option to prefix builtin keywords */ 85int prefix_builtins = 0; /* -P option to prefix builtin keywords */
86int fatal_warnings = 0; /* -E option to exit on warnings */ 86int fatal_warnings = 0; /* -E option to exit on warnings */
87int quiet = 0; /* -Q option to silence warnings */ 87int quiet = 0; /* -Q option to silence warnings */
88int nesting_limit = -1; /* -L for nesting limit */ 88int nesting_limit = -1; /* -L for nesting limit */
89const char *freeze = NULL; /* -F to freeze state */ 89const char *freeze = NULL; /* -F to freeze state */
90const char *reload = NULL; /* -R to reload state */ 90const char *reload = NULL; /* -R to reload state */
91#ifndef REAL_FREEZE 91#ifndef REAL_FREEZE
92FILE *freezef = NULL; 92FILE *freezef = NULL;
93int thawing = 0; 93int thawing = 0;
94#endif 94#endif
95 95
96struct keyblk { 96struct 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
101struct keyblk keywrds[] = { /* m4 keywords to be installed */ 101struct 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
160static struct position { 160static 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
165static void record(struct position *, int); 165static void record(struct position *, int);
166static void dump_stack(struct position *, int); 166static void dump_stack(struct position *, int);
167 167
168static void macro(void); 168static void macro(void);
169static void initkwds(void); 169static void initkwds(void);
170static ndptr inspect(int, char *); 170static ndptr inspect(int, char *);
171static int do_look_ahead(int, const char *); 171static int do_look_ahead(int, const char *);
172static void reallyoutputstr(const char *); 172static void reallyoutputstr(const char *);
173static void reallyputchar(int); 173static void reallyputchar(int);
174 174
175static void enlarge_stack(void); 175static void enlarge_stack(void);
176static void help(void); 176static void help(void);
177 177
178static void 178static void
179usage(FILE *f) 179usage(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
187onintr(int signo) 187onintr(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
196struct option longopts[] = { 196struct 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
225int 225int
226main(int argc, char *argv[]) 226main(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 */
438static int 449static int
439do_look_ahead(int t, const char *token) 450do_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 */
464static void 475static void
465macro(void) 476macro(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 */
654void 665void
655outputstr(const char *s) 666outputstr(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
664void 675void
665reallyoutputstr(const char *s) 676reallyoutputstr(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
681void 692void
682reallyputchar(int c) 693reallyputchar(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 */
696static ndptr 707static ndptr
697inspect(int c, char *tp)  708inspect(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(&macros, ohash_qlookupi(&macros, name, (void *)&tp)); 735 p = ohash_find(&macros, ohash_qlookupi(&macros, 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 */
739static void 750static void
740initkwds(void) 751initkwds(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
753static void 764static void
754record(struct position *t, int lev) 765record(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
762static void 773static void
763dump_stack(struct position *t, int lev) 774dump_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
778static void  789static void
779enlarge_stack(void) 790enlarge_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
790static const struct { 801static 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
815static void 826static void
816help(void) 827help(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}