Subversion Repositories DevTools

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
263 dpurdie 1
/*
2
	CPP V5 -- definition and expansion routines.
3
 
4
	source:  def.c
5
	started: October 22, 1985
6
	version:
7
		July 21, 1988
8
		February 14, 1989:
9
			bug fixes in pp_def().
10
			bug fix in substitute().
11
		August 2, 1989:
12
			support for C++ single-line comment added to pp_def().
13
			overflow checks made in substitute().
14
			Big buffers allocated on heap in expand(), rescan().
15
 
16
	Written by Edward K. Ream.
17
	This software is in the public domain.
18
 
19
	See the read.me file for disclaimer and other information.
20
*/
21
 
22
#include "cpp.h"
23
 
24
/*
25
	Forward declarations of internal routines.
26
*/
27
 
28
static void	aexpand		(char *name, int *nargs, char **p1, char *out,
29
					char **p2);
30
static void	aparse		(char *name, int *nargs, char *out, char **p);
31
static bool	a1parse		(char *name, char *out, int max_out);
32
static void	disable_name	(char *name);
33
static void	enable_name	(void);
34
static bool	is_disabled	(char *name);
35
static void	expand		(char *name, int nargs, char *rtext, char *out,
36
					int max_out);
37
static void	rescan		(char *name, char *in, char *out, int max_out,
38
					bool bounded_flag);
39
static void	substitute	(char *name, int nargs, char *in, char **args,
40
					char *out, int maxout);
41
 
42
/*
43
	Define static variables used only by this module.
44
*/
45
static char *	x_stack [MAX_MDEPTH];	/* Pointers to disabled macro names. */
46
static int	x_count = 0;		/* Number of disabled macro names.   */
47
 
48
/*
49
	Externally visible routines.
50
*/
51
 
52
/*
53
	Check an identifier seen at the outer level to see whether it
54
	could be a macro call and expand it and return TRUE if so.
55
*/
56
bool
57
outer_expand(name, old_mflag)
58
char	*name;
59
bool	old_mflag;
60
{
61
	char 	*p, *p1, *p2;
62
	int	i;
63
	char *	rtext;			/* Replacement text for name.	   */
64
	int	nargs;			/* Required number of args.	   */
65
	char	temp_buf[MAX_RTEXT];	/* Temp spot for macro.		   */	
66
 
67
	TRACEPB("outer_expand", printf("(%s)\n", name));
68
 
69
	if (m_flag || old_mflag || !mst_lookup(name, &rtext, &nargs)) {
70
		RETURN_BOOL("outer_expand", FALSE);
71
	}
72
	else if (nargs != -1) {
73
		skip_ws(TRUE);
74
		if (ch != '(') {
75
			warn3("Function-like macro, ", name,
76
				"appears without arguments.");
77
			RETURN_BOOL("outer_expand", FALSE);
78
		}
79
	}
80
 
81
	/*
82
		Expand the macro into temp_buf[].
83
		This will use ONLY file characters.
84
	*/
85
	expand(name, nargs, rtext, &temp_buf[0], MAX_RTEXT);
86
 
87
	/*
88
		Expand will have changed ch to point past any args.
89
		Use sys_fpb so that
90
		1.  The old ch will appear just AFTER the macro expansion.
91
		2.  It will not be mistaken added to the rescan buffer.
92
	*/
93
	sys_fpb(ch);
94
 
95
	/*
96
		Fully rescan temp_buf[] into macro_buf[].
97
		Use an unbounded rescan, i.e., allow file characters to
98
		complete a partial list of actual parameters which may
99
		be started in the rescanned text.
100
	*/
101
	p_rescan = NULL;
102
	m_flag   = FALSE;
103
	rescan(name, &temp_buf[0], &macro_buf[0], MAX_RTEXT, FALSE);
104
 
105
	/* Make sure we save the now current ch. */
106
	sys_fpb(ch);
107
 
108
	/* Delete any internal flags from the output. */
109
	TRACEP("outer_expand", printf("macro1: %s\n", pr_str(&macro_buf[0])));
110
 
111
	p1 = p2 = &macro_buf[0];
112
	while (*p2) {
113
		if (*p2 == EXPAND_OFF || *p2 == EORT) {
114
			p2++;
115
		}
116
		else {
117
			*p1++ = *p2++;
118
		}
119
	}
120
	*p1 = '\0';
121
	TRACEP("outer_expand", printf("macro2: %s\n", pr_str(&macro_buf[0])));
122
 
123
	/*
124
		Inhibit any further macro expansion until the end of the
125
		current local input stream.
126
	*/
127
 
128
	if (m_flag) {
129
		warning("outer_expand: Can't happen.");
130
	}
131
	m_flag = TRUE;
132
	p_rescan = &macro_buf[0];
133
 
134
	/* Reload ch from the global macro buffer. */
135
	sysnext();
136
 
137
	RETURN_BOOL("outer_expand", TRUE);
138
}
139
 
140
/*
141
	Handle the #define directive by parsing the statement, and entering
142
	the macro's name, number of arguments and replacement text into the
143
	macro table.
144
 
145
	Formal arguments are found and replaced by a flag byte followed by the
146
	number of the formal argument.
147
*/
148
void
149
pp_def()
150
{
151
	int	i;
152
 
153
	char	name[MAX_SYMBOL];	/* Name of macro.		*/
154
 
155
	char	rtext [MAX_RTEXT];	/* Replacement text.		*/
156
	int	rcount = 0;		/* Number of chars in rtext[].	*/
157
 
158
	char	atext [MAX_ATEXT];	/* Text of formal parameters.	*/
159
	int	acount = 0;		/* Number of chars in atext[].	*/
160
 
161
	char *	argps [MAX_NARGS];	/* Pointers into atext[].	 */
162
	int	nargs = 0;		/* Number of entries in argps[]. */
163
 
164
	TICKB("pp_def");
165
 
166
	/* Make sure the name is present. */
167
	if (!isid1(ch)) {
168
		error("#define ignored--no symbol name given.");
169
		skip_1line();
170
		return;
171
	}
172
 
173
	/* Get the macro name into name. */
174
	t_id(&name[0], MAX_SYMBOL);
175
	TRACEPN("pp_def", printf("macro name is %s\n", name));
176
 
177
	if (ch != '(') {
178
		/* Indicate no argument list and continue. */
179
		nargs = -1;
180
		goto gettext;
181
	}
182
	else {
183
		sysnext();
184
	}
185
 
186
	if (ch == ')') {
187
		sysnext();
188
		nargs = 0;
189
		goto gettext;
190
	}
191
 
192
	/* 
193
		Put the formal arguments into dynamic storage.
194
		Set pointers to the arguments argps[].
195
	*/
196
	while (nargs < MAX_NARGS) {
197
 
198
		skip_ws(TRUE);
199
 
200
		if (!isid1(ch)) {
201
			err2(	"#define ignored--",
202
				"formal arg must be an identifier.");
203
			skip_1line();
204
			RETURN_VOID("pp_def");
205
		}
206
 
207
		/* Copy one formal arg to atext[]. */
208
		t_id(&t_symbol[0], MAX_SYMBOL);
209
		argps [nargs++] = &atext[acount];
210
		str_cpy(atext + acount, t_symbol);
211
		acount += strlen(t_symbol)+1;
212
 
213
		TRACEPN("pp_def", printf("formal [%d] is %s\n",
214
			nargs-1, pr_str(argps [nargs-1])));
215
 
216
		/* Bug fix: 2/14/89 */
217
		skip_ws(TRUE);
218
 
219
		if (ch == ')') {
220
			sysnext();
221
			goto gettext;
222
		}
223
		else if (ch == ',') {
224
			sysnext();
225
		}
226
	}
227
	error("#define ignored--too many arguments.");
228
	skip_1line();
229
	RETURN_VOID("pp_def");
230
 
231
 
232
	/*
233
		At this point, nargs contains the number of formal arguments,
234
		or -1 if no argument list was given.
235
 
236
	*/
237
 
238
gettext:
239
 
240
	TRACEPN("pp_def", printf("nargs is %d\n", nargs));
241
 
242
	skip_bl();
243
 
244
	/*
245
		Put the replacement text into rtext[].
246
		The text is tokenized, which means that partial comments
247
		or strings are not valid.
248
 
249
		Replace formal arg n by n FLAG on the fly.
250
	*/
251
 
252
	for (rcount = 0;;) {
253
		switch (ch) {
254
 
255
		case END_FILE:
256
		case '\n':
257
			goto done;
258
 
259
		/* Replace a sequence of white space by a blank. */
260
		case ' ':
261
		case '\t':
262
			sysnext();
263
			if (rcount > 0 && rtext[rcount-1] != ' ') {
264
				rtext[rcount++] = ' ';
265
			}
266
			continue;
267
 
268
		/* Look for continuation of definitions. */
269
		case '\\':
270
			sysnext();
271
			if (ch == '\n') {
272
				sysnlput();
273
				sysnext();
274
				if (rcount > 0 && rtext[rcount-1] != ' ') {
275
					rtext[rcount++] = ' ';
276
				}
277
				bump_line();
278
			}
279
			else {
280
				warning("Backslash not followed by newline.");
281
				rtext[rcount++] = '\\';
282
			}
283
			continue;
284
 
285
		/* Eliminate comments. */
286
		case '/':
287
			sysnext();
288
			if (ch == '*') {
289
				if (rcount > 0 && rtext[rcount-1] != ' ') {
290
					rtext[rcount++] = ' ';
291
				}
292
				sysnext();
293
				skip_comment();
294
			}
295
			else if (slc_flag && ch == '/') {
296
				/* 8/1/89 C++ style single-line comment. */
297
				if (rcount > 0 && rtext[rcount-1] != ' ') {
298
					rtext[rcount++] = ' ';
299
				}
300
				while (ch != END_FILE && ch != '\n') {
301
					sysnext();
302
				}
303
				goto done;
304
			}
305
			else {
306
				rtext[rcount++] = '/';
307
			}
308
			continue;
309
 
310
		/* Handle complete strings. */
311
		case '"':
312
		case '\'':
313
			t_string(&rtext[rcount], MAX_RTEXT-rcount, TRUE);
314
			rcount += t_length;
315
			continue;
316
 
317
		/* Handle # formal param and ## */
318
		case '#':
319
			sysnext();
320
			if (ch == '#') {
321
				sysnext();
322
				rtext[rcount++] = CONCAT_FLAG;
323
				continue;
324
			}
325
 
326
			/* We expect an identifier here. */
327
 
328
			/* Bug fix 2/14/89:  delete leading white space here. */
329
			if (ch == ' ' || ch == '\t') {
330
				if (rcount > 0) {
331
					rtext[rcount++] = ' ';
332
				}
333
				skip_bl();
334
			}
335
			if (isid1(ch)) {
336
				t_id(&rtext[rcount], MAX_RTEXT-rcount);
337
				i = arg_search( rtext + rcount, nargs,
338
						&argps[0]);
339
				if (i >= 0) {
340
					/* Replace id with flags. */
341
					rtext[rcount++] = POUND_FLAG;
342
					rtext[rcount++] = i + ARG_OFFSET;
343
				}
344
				else {
345
					/* Accept id. */
346
					rcount += t_length;
347
				}
348
			}
349
			else {
350
				/* Replace the blank with # */
351
				rtext[rcount++] = '#';
352
				warning("Id expected after '#'.");
353
			}
354
			continue;
355
 
356
		default:
357
 
358
			/* Possible formal parameter. */
359
			if (isid1(ch)) {
360
				t_id(&rtext[rcount], MAX_RTEXT-rcount);
361
				i = arg_search(	rtext + rcount, nargs,
362
						&argps[0]);
363
				if (i >= 0) {
364
					rtext[rcount++] = ARG_FLAG;
365
					rtext[rcount++] = i+ARG_OFFSET;
366
				}
367
				else {
368
					rcount += t_length;
369
				}
370
			}
371
			else {
372
				rtext[rcount++] = ch;
373
				sysnext();
374
			}
375
			continue;
376
		}
377
	}
378
 
379
done:
380
 
381
	/* This check is made only here to save a little time. */
382
	if (rcount >= MAX_RTEXT) {
383
		fatal("Replacement text of macro is too long.");
384
	}
385
 
386
	/* Strip at most one blank off the end of the definition. */
387
	if (rtext[rcount - 1] == ' ') {
388
		rcount--;
389
	}
390
 
391
	/* Terminate the replacement text properly. */
392
	rtext[rcount] = '\0';
393
 
394
	TRACEPN("pp_def", printf("rtext: <%s>\n", pr_str(rtext)));
395
 
396
	/*
397
		Enter the symbol, replacement text and number of arguments
398
		into the macro table.
399
 
400
		Note that mst_enter allocates space for copies of name and
401
		rtext so the fact that they are on the stack doesn't matter.
402
	*/
403
	mst_enter(&name[0], rtext, nargs);
404
	skip_pp();
405
 
406
	RETURN_VOID("pp_def");
407
}
408
 
409
 
410
/*
411
	Internal Routines.
412
*/
413
 
414
/*
415
	Macro expand all arguments in old_argps and leave them in new_atext[].
416
	Put pointers to them in new_argps[].
417
*/
418
static void
419
aexpand(name, nargs, old_argps, new_atext, new_argps)
420
char *	name;		/* Name of macro whose args are being expanded.	*/
421
int *	nargs;		/* Number of args the macro should have.	*/
422
char *	old_argps[];	/* Pointers to unexpanded tokens for each arg.	*/
423
char 	new_atext[];	/* Storage for expanded args.			*/
424
char *	new_argps[];	/* Pointers to expanded args.			*/
425
{
426
	char	save_ch;
427
	int	i;
428
	char *	new;
429
	int	new_count = 0;
430
 
431
	TRACEPB("aexpand", printf("(%s, %d, %p, %p, %p)\n",
432
		name, *nargs, old_argps, new_atext, new_argps));
433
 
434
	/*
435
		Save the current value of ch!
436
		rescan will change ch, and it must be restored afterwards.
437
	*/
438
	save_ch = ch;
439
 
440
	/* Rescan all arguments. */
441
	for (i = 0; i < *nargs; i++) {
442
		new = &new_atext[new_count];
443
		new_argps[i] = new;
444
 
445
		/* Do a bounded rescan. */
446
		rescan(	"<NO_NAME>", old_argps[i], new,
447
			MAX_RTEXT-new_count, TRUE);
448
 
449
		new_count += strlen(new)+1;
450
	}
451
 
452
	/* Restore ch */
453
	ch = save_ch;
454
 
455
	RETURN_VOID("aexpand");
456
}
457
 
458
/*
459
	Parse a list of actual arguments into atext[] and put pointers to
460
	the arguments in argps[].  
461
	Set *nargs to -1 on error.
462
	The size of atext[] is MAX_ATEXT and the size of argps is MAX_ARGPS.
463
*/
464
static int call_start;		/* Line where arguments started.	*/
465
 
466
static void
467
aparse(name, nargs, atext, argps)
468
char *	name;		/* Macro whose arguments are being parsed.	   */
469
int *	nargs;		/* Pointer to arg count.			   */
470
char 	atext[];	/* Buffer into which UNEXPANDED arguments are put. */
471
char *	argps[];	/* Array of pointers to arguments.		   */
472
{
473
	int	arg_c = 0;	/* Number of arguments parsed so far.	*/
474
	int	length = 0;	/* Total length of arguemnts parsed.	*/
475
	char	msg [100];
476
	char	line [10];
477
	bool	flag;
478
 
479
	if (*nargs == -1) {
480
		return;
481
	}
482
 
483
	TRACEPB("aparse", printf("(%s, %d, %p, %p)\n",
484
		name, *nargs, atext, argps));
485
 
486
	if (ch != '(') {
487
		syserr("aparse: Can't happen.");
488
	}
489
	else {
490
		sysnext();
491
	}
492
 
493
	/* Save starting line number of the macro call. */
494
	call_start = t_line;
495
 
496
	if (ch == ')') {
497
		sysnext();
498
		goto check;
499
	}
500
 
501
	for(;;) {
502
		/* Set pointer to the start of the argument. */
503
		argps[arg_c] = &atext[length];
504
 
505
		/* Parse the argument. */
506
		flag = a1parse(name, &atext[length], MAX_ATEXT-length);
507
		if (flag || ch == END_FILE) {
508
			*nargs = -1;
509
			RETURN_VOID("aparse");
510
		}
511
		else {
512
			length += strlen(argps[arg_c])+1;
513
			arg_c++;
514
		}
515
 
516
		if (ch == ')') {
517
			sysnext();
518
			break;
519
		}
520
		else if (ch == ',') {
521
			sysnext();
522
		}
523
		else {
524
			/* Error detected in a1parse. */
525
			break;
526
		}
527
	}
528
 
529
check:
530
	if (arg_c != *nargs) {
531
		if (call_start != t_line) {
532
			strcpy(msg, "Call to macro ");
533
			strcat(msg, name);
534
			strcat(msg, " starting at line ");
535
			conv2s(call_start, line);
536
			strcat(msg, line);
537
		}
538
		else {
539
			strcpy(msg, "Call to macro ");
540
			strcat(msg, name);
541
		}
542
		strcat(msg, " requires ");
543
		conv2s(*nargs, line);
544
		strcat(msg, line);
545
		strcat(msg, " arguments.");
546
		error(msg);
547
		*nargs = -1;
548
	}
549
	TRACEP("aparse",
550
		{int i;
551
		if (*nargs != 0) printf("\n");
552
		for(i = 0; i < *nargs; i++) {
553
			printf("arg[%d]: @%p=%p: <%s>\n",
554
				i, &argps[i], argps[i], pr_str(argps[i]));
555
		}});
556
	RETURN_VOID("aparse");
557
}
558
 
559
/*
560
	Parse one actual argument into out[].
561
	The size of out is max_length.
562
	Return TRUE if an error was seen.
563
 
564
	Formal arguments are simply identifiers, which are handled by t_id(),
565
	but actual arguments are LISTS of tokens separated by commas.  As an
566
	added twist, commas inside single or double quotes, or commas which are
567
	"protected" by additional parentheses do NOT separate actual args.
568
	Thus, each of the following calls have to M have ONE actual argument:
569
 
570
		M(a)
571
		M(a * N(c,b))
572
		M(',')
573
		M("a,b")
574
		M((a,b))
575
		M((a,")",b))
576
 
577
	This routine changes all comments and white space to a single blank.
578
*/
579
static bool
580
a1parse(name, out, max_out)
581
char *	name;			/* Name of the macro being expanded.	*/
582
char *	out;			/* Output buffer.			*/
583
int	max_out;		/* Size of out[].			*/
584
{
585
 	int count;	/* Number of characters in out[].	*/
586
	int plevel;	/* Parenthesis level.			*/
587
 
588
	char buffer [100];	/* Buffer for messages.		*/
589
	char linebuf [10];	/* Buffer for messages.		*/
590
 
591
	TRACEPB("a1parse", printf("(%s, %p, %d)\n",
592
		name, out, max_out));
593
 
594
	/* No parens have been seen yet. */
595
	plevel = 0;
596
 
597
	for(count = 0;;) {
598
 
599
		/* Make sure there is room for one more. */
600
		if (count >= max_out) {
601
			goto toolong;
602
		}
603
 
604
		switch (ch) {
605
 
606
		case END_FILE:
607
			goto runon;
608
 
609
		case '\n':
610
			sysnlput();
611
			sysnext();
612
			bump_line();
613
			if (count > 0 && out[count-1] != ' ') {
614
				out[count++] = ' ';
615
			}
616
			continue;
617
 
618
		/* Convert a sequence of white space to one blank. */
619
		case ' ':
620
		case '\t':
621
			sysnext();
622
			if (count > 0 && out[count-1] != ' ') {
623
				out[count++] = ' ';
624
			}
625
			continue;
626
 
627
		case '\\':
628
			sysnext();
629
			if (ch == '\n') {
630
				sysnlput();
631
				sysnext();
632
				if (count > 0 && out[count-1] != ' ') {
633
					out[count++] = ' ';
634
				}
635
				bump_line();
636
			}
637
			else {
638
				out[count++] = '\\';
639
			}
640
			continue;
641
 
642
		case ',':
643
			if (plevel == 0) {
644
				goto end_arg;
645
			}
646
			else {
647
				out[count++] = ch;
648
				sysnext();
649
			}
650
			continue;
651
 
652
		case ')':
653
 
654
			if (plevel == 0) {
655
				goto end_arg;
656
			}
657
			else {
658
				plevel--;
659
				out[count++] = ch;
660
				sysnext();
661
				continue;
662
			}
663
 
664
		case '(':
665
			plevel++;
666
			out[count++] = ch;
667
			sysnext();
668
			continue;
669
 
670
		case '"':
671
		case '\'':
672
			t_string(&out[count], max_out-count, TRUE);
673
			count += t_length;
674
			if (count >= max_out) {
675
				goto toolong;
676
			}
677
			continue;
678
 
679
		case '/':
680
			sysnext();
681
			if (ch == '*') {
682
				sysnext();
683
				skip_comment();
684
 
685
				/* Change a comment into one blank. */
686
				if (count > 0 && out[count] != ' ') {
687
					out[count++] = ' ';
688
				}
689
			}
690
			else {
691
				out[count++] = '/';
692
			}
693
			continue;
694
 
695
		default:
696
			if (isid1(ch)) {
697
				t_id(&out[count], max_out-count);
698
				count += t_length;
699
				if (count >= max_out) {
700
					goto toolong;
701
				}
702
			}
703
			else {
704
				out[count++] = ch;
705
				sysnext();
706
			}
707
		}
708
	}
709
 
710
end_arg:
711
	/* Finish off the argument. */
712
	out[count] = '\0';
713
	TRACEP("a1parse", printf("out: <%s>\n", pr_str(out)));
714
	RETURN_BOOL("a1parse", FALSE);
715
 
716
runon:
717
	if (call_start != t_line) {
718
		conv2s(call_start, linebuf);
719
		str_cpy(buffer, "Runon macro call at line ");
720
		str_cat(buffer, linebuf);
721
	}
722
	else {
723
		str_cpy(buffer, "Runon macro call");
724
	}
725
	str_cat(buffer, "--last arg set to null.");
726
	error(buffer);
727
	RETURN_BOOL("a1parse", TRUE);
728
 
729
toolong:
730
	conv2s(call_start, linebuf);
731
	str_cpy(buffer, "Macro arg starting at line ");
732
	str_cat(buffer, linebuf);
733
	str_cat(buffer, " is too long.");
734
	fatal(buffer);
735
	RETURN_BOOL("a1parse", TRUE);
736
}
737
 
738
/*
739
	Search an array of formal arguments for a match.
740
*/
741
static int
742
arg_search(name, nargs, argps)
743
char *	name;	/* Name to search for.	*/
744
int	nargs;	/* Number of args.	*/
745
char **	argps;	/* Array of args.	*/
746
{
747
	int i;
748
 
749
	/* See if the id is a formal arg. */
750
	for (i = 0; i < nargs ; i++) {
751
		if (str_eq(name, argps[i])) {
752
			return i;
753
		}
754
	}
755
	return -1;
756
}
757
 
758
/*
759
	Disable the expansion of one name.
760
*/
761
static void
762
disable_name(name)
763
char *name;
764
{
765
	TRACEP("disable_name", printf("(%s)\n", name));
766
 
767
	if (x_count < MAX_MDEPTH) {
768
		x_stack [x_count++] = name;
769
	}
770
}
771
 
772
/*
773
	Enable the expansion of the last disabled name.
774
*/
775
static void
776
enable_name()
777
{
778
	TICK("enable_name");
779
 
780
	if (x_count == 0) {
781
		syserr("enable_name: Can't happen.");
782
	}
783
	x_count--;
784
}
785
 
786
/*
787
	Fully expand the macro described by name, nargs, rtext.
788
*/
789
static void
790
expand(name, nargs, rtext, out, max_out)
791
char *	name;		/* Name of macro expanded.	*/
792
int	nargs;		/* Required number of args.	*/
793
char *	rtext;		/* Replacement text for name.	*/
794
char *	out;		/* Output buffer.		*/
795
int	max_out;	/* Size of out[].		*/
796
{
797
	char *  old_argps[MAX_NARGS];
798
	char *  new_argps[MAX_NARGS];
799
 
800
	char *	old_atext;	/* Buffer for actual arguments.	   */
801
	char *	new_atext;	/* Buffer for expansion of args.   */	
802
 
803
	TRACEPB("expand", printf("(%s, %d, <%s>, %p, %d) ch: %s\n",
804
		name, nargs, pr_str(rtext), out, max_out, pr_ch(ch)));
805
 
806
	/*
807
		Allocate memory on the heap so we don't crash the stack.
808
	*/
809
	old_atext = m_alloc(MAX_ATEXT);
810
	new_atext = m_alloc(MAX_ATEXT);
811
 
812
	/* Parse all arguments to the macro. */
813
	aparse(name, &nargs, old_atext, old_argps);
814
 
815
	/* Fully macro expand all arguments. */
816
	aexpand(name, &nargs, &old_argps[0], &new_atext[0], &new_argps[0]);
817
 
818
	/* Substitute all expanded arguments into rtext[], giving out[]. */
819
	substitute(name, nargs, rtext, &new_argps[0], &out[0], max_out);
820
 
821
	/* Free all locally allocated memory. */
822
	m_free(old_atext);
823
	m_free(new_atext);
824
 
825
	RETURN_VOID("expand");
826
}
827
 
828
/*
829
	Return TRUE if macro expansion of name has been disabled.
830
*/
831
static bool
832
is_disabled(name)
833
char *name;
834
{
835
	int i;
836
 
837
	TRACEPB("is_disabled", printf("(%s), x_count: %d\n",
838
		name, x_count));
839
 
840
	for (i = x_count-1; i >= 0; i--) {
841
		TRACEP("is_disabled_v", printf("compare: <%s> <%s>\n",
842
			name, x_stack[i]));
843
		if (str_eq(name, x_stack[i])) {
844
			RETURN_BOOL("is_disabled", TRUE);
845
		}
846
	}
847
	RETURN_BOOL("is_disabled",FALSE);
848
}
849
 
850
/*
851
	This is it: the guts of macro expansion.
852
	Fully rescan in[] to out[], looking for macro calls.
853
 
854
	A key distinction:  bounded versus unbounded rescanning.
855
	Set the global b_rescan to TRUE for bounded rescans.  
856
	sysnext() returns EORT when p_rescan is empty on bounded rescans.
857
 
858
	The CALLER of rescan must save and restore ch as appropriate.
859
	(Note that rescan is called recursively from within, so it must
860
	save ch in that case just as any other caller must.
861
 
862
	Note that rescan will completely process rescan buffer in all cases.
863
	For unbounded rescans, the previous rescan buffer is appended to in[].
864
	For bounded rescans, the previous rescan buffer is kept separate.
865
*/
866
static void
867
rescan(name, in, out, max_out, bounded_flag)
868
char *	name;		/* Name of macro being rescanned.	*/
869
char *	in;		/* Text to be rescanned.		*/
870
char *	out;		/* Output buffer.			*/
871
int	max_out;	/* Size of out[].			*/
872
bool	bounded_flag;	/* TRUE if bounded rescan.		*/
873
{
874
	int	save_mflag;		/* Saved m_flag.	*/
875
	char *	save_prescan;		/* Saved p_rescan.	*/
876
 
877
	char *	local_in;	/* Local rescan buffer.	*/
878
	char *	expand_buf;	/* Expansion buffer.	*/
879
	char *	id_buf;		/* Saved rescan id.	*/
880
 
881
	int	out_len = 0;		/* Index into out[].	*/
882
	char *	rtext;			/* Ptr to replacement text.	*/
883
	int	nargs;			/* Required number of args.	*/
884
	int	i, level;
885
 
886
	TRACEPB("rescan", printf("(%s, <%s>, %p, %d, %s) ch: %s\n",
887
		name, pr_str(in), out, max_out, pr_bool(bounded_flag),
888
		pr_ch(ch)));
889
 
890
	/*
891
		Allocate buffers here so we don't crash the stack.
892
	*/
893
	local_in   = m_alloc(MAX_RTEXT);
894
	expand_buf = m_alloc(MAX_RTEXT);
895
	id_buf     = m_alloc(MAX_SYMBOL);
896
 
897
	if (bounded_flag) {
898
		/* Save old rescan buffer and use in[] as the new buffer. */
899
		save_mflag   = m_flag;
900
		save_prescan = p_rescan;
901
 
902
		/*
903
			Indicate the end of the buffer with EORT flag.
904
			Copy in[] to local_in[] so we have room for it.
905
		*/
906
		p_rescan = &local_in[0];
907
		strcpy(&local_in[0], in);
908
		i = strlen(&local_in[0]);
909
		local_in[i++] = EORT;
910
		local_in[i]   = '\0';
911
	}
912
	else {
913
		/*
914
			The key programming trick in this whole file.
915
			Set rescan to old rescan appended behind in[].
916
		*/
917
		if (!m_flag) {
918
			p_rescan = in;
919
		}
920
		else {
921
			if (strlen(in) + strlen(p_rescan) >= max_out) {
922
				goto run_on;
923
			}
924
			str_cpy(local_in, in);
925
			str_cat(&local_in[0], p_rescan);
926
			p_rescan = &local_in[0];
927
		}
928
	}
929
 
930
	/* Get input from rescan buffer. */
931
	m_flag = TRUE;
932
 
933
	TRACEP("rescan",
934
		printf("p_rescan: strlen: %d, <%s>\n",
935
			strlen(p_rescan), pr_str(p_rescan)));
936
 
937
	/*
938
		Note1:	At this point, sysnext() will return the characters
939
		that have just been placed in the rescan buffer pointed to by
940
		p_rescan.  This means that t_id(), t_string(), etc.  may be
941
		used to parse the rescan buffer.
942
 
943
		Note2:  For bounded rescans, sysnext() will return EORT when
944
		the rescan buffer is exhausted.  This means that t_string() and
945
		skip_comment() will complain about "unexpected end of input," 
946
		which is as it should be.
947
	*/
948
 
949
	/* Disable further expansions of name for the duration. */
950
	disable_name(name);
951
 
952
	/*
953
		Scan the buffer until it is exhausted or a macro call is seen.
954
		(The recursive call to rescan() will finish off the buffer.)
955
	*/
956
	out_len = 0;
957
	sysnext();
958
	while (m_flag) {
959
 
960
		if (out_len >= max_out) {
961
			goto run_on;
962
		}
963
 
964
		if (ch == EORT) {
965
			break;
966
		}
967
		else if (ch == EXPAND_OFF) {
968
			/* Inhibited identifier. */
969
			out[out_len++] = ch;
970
			sysnext();
971
			t_id(&out[out_len], max_out-out_len);
972
			out_len += strlen(&out[out_len]);
973
		}
974
		else if (ch == '"' || ch == '\'') {
975
			/* Handle complete strings. */
976
			t_string(&out[out_len], max_out-out_len, TRUE);
977
			out_len += t_length;
978
		}
979
		else if (isid1(ch)) {
980
			/* Handle identifiers. */
981
			t_id(&id_buf[0], MAX_SYMBOL);
982
 
983
			/*
984
				Possible inner macro call.
985
				Do not adjust out_len until we know for sure.
986
			*/
987
			if (!mst_lookup(&id_buf[0], &rtext, &nargs)) {
988
				/* Not a macro call. */
989
				str_cpy(&out[out_len], &id_buf[0]);
990
				out_len += t_length;
991
				continue;
992
			}
993
			else if (is_disabled(&id_buf[0])) {
994
				/*
995
					Disabled macro.
996
					Inhibit ALL further expansion.
997
				*/
998
				out[out_len++] = EXPAND_OFF;
999
				str_cpy(&out[out_len], &id_buf[0]);
1000
				out_len += t_length;
1001
				continue;
1002
			}
1003
			else if (nargs > -1) {
1004
				skip_ws(TRUE);
1005
				/*
1006
					Function-like macro name without args
1007
					are ok in bounded expansions.
1008
				*/
1009
				if (ch != '(') {
1010
					if (ch != EORT && !bounded_flag) {
1011
					   warn3("Function-like macro ",
1012
						 &id_buf[0],
1013
						 " appears without arguments.");
1014
					}
1015
					str_cpy(&out[out_len], &id_buf[0]);
1016
					out_len += t_length;
1017
					continue;
1018
				}
1019
			}
1020
 
1021
			/* Valid macro call. */
1022
			if (out_len >= max_out) {
1023
				goto run_on;
1024
			}
1025
 
1026
			out[out_len] = '\0';
1027
 
1028
			/* Expand the text into expand_buf[]. */
1029
			expand(	&id_buf[0], nargs, rtext,
1030
				&expand_buf[0], MAX_RTEXT);
1031
 
1032
			/*
1033
				Do not pull an extra file character into
1034
				the buffer.
1035
			*/
1036
			if (m_flag) {
1037
				/* Append current ch to expand_buf[]. */
1038
				i = strlen(&expand_buf[0]);
1039
				expand_buf[i++] = ch;
1040
				expand_buf[i] = '\0';
1041
			}
1042
			else {
1043
				sys_fpb(ch);
1044
			}
1045
 
1046
			/* Rescan will append the rest of p_rescan. */
1047
			TRACEP("rescan", printf("recursive call. ch: %s\n",
1048
				pr_ch(ch)));
1049
			rescan(	&id_buf[0], &expand_buf[0],
1050
				&out[out_len], MAX_RTEXT-out_len, FALSE);
1051
 
1052
			out_len=strlen(&out[0]);
1053
 
1054
			/* Use the ch that the recursive call left behind. */
1055
			continue;
1056
		}
1057
		/* Comments might appear here due to concatenation. */
1058
		else if (ch == '/') {
1059
			sysnext();
1060
			if (ch == '*') {
1061
				sysnext();
1062
				skip_comment();
1063
			}
1064
			else {
1065
				out[out_len++] = '/';
1066
			}
1067
		}
1068
		else {
1069
			out[out_len++] = ch;
1070
			sysnext();
1071
		}
1072
	}
1073
 
1074
	out[out_len] = '\0';
1075
	TRACEP("rescan", printf("out: <%s>\n", pr_str(out)));
1076
 
1077
	enable_name();
1078
 
1079
	/* Restore the b_rescan global. */
1080
	if (bounded_flag) {
1081
		/* Restore the old rescan buffer. */
1082
		m_flag   = save_mflag;
1083
		p_rescan = save_prescan;
1084
	}
1085
	else {
1086
		/* Everything is complete. */
1087
		m_flag = FALSE;
1088
	}
1089
 
1090
	/* Free buffers. */
1091
	m_free(local_in);
1092
	m_free(expand_buf);
1093
	m_free(id_buf);
1094
 
1095
	RETURN_VOID("rescan");
1096
 
1097
run_on:
1098
	strcpy(id_buf, "Macro expansion for ");
1099
	strcat(id_buf, name);
1100
	strcat(id_buf, " is too long...\nExpansion set to empty string");
1101
	error(id_buf);
1102
 
1103
	/* Truncate the ENTIRE macro. */
1104
	out[0] = '\0';
1105
 
1106
	/* Free the buffers. */
1107
	m_free(local_in);
1108
	m_free(expand_buf);
1109
	m_free(id_buf);
1110
 
1111
	RETURN_VOID("rescan");
1112
}
1113
 
1114
/*
1115
	Substitute expanded arguments from argps[] into in[] leaving out[].
1116
	8/1/89: run-on logic added.
1117
*/
1118
static void
1119
substitute(name, nargs, rtext, argps, out, max_out)
1120
char *	name;	/* The name of the macro being expanded.	*/
1121
int	nargs;	/* Number of args for the macro.		*/
1122
char *	rtext;	/* Replacement text of the macro.		*/
1123
char **	argps;	/* Pointer to fully expanded arguments.		*/
1124
char *	out;	/* Output buffer.				*/
1125
int	max_out;	/* Size of out[]. 			*/
1126
{
1127
	int	limit;
1128
	int	c, i, count;
1129
	int	arg_num;
1130
	char	*p, *p2;
1131
	char	str_buf[MAX_SYMBOL];
1132
 
1133
	TRACEPB("substitute", printf("(%s, %d, <%s>, %p, %p, %d)\n",
1134
		name, nargs, pr_str(rtext), argps, out, max_out));
1135
 
1136
	/* Make SURE the output buffer is never over-run. */
1137
	max_out -= 4;
1138
	if (max_out < 10) {
1139
		goto run_on;
1140
	}
1141
 
1142
	/*
1143
		Put the final replacement text into out[].
1144
		Replace ARG_FLAG n by arg n.
1145
		Replace POUND_FLAG n by the stringized arg n.
1146
		Delete CONCAT_FLAG and surrounding whitespace.
1147
	*/
1148
 
1149
	limit = strlen(rtext);
1150
	count = 0;
1151
	for (i = 0; i < limit;) {
1152
 
1153
		/*
1154
			Check for run-on expansion.
1155
			This is actually possible.
1156
		*/
1157
		if (count >= max_out) {
1158
			goto run_on;
1159
		}
1160
 
1161
		c = rtext[i++];
1162
		if (c == ARG_FLAG) {
1163
 
1164
			/* Copy the argument. */
1165
			arg_num = rtext[i++] - ARG_OFFSET;
1166
			if (arg_num >= nargs) {
1167
				/* Previous user error. */
1168
				out[count++] = ' ';
1169
				continue;
1170
			}
1171
 
1172
			if (count + strlen(argps[arg_num]) >= max_out) {
1173
				goto run_on;
1174
			}
1175
 
1176
			str_cpy(out + count, argps [arg_num]);
1177
			count += strlen(argps[arg_num]);
1178
		}
1179
		else if (c == POUND_FLAG) {
1180
 
1181
			/* Stringize an actual argument */
1182
			arg_num = rtext[i++] - ARG_OFFSET;
1183
			if (arg_num >= nargs) {
1184
				/* Previous user error. */
1185
				out[count++] = ' ';
1186
				continue;
1187
			}
1188
			out[count++] = '"';
1189
			for(p = argps[arg_num]; *p; ) {
1190
			/* bug fix: 2/14/89 (test for single quote also. */
1191
			if (*p == '"' || *p == '\'') {
1192
				/* Copy the string. */
1193
				p += in_string(p, &str_buf[0], MAX_SYMBOL);
1194
 
1195
				/* Stingize the string. */
1196
				for (p2 = &str_buf[0]; *p2; ) {
1197
					if (count >= max_out-1) {
1198
						goto run_on;
1199
					}
1200
					if (*p2 == '\\' || *p2 == '"') {
1201
						out[count++] = '\\';
1202
						out[count++] = *p2++;
1203
					}
1204
					else {
1205
						out[count++] = *p2++;
1206
					}
1207
				}
1208
			}
1209
			else {
1210
				out[count++] = *p++;
1211
			}
1212
			} /* end for */
1213
			out[count++] = '"';
1214
		}
1215
		else if (c == CONCAT_FLAG) {
1216
 
1217
			/* Delete preceding whitespace. */
1218
			while (count-1 >= 0 && out[count-1] == ' ') {
1219
				count--;
1220
			}
1221
 
1222
			/* Delete following whitespace. */
1223
			while (i < limit && rtext[i] == ' ') {
1224
				i++;
1225
			}
1226
		}
1227
		else {
1228
			out[count++] = c;
1229
		}
1230
	}
1231
 
1232
	/* Append current ch to output. */
1233
	out[count] = '\0';
1234
 
1235
	TRACEP("substitute",
1236
		printf("count: %d, out: <%s>\n", count, pr_str(out)));
1237
 
1238
	RETURN_VOID("substitute");
1239
 
1240
run_on:
1241
	strcpy(str_buf, "Macro expansion for ");
1242
	strcat(str_buf, name);
1243
	strcat(str_buf, " is too long...\nExpansion set to empty string");
1244
	error(str_buf);
1245
 
1246
	/* Truncate the ENTIRE macro. */
1247
	out[0] = '\0';
1248
	RETURN_VOID("substitute");
1249
}