GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/m4/main.c Lines: 231 269 85.9 %
Date: 2017-11-07 Branches: 205 266 77.1 %

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
668
	if (pledge("stdio rpath wpath cpath tmppath proc exec flock", NULL) == -1)
174
		err(1, "pledge");
175
176
334
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
177
334
		signal(SIGINT, onintr);
178
179
334
	init_macros();
180
334
	initspaces();
181
334
	STACKMAX = INITSTACKMAX;
182
183
334
	mstack = xreallocarray(NULL, STACKMAX, sizeof(stae), NULL);
184
334
	sstack = xalloc(STACKMAX, NULL);
185
186
334
	maxout = 0;
187
334
	outfile = NULL;
188
334
	resizedivs(MAXOUT);
189
190
849
	while ((c = getopt(argc, argv, "gst:d:D:EU:o:I:P")) != -1)
191



362
		switch(c) {
192
193
		case 'D':               /* define something..*/
194
198
			for (p = optarg; *p; p++)
195
99
				if (*p == '=')
196
					break;
197
18
			if (*p)
198
18
				*p++ = EOS;
199
18
			dodefine(optarg, p);
200
18
			break;
201
		case 'E':               /* like GNU m4 1.4.9+ */
202
27
			if (error_warns == 0)
203
				error_warns = 1;
204
			else
205
				fatal_warns = 1;
206
27
			break;
207
		case 'I':
208
9
			addtoincludepath(optarg);
209
9
			break;
210
		case 'P':
211
19
			prefix_builtins = 1;
212
19
			break;
213
		case 'U':               /* undefine...       */
214
			macro_popdef(optarg);
215
			break;
216
		case 'g':
217
90
			mimic_gnu = 1;
218
90
			break;
219
		case 'd':
220
			set_trace_flags(optarg);
221
			break;
222
		case 's':
223
18
			synch_lines = 1;
224
18
			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
334
        argc -= optind;
236
334
        argv += optind;
237
238
334
	initkwds();
239
334
	if (mimic_gnu)
240
90
		setup_builtin("format", FORMATTYPE);
241
242
334
	active = stdout;		/* default active output     */
243
334
	bbase[0] = bufbase;
244
334
        if (!argc) {
245
37
		sp = -1;		/* stack pointer initialized */
246
37
		fp = 0;			/* frame pointer initialized */
247
37
		set_input(infile+0, stdin, "stdin");
248
					/* default input (naturally) */
249
37
		macro();
250
37
	} else
251
891
		for (; argc--; ++argv) {
252
306
			p = *argv;
253

306
			if (p[0] == '-' && p[1] == EOS)
254
				set_input(infile, stdin, "stdin");
255
306
			else if (fopen_trypath(infile, p) == NULL)
256
				err(1, "%s", p);
257
306
			sp = -1;
258
306
			fp = 0;
259
306
			macro();
260
306
			release_input(infile);
261
		}
262
263
316
	if (wrapindex) {
264
		int i;
265
266
36
		ilevel = 0;		/* in case m4wrap includes.. */
267
36
		bufbase = bp = buf;	/* use the entire buffer   */
268
36
		if (mimic_gnu) {
269
45
			while (wrapindex != 0) {
270
108
				for (i = 0; i < wrapindex; i++)
271
36
					pbstr(m4wraps[i]);
272
18
				wrapindex =0;
273
18
				macro();
274
			}
275
		} else {
276
162
			for (i = 0; i < wrapindex; i++) {
277
54
				pbstr(m4wraps[i]);
278
54
				macro();
279
			}
280
		}
281
36
	}
282
283
316
	if (active != stdout)
284
		active = stdout;	/* reset output just in case */
285
6320
	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
286
2844
		if (outfile[n] != NULL)
287
			getdiv(n);
288
					/* remove bitbucket if used  */
289
316
	if (outfile[0] != NULL) {
290
		(void) fclose(outfile[0]);
291
	}
292
293
316
	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
39418678
	assert((unsigned char)t == (unsigned char)token[0]);
309
310
39481128
	for (i = 1; *++token; i++) {
311
109515
		t = gpbc();
312

73010
		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
313
5280
			pushback(t);
314
10560
			while (--i)
315
				pushback(*--token);
316
5280
			return 0;
317
		}
318
	}
319
19704059
	return 1;
320
19709339
}
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
830
	char token[MAXTOK+1];
333
	int t, l;
334
	ndptr p;
335
	int  nlpar;
336
337
415
	cycle {
338
219631377
		t = gpbc();
339
340

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

237484587
				if (LOOK_AHEAD(l,rquote)) {
351
9850018
					if (--nlpar > 0)
352
2189471
						outputstr(rquote);
353

210124006
				} else if (LOOK_AHEAD(l,lquote)) {
354
2189471
					record(quotes, nlpar++);
355
2189471
					outputstr(lquote);
356
103966804
				} else if (l == EOF) {
357
9
					if (nlpar == 1)
358
9
						warnx("unclosed quote:");
359
					else
360
						warnx("%d unclosed quotes:", nlpar);
361
					dump_stack(quotes, nlpar);
362
					exit(1);
363
				} else {
364
101777324
					if (nlpar > 0) {
365
101777324
						if (sp < 0)
366
25914
							reallyputchar(l);
367
						else
368
203502854
							CHRSAVE(l);
369
					}
370
				}
371
113816813
			}
372
113816813
			while (nlpar != 0);
373

66444697
		} else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
374
1971
			reallyoutputstr(scommt);
375
376
103032
			for(;;) {
377
309096
				t = gpbc();
378

208035
				if (LOOK_AHEAD(t, ecommt)) {
379
1971
					reallyoutputstr(ecommt);
380
1971
					break;
381
				}
382
101061
				if (t == EOF)
383
					break;
384
101061
				reallyputchar(t);
385
			}
386

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

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

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

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

47154478
		else switch(t) {
441
442
		case LPAREN:
443
9842675
			if (PARLEV > 0)
444
107
				chrsave(t);
445

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

52500724
				while (isspace(l = gpbc()))
474
					;
475
13122139
				pushback(l);
476
26244278
				pushs(ep);
477
13122139
			} else
478
2
				chrsave(t);
479
			break;
480
481
		default:
482

28694010
			if (LOOK_AHEAD(t, scommt)) {
483
				char *p;
484
144
				for (p = scommt; *p; p++)
485
36
					chrsave(*p);
486
1890
				for(;;) {
487
5778
					t = gpbc();
488

3888
					if (LOOK_AHEAD(t, ecommt)) {
489
144
						for (p = ecommt; *p; p++)
490
36
							chrsave(*p);
491
						break;
492
					}
493
1890
					if (t == EOF)
494
					    break;
495
3780
					CHRSAVE(t);
496
				}
497
36
			} else
498
28693902
				CHRSAVE(t);		/* stack the char */
499
			break;
500
		}
501
	}
502
397
}
503
504
/*
505
 * output string directly, without pushing it for reparses.
506
 */
507
void
508
outputstr(const char *s)
509
{
510
15621238
	if (sp < 0)
511
133805
		reallyoutputstr(s);
512
	else
513
54991270
		while (*s)
514
47314457
			CHRSAVE(*s++);
515
7810619
}
516
517
void
518
reallyoutputstr(const char *s)
519
{
520
275494
	if (synch_lines) {
521
3708
		while (*s) {
522
2970
			fputc(*s, active);
523
2970
			if (*s++ == '\n') {
524
18
				infile[ilevel].synch_lineno++;
525
36
				if (infile[ilevel].synch_lineno !=
526
18
				    infile[ilevel].lineno)
527
					do_emit_synchline();
528
			}
529
		}
530
	} else
531
137009
		fputs(s, active);
532
137747
}
533
534
void
535
reallyputchar(int c)
536
{
537
2602977
	putc(c, active);
538
867659
	if (synch_lines && c == '\n') {
539
162
		infile[ilevel].synch_lineno++;
540
162
		if (infile[ilevel].synch_lineno != infile[ilevel].lineno)
541
54
			do_emit_synchline();
542
	}
543
867659
}
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
35304728
	char *etp = tp+MAXTOK;
554
	ndptr p;
555
556
17652364
	*tp++ = c;
557
558

450151538
	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
559
60312352
		*tp++ = c;
560
17652364
	if (c != EOF)
561
35304710
		PUSHBACK(c);
562
17652364
	*tp = EOS;
563
	/* token is too long, it won't match anything, but it can still
564
	 * be output. */
565
17652364
	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
17652364
	p = ohash_find(&macros, ohash_qlookupi(&macros, name, (const char **)&tp));
578
17652364
	if (p == NULL)
579
3429886
		return NULL;
580
14222478
	if (macro_getdef(p) == NULL)
581
		return NULL;
582
14222478
	return p;
583
17652364
}
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
30394
	for (i = 0; i < MAXKEYS; i++) {
599
14696
		type = keywrds[i].ktyp & TYPEMASK;
600
14696
		if ((keywrds[i].ktyp & NOARGS) == 0)
601
9018
			type |= NEEDARGS;
602
14696
		setup_builtin(keywrds[i].knam, type);
603
	}
604
334
}
605
606
static void
607
record(struct position *t, int lev)
608
{
609
39385404
	if (lev < MAXRECORD) {
610
19692702
		t[lev].name = CURRENT_NAME;
611
19692702
		t[lev].line = CURRENT_LINE;
612
19692702
	}
613
19692702
}
614
615
static void
616
dump_stack(struct position *t, int lev)
617
{
618
	int i;
619
620
45
	for (i = 0; i < lev; i++) {
621
9
		if (i == MAXRECORD) {
622
			fprintf(stderr, "   ...\n");
623
			break;
624
		}
625
9
		fprintf(stderr, "   %s at line %lu\n",
626
9
			t[i].name, t[i].line);
627
	}
628
9
}
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
}