GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/m4/main.c Lines: 230 269 85.5 %
Date: 2017-11-13 Branches: 203 266 76.3 %

Line Branch Exec Source
1
/*	$OpenBSD: main.c,v 1.87 2017/06/15 13:48:42 bcallah Exp $	*/
2
/*	$NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $	*/
3
4
/*-
5
 * Copyright (c) 1989, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * This code is derived from software contributed to Berkeley by
9
 * Ozan Yigit at York University.
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
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
21
 *    without specific prior written permission.
22
 *
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
25
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
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
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
33
 * SUCH DAMAGE.
34
 */
35
36
/*
37
 * main.c
38
 * Facility: m4 macro processor
39
 * by: oz
40
 */
41
42
#include <assert.h>
43
#include <signal.h>
44
#include <err.h>
45
#include <errno.h>
46
#include <unistd.h>
47
#include <stdio.h>
48
#include <ctype.h>
49
#include <string.h>
50
#include <stddef.h>
51
#include <stdint.h>
52
#include <stdlib.h>
53
#include <ohash.h>
54
#include "mdef.h"
55
#include "stdd.h"
56
#include "extern.h"
57
#include "pathnames.h"
58
59
stae *mstack;			/* stack of m4 machine         */
60
char *sstack;			/* shadow stack, for string space extension */
61
static size_t STACKMAX;		/* current maximum size of stack */
62
int sp;				/* current m4  stack pointer   */
63
int fp;				/* m4 call frame pointer       */
64
struct input_file infile[MAXINP];/* input file stack (0=stdin)  */
65
FILE **outfile;			/* diversion array(0=bitbucket)*/
66
int maxout;
67
FILE *active;			/* active output file pointer  */
68
int ilevel = 0;			/* input file stack pointer    */
69
int oindex = 0;			/* diversion index..	       */
70
char *null = "";                /* as it says.. just a null..  */
71
char **m4wraps = NULL;		/* m4wraps array.	       */
72
int maxwraps = 0;		/* size of m4wraps array       */
73
int wrapindex = 0;		/* current offset in m4wraps   */
74
char lquote[MAXCCHARS+1] = {LQUOTE};	/* left quote character  (`)   */
75
char rquote[MAXCCHARS+1] = {RQUOTE};	/* right quote character (')   */
76
char scommt[MAXCCHARS+1] = {SCOMMT};	/* start character for comment */
77
char ecommt[MAXCCHARS+1] = {ECOMMT};	/* end character for comment   */
78
int  synch_lines = 0;		/* line synchronisation for C preprocessor */
79
int  prefix_builtins = 0;	/* -P option to prefix builtin keywords */
80
int  error_warns = 0;           /* -E option to make warnings exit_code = 1 */
81
int  fatal_warns = 0;		/* -E -E option to make warnings fatal */
82
83
struct keyblk {
84
        char    *knam;          /* keyword name */
85
        int     ktyp;           /* keyword type */
86
};
87
88
struct keyblk keywrds[] = {	/* m4 keywords to be installed */
89
	{ "include",      INCLTYPE },
90
	{ "sinclude",     SINCTYPE },
91
	{ "define",       DEFITYPE },
92
	{ "defn",         DEFNTYPE },
93
	{ "divert",       DIVRTYPE | NOARGS },
94
	{ "expr",         EXPRTYPE },
95
	{ "eval",         EXPRTYPE },
96
	{ "substr",       SUBSTYPE },
97
	{ "ifelse",       IFELTYPE },
98
	{ "ifdef",        IFDFTYPE },
99
	{ "len",          LENGTYPE },
100
	{ "incr",         INCRTYPE },
101
	{ "decr",         DECRTYPE },
102
	{ "dnl",          DNLNTYPE | NOARGS },
103
	{ "changequote",  CHNQTYPE | NOARGS },
104
	{ "changecom",    CHNCTYPE | NOARGS },
105
	{ "index",        INDXTYPE },
106
#ifdef EXTENDED
107
	{ "paste",        PASTTYPE },
108
	{ "spaste",       SPASTYPE },
109
	/* Newer extensions, needed to handle gnu-m4 scripts */
110
	{ "indir",        INDIRTYPE},
111
	{ "builtin",      BUILTINTYPE},
112
	{ "patsubst",	  PATSTYPE},
113
	{ "regexp",	  REGEXPTYPE},
114
	{ "esyscmd",	  ESYSCMDTYPE},
115
	{ "__file__",	  FILENAMETYPE | NOARGS},
116
	{ "__line__",	  LINETYPE | NOARGS},
117
#endif
118
	{ "popdef",       POPDTYPE },
119
	{ "pushdef",      PUSDTYPE },
120
	{ "dumpdef",      DUMPTYPE | NOARGS },
121
	{ "shift",        SHIFTYPE | NOARGS },
122
	{ "translit",     TRNLTYPE },
123
	{ "undefine",     UNDFTYPE },
124
	{ "undivert",     UNDVTYPE | NOARGS },
125
	{ "divnum",       DIVNTYPE | NOARGS },
126
	{ "maketemp",     MKTMTYPE },
127
	{ "mkstemp",      MKTMTYPE },
128
	{ "errprint",     ERRPTYPE | NOARGS },
129
	{ "m4wrap",       M4WRTYPE | NOARGS },
130
	{ "m4exit",       EXITTYPE | NOARGS },
131
	{ "syscmd",       SYSCTYPE },
132
	{ "sysval",       SYSVTYPE | NOARGS },
133
	{ "traceon",	  TRACEONTYPE | NOARGS },
134
	{ "traceoff",	  TRACEOFFTYPE | NOARGS },
135
136
	{ "unix",         SELFTYPE | NOARGS },
137
};
138
139
#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
140
141
extern int optind;
142
extern char *optarg;
143
144
#define MAXRECORD 50
145
static struct position {
146
	char *name;
147
	unsigned long line;
148
} quotes[MAXRECORD], paren[MAXRECORD];
149
150
static void record(struct position *, int);
151
static void dump_stack(struct position *, int);
152
153
static void macro(void);
154
static void initkwds(void);
155
static ndptr inspect(int, char *);
156
static int do_look_ahead(int, const char *);
157
static void reallyoutputstr(const char *);
158
static void reallyputchar(int);
159
160
static void enlarge_stack(void);
161
162
int main(int, char *[]);
163
164
int exit_code = 0;
165
166
int
167
main(int argc, char *argv[])
168
{
169
	int c;
170
	int n;
171
	char *p;
172
173
378
	if (pledge("stdio rpath wpath cpath tmppath proc exec flock", NULL) == -1)
174
		err(1, "pledge");
175
176
189
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
177
189
		signal(SIGINT, onintr);
178
179
189
	init_macros();
180
189
	initspaces();
181
189
	STACKMAX = INITSTACKMAX;
182
183
189
	mstack = xreallocarray(NULL, STACKMAX, sizeof(stae), NULL);
184
189
	sstack = xalloc(STACKMAX, NULL);
185
186
189
	maxout = 0;
187
189
	outfile = NULL;
188
189
	resizedivs(MAXOUT);
189
190
576
	while ((c = getopt(argc, argv, "gst:d:D:EU:o:I:P")) != -1)
191



198
		switch(c) {
192
193
		case 'D':               /* define something..*/
194
110
			for (p = optarg; *p; p++)
195
55
				if (*p == '=')
196
					break;
197
10
			if (*p)
198
10
				*p++ = EOS;
199
10
			dodefine(optarg, p);
200
10
			break;
201
		case 'E':               /* like GNU m4 1.4.9+ */
202
15
			if (error_warns == 0)
203
				error_warns = 1;
204
			else
205
				fatal_warns = 1;
206
15
			break;
207
		case 'I':
208
5
			addtoincludepath(optarg);
209
5
			break;
210
		case 'P':
211
9
			prefix_builtins = 1;
212
9
			break;
213
		case 'U':               /* undefine...       */
214
			macro_popdef(optarg);
215
			break;
216
		case 'g':
217
50
			mimic_gnu = 1;
218
50
			break;
219
		case 'd':
220
			set_trace_flags(optarg);
221
			break;
222
		case 's':
223
10
			synch_lines = 1;
224
10
			break;
225
		case 't':
226
			mark_traced(optarg, 1);
227
			break;
228
		case 'o':
229
			trace_file(optarg);
230
                        break;
231
		case '?':
232
			usage();
233
		}
234
235
189
        argc -= optind;
236
189
        argv += optind;
237
238
189
	initkwds();
239
189
	if (mimic_gnu)
240
50
		setup_builtin("format", FORMATTYPE);
241
242
189
	active = stdout;		/* default active output     */
243
189
	bbase[0] = bufbase;
244
189
        if (!argc) {
245
19
		sp = -1;		/* stack pointer initialized */
246
19
		fp = 0;			/* frame pointer initialized */
247
19
		set_input(infile+0, stdin, "stdin");
248
					/* default input (naturally) */
249
19
		macro();
250
19
	} else
251
680
		for (; argc--; ++argv) {
252
175
			p = *argv;
253

175
			if (p[0] == '-' && p[1] == EOS)
254
				set_input(infile, stdin, "stdin");
255
175
			else if (fopen_trypath(infile, p) == NULL)
256
				err(1, "%s", p);
257
175
			sp = -1;
258
175
			fp = 0;
259
175
			macro();
260
175
			release_input(infile);
261
		}
262
263
179
	if (wrapindex) {
264
		int i;
265
266
20
		ilevel = 0;		/* in case m4wrap includes.. */
267
20
		bufbase = bp = buf;	/* use the entire buffer   */
268
20
		if (mimic_gnu) {
269
30
			while (wrapindex != 0) {
270
60
				for (i = 0; i < wrapindex; i++)
271
20
					pbstr(m4wraps[i]);
272
10
				wrapindex =0;
273
10
				macro();
274
			}
275
		} else {
276
90
			for (i = 0; i < wrapindex; i++) {
277
30
				pbstr(m4wraps[i]);
278
30
				macro();
279
			}
280
		}
281
20
	}
282
283
179
	if (active != stdout)
284
		active = stdout;	/* reset output just in case */
285
3580
	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
286
1611
		if (outfile[n] != NULL)
287
			getdiv(n);
288
					/* remove bitbucket if used  */
289
179
	if (outfile[0] != NULL) {
290
		(void) fclose(outfile[0]);
291
	}
292
293
179
	return exit_code;
294
}
295
296
/*
297
 * Look ahead for `token'.
298
 * (on input `t == token[0]')
299
 * Used for comment and quoting delimiters.
300
 * Returns 1 if `token' present; copied to output.
301
 *         0 if `token' not found; all characters pushed back
302
 */
303
static int
304
do_look_ahead(int t, const char *token)
305
{
306
	int i;
307
308
21889508
	assert((unsigned char)t == (unsigned char)token[0]);
309
310
21915334
	for (i = 1; *++token; i++) {
311
46224
		t = gpbc();
312

30816
		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
313
2495
			pushback(t);
314
4990
			while (--i)
315
				pushback(*--token);
316
2495
			return 0;
317
		}
318
	}
319
10942259
	return 1;
320
10944754
}
321
322
#define LOOK_AHEAD(t, token) (t != EOF &&		\
323
    (unsigned char)(t)==(unsigned char)(token)[0] &&	\
324
    do_look_ahead(t,token))
325
326
/*
327
 * macro - the work horse..
328
 */
329
static void
330
macro(void)
331
{
332
468
	char token[MAXTOK+1];
333
	int t, l;
334
	ndptr p;
335
	int  nlpar;
336
337
40715998
	cycle {
338
122148009
		t = gpbc();
339
340

85687870
		if (LOOK_AHEAD(t,lquote)) {	/* strip quotes */
341
			nlpar = 0;
342
4254318
			record(quotes, nlpar++);
343
			/*
344
			 * Opening quote: scan forward until matching
345
			 * closing quote has been found.
346
			 */
347
4254318
			do {
348
349
189591444
				l = gpbc();
350

131864663
				if (LOOK_AHEAD(l,rquote)) {
351
5470012
					if (--nlpar > 0)
352
1215699
						outputstr(rquote);
353

116670326
				} else if (LOOK_AHEAD(l,lquote)) {
354
1215699
					record(quotes, nlpar++);
355
1215699
					outputstr(lquote);
356
57727136
				} else if (l == EOF) {
357
5
					if (nlpar == 1)
358
5
						warnx("unclosed quote:");
359
					else
360
						warnx("%d unclosed quotes:", nlpar);
361
					dump_stack(quotes, nlpar);
362
					exit(1);
363
				} else {
364
56511432
					if (nlpar > 0) {
365
56511432
						if (sp < 0)
366
10634
							reallyputchar(l);
367
						else
368
113001612
							CHRSAVE(l);
369
					}
370
				}
371
63197143
			}
372
63197143
			while (nlpar != 0);
373

37010102
		} else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
374
1095
			reallyoutputstr(scommt);
375
376
57240
			for(;;) {
377
171720
				t = gpbc();
378

115575
				if (LOOK_AHEAD(t, ecommt)) {
379
1095
					reallyoutputstr(ecommt);
380
1095
					break;
381
				}
382
56145
				if (t == EOF)
383
					break;
384
56145
				reallyputchar(t);
385
			}
386

72920998
		} else if (t == '_' || isalpha(t)) {
387
9797202
			p = inspect(t, token);
388
9797202
			if (p != NULL)
389
23697489
				pushback(l = gpbc());
390

20128632
			if (p == NULL || (l != LPAREN &&
391
2432267
			    (macro_getdef(p)->type & NEEDARGS) != 0))
392
1899034
				outputstr(token);
393
			else {
394
		/*
395
		 * real thing.. First build a call frame:
396
		 */
397
15796336
				pushf(fp);	/* previous call frm */
398
15796336
				pushf(macro_getdef(p)->type); /* type of the call  */
399

31592672
				pushf(is_traced(p));
400
15796336
				pushf(0);	/* parenthesis level */
401
7898168
				fp = sp;	/* new frame pointer */
402
		/*
403
		 * now push the string arguments:
404
		 */
405
15796336
				pushdef(p);			/* defn string */
406
15796336
				pushs1((char *)macro_name(p));	/* macro name  */
407
15796336
				pushs(ep);			/* start next..*/
408
409

10329440
				if (l != LPAREN && PARLEV == 0) {
410
				    /* no bracks  */
411
2431272
					chrsave(EOS);
412
413
2431272
					if (sp == STACKMAX)
414
						errx(1, "internal stack overflow");
415
4862544
					eval((const char **) mstack+fp+1, 2,
416
2431272
					    CALTYP, TRACESTATUS);
417
418
2431272
					ep = PREVEP;	/* flush strspace */
419
2431272
					sp = PREVSP;	/* previous sp..  */
420
2431272
					fp = PREVFP;	/* rewind stack...*/
421
2431272
				}
422
			}
423
26663388
		} else if (t == EOF) {
424
229
			if (!mimic_gnu /* you can puke right there */
425
229
			    && sp > -1 && ilevel <= 0) {
426
				warnx( "unexpected end of input, unclosed parenthesis:");
427
				dump_stack(paren, PARLEV);
428
				exit(1);
429
			}
430
229
			if (ilevel <= 0)
431
				break;			/* all done thanks.. */
432
5
			release_input(infile+ilevel--);
433
5
			emit_synchline();
434
5
			bufbase = bbase[ilevel];
435
5
			continue;
436
26663159
		} else if (sp < 0) {		/* not in a macro at all */
437
471568
			reallyputchar(t);	/* output directly..	 */
438
471568
		}
439
440

26191591
		else switch(t) {
441
442
		case LPAREN:
443
5466941
			if (PARLEV > 0)
444
45
				chrsave(t);
445

21871944
			while (isspace(l = gpbc())) /* skip blank, tab, nl.. */
446
1045
				if (PARLEV > 0)
447
23
					chrsave(l);
448
5466941
			pushback(l);
449
5466941
			record(paren, PARLEV++);
450
5466941
			break;
451
452
		case RPAREN:
453
5466941
			if (--PARLEV > 0)
454
45
				chrsave(t);
455
			else {			/* end of argument list */
456
5466896
				chrsave(EOS);
457
458
5466896
				if (sp == STACKMAX)
459
					errx(1, "internal stack overflow");
460
461
10933792
				eval((const char **) mstack+fp+1, sp-fp,
462
5466896
				    CALTYP, TRACESTATUS);
463
464
5466896
				ep = PREVEP;	/* flush strspace */
465
5466896
				sp = PREVSP;	/* previous sp..  */
466
5466896
				fp = PREVFP;	/* rewind stack...*/
467
			}
468
			break;
469
470
		case COMMA:
471
7288437
			if (PARLEV == 1) {
472
7288437
				chrsave(EOS);		/* new argument   */
473

29161188
				while (isspace(l = gpbc()))
474
					;
475
7288437
				pushback(l);
476
14576874
				pushs(ep);
477
7288437
			} else
478
				chrsave(t);
479
			break;
480
481
		default:
482

15938564
			if (LOOK_AHEAD(t, scommt)) {
483
				char *p;
484
80
				for (p = scommt; *p; p++)
485
20
					chrsave(*p);
486
1070
				for(;;) {
487
3210
					t = gpbc();
488

2160
					if (LOOK_AHEAD(t, ecommt)) {
489
80
						for (p = ecommt; *p; p++)
490
20
							chrsave(*p);
491
						break;
492
					}
493
1050
					if (t == EOF)
494
					    break;
495
2100
					CHRSAVE(t);
496
				}
497
20
			} else
498
15938504
				CHRSAVE(t);		/* stack the char */
499
			break;
500
		}
501
	}
502
224
}
503
504
/*
505
 * output string directly, without pushing it for reparses.
506
 */
507
void
508
outputstr(const char *s)
509
{
510
8660864
	if (sp < 0)
511
67588
		reallyoutputstr(s);
512
	else
513
34794886
		while (*s)
514
26269198
			CHRSAVE(*s++);
515
4330432
}
516
517
void
518
reallyoutputstr(const char *s)
519
{
520
139556
	if (synch_lines) {
521
4120
		while (*s) {
522
1650
			fputc(*s, active);
523
1650
			if (*s++ == '\n') {
524
10
				infile[ilevel].synch_lineno++;
525
20
				if (infile[ilevel].synch_lineno !=
526
10
				    infile[ilevel].lineno)
527
					do_emit_synchline();
528
			}
529
		}
530
	} else
531
69368
		fputs(s, active);
532
69778
}
533
534
void
535
reallyputchar(int c)
536
{
537
1615041
	putc(c, active);
538
538347
	if (synch_lines && c == '\n') {
539
90
		infile[ilevel].synch_lineno++;
540
90
		if (infile[ilevel].synch_lineno != infile[ilevel].lineno)
541
30
			do_emit_synchline();
542
	}
543
538347
}
544
545
/*
546
 * build an input token..
547
 * consider only those starting with _ or A-Za-z.
548
 */
549
static ndptr
550
inspect(int c, char *tp)
551
{
552
	char *name = tp;
553
19594404
	char *etp = tp+MAXTOK;
554
	ndptr p;
555
556
9797202
	*tp++ = c;
557
558

249691206
	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
559
33449421
		*tp++ = c;
560
9797202
	if (c != EOF)
561
19594394
		PUSHBACK(c);
562
9797202
	*tp = EOS;
563
	/* token is too long, it won't match anything, but it can still
564
	 * be output. */
565
9797202
	if (tp == ep) {
566
		outputstr(name);
567
		while (isalnum(c = gpbc()) || c == '_') {
568
			if (sp < 0)
569
				reallyputchar(c);
570
			else
571
				CHRSAVE(c);
572
		}
573
		*name = EOS;
574
		return NULL;
575
	}
576
577
9797202
	p = ohash_find(&macros, ohash_qlookupi(&macros, name, (const char **)&tp));
578
9797202
	if (p == NULL)
579
1898039
		return NULL;
580
7899163
	if (macro_getdef(p) == NULL)
581
		return NULL;
582
7899163
	return p;
583
9797202
}
584
585
/*
586
 * initkwds - initialise m4 keywords as fast as possible.
587
 * This very similar to install, but without certain overheads,
588
 * such as calling lookup. Malloc is not used for storing the
589
 * keyword strings, since we simply use the static pointers
590
 * within keywrds block.
591
 */
592
static void
593
initkwds(void)
594
{
595
	unsigned int type;
596
	int i;
597
598
17199
	for (i = 0; i < MAXKEYS; i++) {
599
8316
		type = keywrds[i].ktyp & TYPEMASK;
600
8316
		if ((keywrds[i].ktyp & NOARGS) == 0)
601
5103
			type |= NEEDARGS;
602
8316
		setup_builtin(keywrds[i].knam, type);
603
	}
604
189
}
605
606
static void
607
record(struct position *t, int lev)
608
{
609
21873916
	if (lev < MAXRECORD) {
610
10936958
		t[lev].name = CURRENT_NAME;
611
10936958
		t[lev].line = CURRENT_LINE;
612
10936958
	}
613
10936958
}
614
615
static void
616
dump_stack(struct position *t, int lev)
617
{
618
	int i;
619
620
25
	for (i = 0; i < lev; i++) {
621
5
		if (i == MAXRECORD) {
622
			fprintf(stderr, "   ...\n");
623
			break;
624
		}
625
5
		fprintf(stderr, "   %s at line %lu\n",
626
5
			t[i].name, t[i].line);
627
	}
628
5
}
629
630
631
static void
632
enlarge_stack(void)
633
{
634
	STACKMAX += STACKMAX/2;
635
	mstack = xreallocarray(mstack, STACKMAX, sizeof(stae),
636
	    "Evaluation stack overflow (%lu)",
637
	    (unsigned long)STACKMAX);
638
	sstack = xrealloc(sstack, STACKMAX,
639
	    "Evaluation stack overflow (%lu)",
640
	    (unsigned long)STACKMAX);
641
}