GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/m4/main.c Lines: 155 260 59.6 %
Date: 2016-12-06 Branches: 140 275 50.9 %

Line Branch Exec Source
1
/*	$OpenBSD: main.c,v 1.86 2015/11/03 16:21:47 deraadt 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
81
struct keyblk {
82
        char    *knam;          /* keyword name */
83
        int     ktyp;           /* keyword type */
84
};
85
86
struct keyblk keywrds[] = {	/* m4 keywords to be installed */
87
	{ "include",      INCLTYPE },
88
	{ "sinclude",     SINCTYPE },
89
	{ "define",       DEFITYPE },
90
	{ "defn",         DEFNTYPE },
91
	{ "divert",       DIVRTYPE | NOARGS },
92
	{ "expr",         EXPRTYPE },
93
	{ "eval",         EXPRTYPE },
94
	{ "substr",       SUBSTYPE },
95
	{ "ifelse",       IFELTYPE },
96
	{ "ifdef",        IFDFTYPE },
97
	{ "len",          LENGTYPE },
98
	{ "incr",         INCRTYPE },
99
	{ "decr",         DECRTYPE },
100
	{ "dnl",          DNLNTYPE | NOARGS },
101
	{ "changequote",  CHNQTYPE | NOARGS },
102
	{ "changecom",    CHNCTYPE | NOARGS },
103
	{ "index",        INDXTYPE },
104
#ifdef EXTENDED
105
	{ "paste",        PASTTYPE },
106
	{ "spaste",       SPASTYPE },
107
	/* Newer extensions, needed to handle gnu-m4 scripts */
108
	{ "indir",        INDIRTYPE},
109
	{ "builtin",      BUILTINTYPE},
110
	{ "patsubst",	  PATSTYPE},
111
	{ "regexp",	  REGEXPTYPE},
112
	{ "esyscmd",	  ESYSCMDTYPE},
113
	{ "__file__",	  FILENAMETYPE | NOARGS},
114
	{ "__line__",	  LINETYPE | NOARGS},
115
#endif
116
	{ "popdef",       POPDTYPE },
117
	{ "pushdef",      PUSDTYPE },
118
	{ "dumpdef",      DUMPTYPE | NOARGS },
119
	{ "shift",        SHIFTYPE | NOARGS },
120
	{ "translit",     TRNLTYPE },
121
	{ "undefine",     UNDFTYPE },
122
	{ "undivert",     UNDVTYPE | NOARGS },
123
	{ "divnum",       DIVNTYPE | NOARGS },
124
	{ "maketemp",     MKTMTYPE },
125
	{ "mkstemp",      MKTMTYPE },
126
	{ "errprint",     ERRPTYPE | NOARGS },
127
	{ "m4wrap",       M4WRTYPE | NOARGS },
128
	{ "m4exit",       EXITTYPE | NOARGS },
129
	{ "syscmd",       SYSCTYPE },
130
	{ "sysval",       SYSVTYPE | NOARGS },
131
	{ "traceon",	  TRACEONTYPE | NOARGS },
132
	{ "traceoff",	  TRACEOFFTYPE | NOARGS },
133
134
	{ "unix",         SELFTYPE | NOARGS },
135
};
136
137
#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
138
139
extern int optind;
140
extern char *optarg;
141
142
#define MAXRECORD 50
143
static struct position {
144
	char *name;
145
	unsigned long line;
146
} quotes[MAXRECORD], paren[MAXRECORD];
147
148
static void record(struct position *, int);
149
static void dump_stack(struct position *, int);
150
151
static void macro(void);
152
static void initkwds(void);
153
static ndptr inspect(int, char *);
154
static int do_look_ahead(int, const char *);
155
static void reallyoutputstr(const char *);
156
static void reallyputchar(int);
157
158
static void enlarge_stack(void);
159
160
int main(int, char *[]);
161
162
int exit_code = 0;
163
164
int
165
main(int argc, char *argv[])
166
5
{
167
	int c;
168
	int n;
169
	char *p;
170
171
5
	if (pledge("stdio rpath wpath cpath tmppath proc exec", NULL) == -1)
172
		err(1, "pledge");
173
174
5
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
175
5
		signal(SIGINT, onintr);
176
177
5
	init_macros();
178
5
	initspaces();
179
5
	STACKMAX = INITSTACKMAX;
180
181
5
	mstack = xreallocarray(NULL, STACKMAX, sizeof(stae), NULL);
182
5
	sstack = xalloc(STACKMAX, NULL);
183
184
5
	maxout = 0;
185
5
	outfile = NULL;
186
5
	resizedivs(MAXOUT);
187
188
15
	while ((c = getopt(argc, argv, "gst:d:D:U:o:I:P")) != -1)
189


5
		switch(c) {
190
191
		case 'D':               /* define something..*/
192
			for (p = optarg; *p; p++)
193
				if (*p == '=')
194
					break;
195
			if (*p)
196
				*p++ = EOS;
197
			dodefine(optarg, p);
198
			break;
199
		case 'I':
200
			addtoincludepath(optarg);
201
			break;
202
		case 'P':
203
5
			prefix_builtins = 1;
204
5
			break;
205
		case 'U':               /* undefine...       */
206
			macro_popdef(optarg);
207
			break;
208
		case 'g':
209
			mimic_gnu = 1;
210
			break;
211
		case 'd':
212
			set_trace_flags(optarg);
213
			break;
214
		case 's':
215
			synch_lines = 1;
216
			break;
217
		case 't':
218
			mark_traced(optarg, 1);
219
			break;
220
		case 'o':
221
			trace_file(optarg);
222
                        break;
223
		case '?':
224
			usage();
225
		}
226
227
5
        argc -= optind;
228
5
        argv += optind;
229
230
5
	initkwds();
231
5
	if (mimic_gnu)
232
		setup_builtin("format", FORMATTYPE);
233
234
5
	active = stdout;		/* default active output     */
235
5
	bbase[0] = bufbase;
236
5
        if (!argc) {
237
5
		sp = -1;		/* stack pointer initialized */
238
5
		fp = 0;			/* frame pointer initialized */
239
5
		set_input(infile+0, stdin, "stdin");
240
					/* default input (naturally) */
241
5
		macro();
242
	} else
243
		for (; argc--; ++argv) {
244
			p = *argv;
245
			if (p[0] == '-' && p[1] == EOS)
246
				set_input(infile, stdin, "stdin");
247
			else if (fopen_trypath(infile, p) == NULL)
248
				err(1, "%s", p);
249
			sp = -1;
250
			fp = 0;
251
			macro();
252
			release_input(infile);
253
		}
254
255
5
	if (wrapindex) {
256
		int i;
257
258
		ilevel = 0;		/* in case m4wrap includes.. */
259
		bufbase = bp = buf;	/* use the entire buffer   */
260
		if (mimic_gnu) {
261
			while (wrapindex != 0) {
262
				for (i = 0; i < wrapindex; i++)
263
					pbstr(m4wraps[i]);
264
				wrapindex =0;
265
				macro();
266
			}
267
		} else {
268
			for (i = 0; i < wrapindex; i++) {
269
				pbstr(m4wraps[i]);
270
				macro();
271
			}
272
		}
273
	}
274
275
5
	if (active != stdout)
276
		active = stdout;	/* reset output just in case */
277
50
	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
278
45
		if (outfile[n] != NULL)
279
			getdiv(n);
280
					/* remove bitbucket if used  */
281
5
	if (outfile[0] != NULL) {
282
		(void) fclose(outfile[0]);
283
	}
284
285
5
	return exit_code;
286
}
287
288
/*
289
 * Look ahead for `token'.
290
 * (on input `t == token[0]')
291
 * Used for comment and quoting delimiters.
292
 * Returns 1 if `token' present; copied to output.
293
 *         0 if `token' not found; all characters pushed back
294
 */
295
static int
296
do_look_ahead(int t, const char *token)
297
15253
{
298
	int i;
299
300
15253
	assert((unsigned char)t == (unsigned char)token[0]);
301
302
29215
	for (i = 1; *++token; i++) {
303
15233
		t = gpbc();
304

15233
		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
305
1271
			pushback(t);
306
2542
			while (--i)
307
				pushback(*--token);
308
1271
			return 0;
309
		}
310
	}
311
13982
	return 1;
312
}
313
314
#define LOOK_AHEAD(t, token) (t != EOF &&		\
315
    (unsigned char)(t)==(unsigned char)(token)[0] &&	\
316
    do_look_ahead(t,token))
317
318
/*
319
 * macro - the work horse..
320
 */
321
static void
322
macro(void)
323
206073
{
324
	char token[MAXTOK+1];
325
	int t, l;
326
	ndptr p;
327
	int  nlpar;
328
329
	cycle {
330
206073
		t = gpbc();
331
332

206073
		if (LOOK_AHEAD(t,lquote)) {	/* strip quotes */
333
4818
			nlpar = 0;
334
4818
			record(quotes, nlpar++);
335
			/*
336
			 * Opening quote: scan forward until matching
337
			 * closing quote has been found.
338
			 */
339
			do {
340
341
471208
				l = gpbc();
342

471208
				if (LOOK_AHEAD(l,rquote)) {
343
6991
					if (--nlpar > 0)
344
2173
						outputstr(rquote);
345

466390
				} else if (LOOK_AHEAD(l,lquote)) {
346
2173
					record(quotes, nlpar++);
347
2173
					outputstr(lquote);
348
462044
				} else if (l == EOF) {
349
					if (nlpar == 1)
350
						warnx("unclosed quote:");
351
					else
352
						warnx("%d unclosed quotes:", nlpar);
353
					dump_stack(quotes, nlpar);
354
					exit(1);
355
				} else {
356
462044
					if (nlpar > 0) {
357
462044
						if (sp < 0)
358
11630
							reallyputchar(l);
359
						else
360
450414
							CHRSAVE(l);
361
					}
362
				}
363
			}
364
471208
			while (nlpar != 0);
365


201255
		} else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
366
			reallyoutputstr(scommt);
367
368
			for(;;) {
369
				t = gpbc();
370
				if (LOOK_AHEAD(t, ecommt)) {
371
					reallyoutputstr(ecommt);
372
					break;
373
				}
374
				if (t == EOF)
375
					break;
376
				reallyputchar(t);
377
			}
378

402395
		} else if (t == '_' || isalpha(t)) {
379
34680
			p = inspect(t, token);
380
34680
			if (p != NULL)
381
6934
				pushback(l = gpbc());
382

62426
			if (p == NULL || (l != LPAREN &&
383
			    (macro_getdef(p)->type & NEEDARGS) != 0))
384
27746
				outputstr(token);
385
			else {
386
		/*
387
		 * real thing.. First build a call frame:
388
		 */
389
6934
				pushf(fp);	/* previous call frm */
390
6934
				pushf(macro_getdef(p)->type); /* type of the call  */
391

6934
				pushf(is_traced(p));
392
6934
				pushf(0);	/* parenthesis level */
393
6934
				fp = sp;	/* new frame pointer */
394
		/*
395
		 * now push the string arguments:
396
		 */
397
6934
				pushdef(p);			/* defn string */
398
6934
				pushs1((char *)macro_name(p));	/* macro name  */
399
6934
				pushs(ep);			/* start next..*/
400
401

6934
				if (l != LPAREN && PARLEV == 0) {
402
				    /* no bracks  */
403
3125
					chrsave(EOS);
404
405
3125
					if (sp == STACKMAX)
406
						errx(1, "internal stack overflow");
407
3125
					eval((const char **) mstack+fp+1, 2,
408
					    CALTYP, TRACESTATUS);
409
410
3125
					ep = PREVEP;	/* flush strspace */
411
3125
					sp = PREVSP;	/* previous sp..  */
412
3125
					fp = PREVFP;	/* rewind stack...*/
413
				}
414
			}
415
166575
		} else if (t == EOF) {
416

5
			if (!mimic_gnu /* you can puke right there */
417
			    && sp > -1 && ilevel <= 0) {
418
				warnx( "unexpected end of input, unclosed parenthesis:");
419
				dump_stack(paren, PARLEV);
420
				exit(1);
421
			}
422
5
			if (ilevel <= 0)
423
5
				break;			/* all done thanks.. */
424
			release_input(infile+ilevel--);
425
			emit_synchline();
426
			bufbase = bbase[ilevel];
427
			continue;
428
166570
		} else if (sp < 0) {		/* not in a macro at all */
429
149857
			reallyputchar(t);	/* output directly..	 */
430
		}
431
432

16713
		else switch(t) {
433
434
		case LPAREN:
435
3852
			if (PARLEV > 0)
436
43
				chrsave(t);
437

10214
			while (isspace(l = gpbc())) /* skip blank, tab, nl.. */
438
1255
				if (PARLEV > 0)
439
10
					chrsave(l);
440
3852
			pushback(l);
441
3852
			record(paren, PARLEV++);
442
3852
			break;
443
444
		case RPAREN:
445
3852
			if (--PARLEV > 0)
446
43
				chrsave(t);
447
			else {			/* end of argument list */
448
3809
				chrsave(EOS);
449
450
3809
				if (sp == STACKMAX)
451
					errx(1, "internal stack overflow");
452
453
3809
				eval((const char **) mstack+fp+1, sp-fp,
454
				    CALTYP, TRACESTATUS);
455
456
3809
				ep = PREVEP;	/* flush strspace */
457
3809
				sp = PREVSP;	/* previous sp..  */
458
3809
				fp = PREVFP;	/* rewind stack...*/
459
			}
460
			break;
461
462
		case COMMA:
463
5081
			if (PARLEV == 1) {
464
5078
				chrsave(EOS);		/* new argument   */
465

12808
				while (isspace(l = gpbc()))
466
					;
467
5078
				pushback(l);
468
5078
				pushs(ep);
469
			} else
470
3
				chrsave(t);
471
			break;
472
473
		default:
474

3928
			if (LOOK_AHEAD(t, scommt)) {
475
				char *p;
476
				for (p = scommt; *p; p++)
477
					chrsave(*p);
478
				for(;;) {
479
					t = gpbc();
480
					if (LOOK_AHEAD(t, ecommt)) {
481
						for (p = ecommt; *p; p++)
482
							chrsave(*p);
483
						break;
484
					}
485
					if (t == EOF)
486
					    break;
487
					CHRSAVE(t);
488
				}
489
			} else
490
3928
				CHRSAVE(t);		/* stack the char */
491
			break;
492
		}
493
	}
494
5
}
495
496
/*
497
 * output string directly, without pushing it for reparses.
498
 */
499
void
500
outputstr(const char *s)
501
32092
{
502
32092
	if (sp < 0)
503
25449
		reallyoutputstr(s);
504
	else
505
33539
		while (*s)
506
26896
			CHRSAVE(*s++);
507
32092
}
508
509
void
510
reallyoutputstr(const char *s)
511
25449
{
512
25449
	if (synch_lines) {
513
		while (*s) {
514
			fputc(*s, active);
515
			if (*s++ == '\n') {
516
				infile[ilevel].synch_lineno++;
517
				if (infile[ilevel].synch_lineno !=
518
				    infile[ilevel].lineno)
519
					do_emit_synchline();
520
			}
521
		}
522
	} else
523
25449
		fputs(s, active);
524
25449
}
525
526
void
527
reallyputchar(int c)
528
161487
{
529
161487
	putc(c, active);
530
161487
	if (synch_lines && c == '\n') {
531
		infile[ilevel].synch_lineno++;
532
		if (infile[ilevel].synch_lineno != infile[ilevel].lineno)
533
			do_emit_synchline();
534
	}
535
161487
}
536
537
/*
538
 * build an input token..
539
 * consider only those starting with _ or A-Za-z.
540
 */
541
static ndptr
542
inspect(int c, char *tp)
543
34680
{
544
34680
	char *name = tp;
545
34680
	char *etp = tp+MAXTOK;
546
	ndptr p;
547
548
34680
	*tp++ = c;
549
550


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