xref: /freebsd/usr.bin/sed/compile.c (revision 0fddbf874719b9bd50cf66ac26d1140bb3f2be69)
1 /*-
2  * Copyright (c) 1992 Diomidis Spinellis.
3  * Copyright (c) 1992, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Diomidis Spinellis of Imperial College, University of London.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)compile.c	8.1 (Berkeley) 6/6/93";
41 #endif
42 static const char rcsid[] =
43   "$FreeBSD$";
44 #endif /* not lint */
45 
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 
49 #include <ctype.h>
50 #include <err.h>
51 #include <fcntl.h>
52 #include <limits.h>
53 #include <regex.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 
58 #include "defs.h"
59 #include "extern.h"
60 
61 #define LHSZ	128
62 #define	LHMASK	(LHSZ - 1)
63 static struct labhash {
64 	struct	labhash *lh_next;
65 	u_int	lh_hash;
66 	struct	s_command *lh_cmd;
67 	int	lh_ref;
68 } *labels[LHSZ];
69 
70 static char	 *compile_addr __P((char *, struct s_addr *));
71 static char	 *compile_ccl __P((char **, char *));
72 static char	 *compile_delimited __P((char *, char *));
73 static char	 *compile_flags __P((char *, struct s_subst *));
74 static char	 *compile_re __P((char *, regex_t **));
75 static char	 *compile_subst __P((char *, struct s_subst *));
76 static char	 *compile_text __P((void));
77 static char	 *compile_tr __P((char *, char **));
78 static struct s_command
79 		**compile_stream __P((struct s_command **));
80 static char	 *duptoeol __P((char *, char *));
81 static void	  enterlabel __P((struct s_command *));
82 static struct s_command
83 		 *findlabel __P((char *));
84 static void	  fixuplabel __P((struct s_command *, struct s_command *));
85 static void	  uselabel __P((void));
86 
87 /*
88  * Command specification.  This is used to drive the command parser.
89  */
90 struct s_format {
91 	char code;				/* Command code */
92 	int naddr;				/* Number of address args */
93 	enum e_args args;			/* Argument type */
94 };
95 
96 static struct s_format cmd_fmts[] = {
97 	{'{', 2, GROUP},
98 	{'}', 0, ENDGROUP},
99 	{'a', 1, TEXT},
100 	{'b', 2, BRANCH},
101 	{'c', 2, TEXT},
102 	{'d', 2, EMPTY},
103 	{'D', 2, EMPTY},
104 	{'g', 2, EMPTY},
105 	{'G', 2, EMPTY},
106 	{'h', 2, EMPTY},
107 	{'H', 2, EMPTY},
108 	{'i', 1, TEXT},
109 	{'l', 2, EMPTY},
110 	{'n', 2, EMPTY},
111 	{'N', 2, EMPTY},
112 	{'p', 2, EMPTY},
113 	{'P', 2, EMPTY},
114 	{'q', 1, EMPTY},
115 	{'r', 1, RFILE},
116 	{'s', 2, SUBST},
117 	{'t', 2, BRANCH},
118 	{'w', 2, WFILE},
119 	{'x', 2, EMPTY},
120 	{'y', 2, TR},
121 	{'!', 2, NONSEL},
122 	{':', 0, LABEL},
123 	{'#', 0, COMMENT},
124 	{'=', 1, EMPTY},
125 	{'\0', 0, COMMENT},
126 };
127 
128 /* The compiled program. */
129 struct s_command *prog;
130 
131 /*
132  * Compile the program into prog.
133  * Initialise appends.
134  */
135 void
136 compile()
137 {
138 	*compile_stream(&prog) = NULL;
139 	fixuplabel(prog, NULL);
140 	uselabel();
141 	if ((appends = malloc(sizeof(struct s_appends) * appendnum)) == NULL)
142 		err(1, "malloc");
143 	if ((match = malloc((maxnsub + 1) * sizeof(regmatch_t))) == NULL)
144 		err(1, "malloc");
145 }
146 
147 #define EATSPACE() do {							\
148 	if (p)								\
149 		while (*p && isspace((unsigned char)*p))                \
150 			p++;						\
151 	} while (0)
152 
153 static struct s_command **
154 compile_stream(link)
155 	struct s_command **link;
156 {
157 	register char *p;
158 	static char lbuf[_POSIX2_LINE_MAX + 1];	/* To save stack */
159 	struct s_command *cmd, *cmd2, *stack;
160 	struct s_format *fp;
161 	int naddr;				/* Number of addresses */
162 
163 	stack = 0;
164 	for (;;) {
165 		if ((p = cu_fgets(lbuf, sizeof(lbuf), NULL)) == NULL) {
166 			if (stack != 0)
167 				errx(1, "%lu: %s: unexpected EOF (pending }'s)",
168 							linenum, fname);
169 			return (link);
170 		}
171 
172 semicolon:	EATSPACE();
173 		if (p && (*p == '#' || *p == '\0'))
174 			continue;
175 		if ((*link = cmd = malloc(sizeof(struct s_command))) == NULL)
176 			err(1, "malloc");
177 		link = &cmd->next;
178 		cmd->nonsel = cmd->inrange = 0;
179 		/* First parse the addresses */
180 		naddr = 0;
181 
182 /* Valid characters to start an address */
183 #define	addrchar(c)	(strchr("0123456789/\\$", (c)))
184 		if (addrchar(*p)) {
185 			naddr++;
186 			if ((cmd->a1 = malloc(sizeof(struct s_addr))) == NULL)
187 				err(1, "malloc");
188 			p = compile_addr(p, cmd->a1);
189 			EATSPACE();				/* EXTENSION */
190 			if (*p == ',') {
191 				p++;
192 				EATSPACE();			/* EXTENSION */
193 				naddr++;
194 				if ((cmd->a2 = malloc(sizeof(struct s_addr)))
195 				    == NULL)
196 					err(1, "malloc");
197 				p = compile_addr(p, cmd->a2);
198 				EATSPACE();
199 			} else
200 				cmd->a2 = 0;
201 		} else
202 			cmd->a1 = cmd->a2 = 0;
203 
204 nonsel:		/* Now parse the command */
205 		if (!*p)
206 			errx(1, "%lu: %s: command expected", linenum, fname);
207 		cmd->code = *p;
208 		for (fp = cmd_fmts; fp->code; fp++)
209 			if (fp->code == *p)
210 				break;
211 		if (!fp->code)
212 			errx(1, "%lu: %s: invalid command code %c", linenum, fname, *p);
213 		if (naddr > fp->naddr)
214 			errx(1,
215 				"%lu: %s: command %c expects up to %d address(es), found %d",
216 				linenum, fname, *p, fp->naddr, naddr);
217 		switch (fp->args) {
218 		case NONSEL:			/* ! */
219 			p++;
220 			EATSPACE();
221 			cmd->nonsel = ! cmd->nonsel;
222 			goto nonsel;
223 		case GROUP:			/* { */
224 			p++;
225 			EATSPACE();
226 			cmd->next = stack;
227 			stack = cmd;
228 			link = &cmd->u.c;
229 			if (*p)
230 				goto semicolon;
231 			break;
232 		case ENDGROUP:
233 			/*
234 			 * Short-circuit command processing, since end of
235 			 * group is really just a noop.
236 			 */
237 			cmd->nonsel = 1;
238 			if (stack == 0)
239 				errx(1, "%lu: %s: unexpected }", linenum, fname);
240 			cmd2 = stack;
241 			stack = cmd2->next;
242 			cmd2->next = cmd;
243 			/*FALLTHROUGH*/
244 		case EMPTY:		/* d D g G h H l n N p P q x = \0 */
245 			p++;
246 			EATSPACE();
247 			if (*p == ';') {
248 				p++;
249 				link = &cmd->next;
250 				goto semicolon;
251 			}
252 			if (*p)
253 				errx(1, "%lu: %s: extra characters at the end of %c command",
254 						linenum, fname, cmd->code);
255 			break;
256 		case TEXT:			/* a c i */
257 			p++;
258 			EATSPACE();
259 			if (*p != '\\')
260 				errx(1,
261 "%lu: %s: command %c expects \\ followed by text", linenum, fname, cmd->code);
262 			p++;
263 			EATSPACE();
264 			if (*p)
265 				errx(1,
266 				"%lu: %s: extra characters after \\ at the end of %c command",
267 				linenum, fname, cmd->code);
268 			cmd->t = compile_text();
269 			break;
270 		case COMMENT:			/* \0 # */
271 			break;
272 		case WFILE:			/* w */
273 			p++;
274 			EATSPACE();
275 			if (*p == '\0')
276 				errx(1, "%lu: %s: filename expected", linenum, fname);
277 			cmd->t = duptoeol(p, "w command");
278 			if (aflag)
279 				cmd->u.fd = -1;
280 			else if ((cmd->u.fd = open(p,
281 			    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
282 			    DEFFILEMODE)) == -1)
283 				err(1, "%s", p);
284 			break;
285 		case RFILE:			/* r */
286 			p++;
287 			EATSPACE();
288 			if (*p == '\0')
289 				errx(1, "%lu: %s: filename expected", linenum, fname);
290 			else
291 				cmd->t = duptoeol(p, "read command");
292 			break;
293 		case BRANCH:			/* b t */
294 			p++;
295 			EATSPACE();
296 			if (*p == '\0')
297 				cmd->t = NULL;
298 			else
299 				cmd->t = duptoeol(p, "branch");
300 			break;
301 		case LABEL:			/* : */
302 			p++;
303 			EATSPACE();
304 			cmd->t = duptoeol(p, "label");
305 			if (strlen(p) == 0)
306 				errx(1, "%lu: %s: empty label", linenum, fname);
307 			enterlabel(cmd);
308 			break;
309 		case SUBST:			/* s */
310 			p++;
311 			if (*p == '\0' || *p == '\\')
312 				errx(1,
313 "%lu: %s: substitute pattern can not be delimited by newline or backslash",
314 					linenum, fname);
315 			if ((cmd->u.s = malloc(sizeof(struct s_subst))) == NULL)
316 				err(1, "malloc");
317 			p = compile_re(p, &cmd->u.s->re);
318 			if (p == NULL)
319 				errx(1,
320 				"%lu: %s: unterminated substitute pattern", linenum, fname);
321 			--p;
322 			p = compile_subst(p, cmd->u.s);
323 			p = compile_flags(p, cmd->u.s);
324 			EATSPACE();
325 			if (*p == ';') {
326 				p++;
327 				link = &cmd->next;
328 				goto semicolon;
329 			}
330 			break;
331 		case TR:			/* y */
332 			p++;
333 			p = compile_tr(p, (char **)&cmd->u.y);
334 			EATSPACE();
335 			if (*p == ';') {
336 				p++;
337 				link = &cmd->next;
338 				goto semicolon;
339 			}
340 			if (*p)
341 				errx(1,
342 "%lu: %s: extra text at the end of a transform command", linenum, fname);
343 			break;
344 		}
345 	}
346 }
347 
348 /*
349  * Get a delimited string.  P points to the delimeter of the string; d points
350  * to a buffer area.  Newline and delimiter escapes are processed; other
351  * escapes are ignored.
352  *
353  * Returns a pointer to the first character after the final delimiter or NULL
354  * in the case of a non-terminated string.  The character array d is filled
355  * with the processed string.
356  */
357 static char *
358 compile_delimited(p, d)
359 	char *p, *d;
360 {
361 	char c;
362 
363 	c = *p++;
364 	if (c == '\0')
365 		return (NULL);
366 	else if (c == '\\')
367 		errx(1, "%lu: %s: \\ can not be used as a string delimiter",
368 				linenum, fname);
369 	else if (c == '\n')
370 		errx(1, "%lu: %s: newline can not be used as a string delimiter",
371 				linenum, fname);
372 	while (*p) {
373 		if (*p == '[') {
374 			if ((d = compile_ccl(&p, d)) == NULL)
375 				errx(1, "%lu: %s: unbalanced brackets ([])", linenum, fname);
376 			continue;
377 		} else if (*p == '\\' && p[1] == '[') {
378 			*d++ = *p++;
379 		} else if (*p == '\\' && p[1] == c)
380 			p++;
381 		else if (*p == '\\' && p[1] == 'n') {
382 			*d++ = '\n';
383 			p += 2;
384 			continue;
385 		} else if (*p == '\\' && p[1] == '\\')
386 			*d++ = *p++;
387 		else if (*p == c) {
388 			*d = '\0';
389 			return (p + 1);
390 		}
391 		*d++ = *p++;
392 	}
393 	return (NULL);
394 }
395 
396 
397 /* compile_ccl: expand a POSIX character class */
398 static char *
399 compile_ccl(sp, t)
400 	char **sp;
401 	char *t;
402 {
403 	int c, d;
404 	char *s = *sp;
405 
406 	*t++ = *s++;
407 	if (*s == '^')
408 		*t++ = *s++;
409 	if (*s == ']')
410 		*t++ = *s++;
411 	for (; *s && (*t = *s) != ']'; s++, t++)
412 		if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) {
413 			*++t = *++s, t++, s++;
414 			for (c = *s; (*t = *s) != ']' || c != d; s++, t++)
415 				if ((c = *s) == '\0')
416 					return NULL;
417 		} else if (*s == '\\' && s[1] == 'n')
418 			    *t = '\n', s++;
419 	return (*s == ']') ? *sp = ++s, ++t : NULL;
420 }
421 
422 /*
423  * Get a regular expression.  P points to the delimiter of the regular
424  * expression; repp points to the address of a regexp pointer.  Newline
425  * and delimiter escapes are processed; other escapes are ignored.
426  * Returns a pointer to the first character after the final delimiter
427  * or NULL in the case of a non terminated regular expression.  The regexp
428  * pointer is set to the compiled regular expression.
429  * Cflags are passed to regcomp.
430  */
431 static char *
432 compile_re(p, repp)
433 	char *p;
434 	regex_t **repp;
435 {
436 	int eval;
437 	char re[_POSIX2_LINE_MAX + 1];
438 
439 	p = compile_delimited(p, re);
440 	if (p && strlen(re) == 0) {
441 		*repp = NULL;
442 		return (p);
443 	}
444 	if ((*repp = malloc(sizeof(regex_t))) == NULL)
445 		err(1, "malloc");
446 	if (p && (eval = regcomp(*repp, re, rflags)) != 0)
447 		errx(1, "%lu: %s: RE error: %s",
448 				linenum, fname, strregerror(eval, *repp));
449 	if (maxnsub < (*repp)->re_nsub)
450 		maxnsub = (*repp)->re_nsub;
451 	return (p);
452 }
453 
454 /*
455  * Compile the substitution string of a regular expression and set res to
456  * point to a saved copy of it.  Nsub is the number of parenthesized regular
457  * expressions.
458  */
459 static char *
460 compile_subst(p, s)
461 	char *p;
462 	struct s_subst *s;
463 {
464 	static char lbuf[_POSIX2_LINE_MAX + 1];
465 	int asize, ref, size;
466 	char c, *text, *op, *sp;
467 	int more = 0;
468 
469 	c = *p++;			/* Terminator character */
470 	if (c == '\0')
471 		return (NULL);
472 
473 	s->maxbref = 0;
474 	s->linenum = linenum;
475 	asize = 2 * _POSIX2_LINE_MAX + 1;
476 	if ((text = malloc(asize)) == NULL)
477 		err(1, "malloc");
478 	size = 0;
479 	do {
480 		op = sp = text + size;
481 		for (; *p; p++) {
482 			if (*p == '\\') {
483 				p++;
484 				if (strchr("123456789", *p) != NULL) {
485 					*sp++ = '\\';
486 					ref = *p - '0';
487 					if (s->re != NULL &&
488 					    ref > s->re->re_nsub)
489 						errx(1, "%lu: %s: \\%c not defined in the RE",
490 								linenum, fname, *p);
491 					if (s->maxbref < ref)
492 						s->maxbref = ref;
493 				} else if (*p == '&' || *p == '\\')
494 					*sp++ = '\\';
495 			} else if (*p == c) {
496 				if (*++p == '\0' && more) {
497 					if (cu_fgets(lbuf, sizeof(lbuf), &more))
498 						p = lbuf;
499 				}
500 				*sp++ = '\0';
501 				size += sp - op;
502 				if ((s->new = realloc(text, size)) == NULL)
503 					err(1, "realloc");
504 				return (p);
505 			} else if (*p == '\n') {
506 				errx(1,
507 "%lu: %s: unescaped newline inside substitute pattern", linenum, fname);
508 				/* NOTREACHED */
509 			}
510 			*sp++ = *p;
511 		}
512 		size += sp - op;
513 		if (asize - size < _POSIX2_LINE_MAX + 1) {
514 			asize *= 2;
515 			if ((text = realloc(text, asize)) == NULL)
516 				err(1, "realloc");
517 		}
518 	} while (cu_fgets(p = lbuf, sizeof(lbuf), &more));
519 	errx(1, "%lu: %s: unterminated substitute in regular expression",
520 			linenum, fname);
521 	/* NOTREACHED */
522 }
523 
524 /*
525  * Compile the flags of the s command
526  */
527 static char *
528 compile_flags(p, s)
529 	char *p;
530 	struct s_subst *s;
531 {
532 	int gn;			/* True if we have seen g or n */
533 	char wfile[_POSIX2_LINE_MAX + 1], *q;
534 
535 	s->n = 1;				/* Default */
536 	s->p = 0;
537 	s->wfile = NULL;
538 	s->wfd = -1;
539 	for (gn = 0;;) {
540 		EATSPACE();			/* EXTENSION */
541 		switch (*p) {
542 		case 'g':
543 			if (gn)
544 				errx(1,
545 "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname);
546 			gn = 1;
547 			s->n = 0;
548 			break;
549 		case '\0':
550 		case '\n':
551 		case ';':
552 			return (p);
553 		case 'p':
554 			s->p = 1;
555 			break;
556 		case '1': case '2': case '3':
557 		case '4': case '5': case '6':
558 		case '7': case '8': case '9':
559 			if (gn)
560 				errx(1,
561 "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname);
562 			gn = 1;
563 			/* XXX Check for overflow */
564 			s->n = (int)strtol(p, &p, 10);
565 			break;
566 		case 'w':
567 			p++;
568 #ifdef HISTORIC_PRACTICE
569 			if (*p != ' ') {
570 				warnx("%lu: %s: space missing before w wfile", linenum, fname);
571 				return (p);
572 			}
573 #endif
574 			EATSPACE();
575 			q = wfile;
576 			while (*p) {
577 				if (*p == '\n')
578 					break;
579 				*q++ = *p++;
580 			}
581 			*q = '\0';
582 			if (q == wfile)
583 				errx(1, "%lu: %s: no wfile specified", linenum, fname);
584 			s->wfile = strdup(wfile);
585 			if (!aflag && (s->wfd = open(wfile,
586 			    O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
587 			    DEFFILEMODE)) == -1)
588 				err(1, "%s", wfile);
589 			return (p);
590 		default:
591 			errx(1, "%lu: %s: bad flag in substitute command: '%c'",
592 					linenum, fname, *p);
593 			break;
594 		}
595 		p++;
596 	}
597 }
598 
599 /*
600  * Compile a translation set of strings into a lookup table.
601  */
602 static char *
603 compile_tr(p, transtab)
604 	char *p;
605 	char **transtab;
606 {
607 	int i;
608 	char *lt, *op, *np;
609 	char old[_POSIX2_LINE_MAX + 1];
610 	char new[_POSIX2_LINE_MAX + 1];
611 
612 	if (*p == '\0' || *p == '\\')
613 		errx(1,
614 	"%lu: %s: transform pattern can not be delimited by newline or backslash",
615 			linenum, fname);
616 	p = compile_delimited(p, old);
617 	if (p == NULL)
618 		errx(1, "%lu: %s: unterminated transform source string",
619 				linenum, fname);
620 	p = compile_delimited(--p, new);
621 	if (p == NULL)
622 		errx(1, "%lu: %s: unterminated transform target string",
623 				linenum, fname);
624 	EATSPACE();
625 	if (strlen(new) != strlen(old))
626 		errx(1, "%lu: %s: transform strings are not the same length",
627 				linenum, fname);
628 	/* We assume characters are 8 bits */
629 	if ((lt = malloc(UCHAR_MAX)) == NULL)
630 		err(1, "malloc");
631 	for (i = 0; i <= UCHAR_MAX; i++)
632 		lt[i] = (char)i;
633 	for (op = old, np = new; *op; op++, np++)
634 		lt[(u_char)*op] = *np;
635 	*transtab = lt;
636 	return (p);
637 }
638 
639 /*
640  * Compile the text following an a or i command.
641  */
642 static char *
643 compile_text()
644 {
645 	int asize, esc_nl, size;
646 	char *text, *p, *op, *s;
647 	char lbuf[_POSIX2_LINE_MAX + 1];
648 
649 	asize = 2 * _POSIX2_LINE_MAX + 1;
650 	if ((text = malloc(asize)) == NULL)
651 		err(1, "malloc");
652 	size = 0;
653 	while (cu_fgets(lbuf, sizeof(lbuf), NULL)) {
654 		op = s = text + size;
655 		p = lbuf;
656 		EATSPACE();
657 		for (esc_nl = 0; *p != '\0'; p++) {
658 			if (*p == '\\' && p[1] != '\0' && *++p == '\n')
659 				esc_nl = 1;
660 			*s++ = *p;
661 		}
662 		size += s - op;
663 		if (!esc_nl) {
664 			*s = '\0';
665 			break;
666 		}
667 		if (asize - size < _POSIX2_LINE_MAX + 1) {
668 			asize *= 2;
669 			if ((text = realloc(text, asize)) == NULL)
670 				err(1, "realloc");
671 		}
672 	}
673 	text[size] = '\0';
674 	if ((p = realloc(text, size + 1)) == NULL)
675 		err(1, "realloc");
676 	return (p);
677 }
678 
679 /*
680  * Get an address and return a pointer to the first character after
681  * it.  Fill the structure pointed to according to the address.
682  */
683 static char *
684 compile_addr(p, a)
685 	char *p;
686 	struct s_addr *a;
687 {
688 	char *end;
689 
690 	switch (*p) {
691 	case '\\':				/* Context address */
692 		++p;
693 		/* FALLTHROUGH */
694 	case '/':				/* Context address */
695 		p = compile_re(p, &a->u.r);
696 		if (p == NULL)
697 			errx(1, "%lu: %s: unterminated regular expression", linenum, fname);
698 		a->type = AT_RE;
699 		return (p);
700 
701 	case '$':				/* Last line */
702 		a->type = AT_LAST;
703 		return (p + 1);
704 						/* Line number */
705 	case '0': case '1': case '2': case '3': case '4':
706 	case '5': case '6': case '7': case '8': case '9':
707 		a->type = AT_LINE;
708 		a->u.l = strtol(p, &end, 10);
709 		return (end);
710 	default:
711 		errx(1, "%lu: %s: expected context address", linenum, fname);
712 		return (NULL);
713 	}
714 }
715 
716 /*
717  * duptoeol --
718  *	Return a copy of all the characters up to \n or \0.
719  */
720 static char *
721 duptoeol(s, ctype)
722 	register char *s;
723 	char *ctype;
724 {
725 	size_t len;
726 	int ws;
727 	char *p, *start;
728 
729 	ws = 0;
730 	for (start = s; *s != '\0' && *s != '\n'; ++s)
731 		ws = isspace((unsigned char)*s);
732 	*s = '\0';
733 	if (ws)
734 		warnx("%lu: %s: whitespace after %s", linenum, fname, ctype);
735 	len = s - start + 1;
736 	if ((p = malloc(len)) == NULL)
737 		err(1, "malloc");
738 	return (memmove(p, start, len));
739 }
740 
741 /*
742  * Convert goto label names to addresses, and count a and r commands, in
743  * the given subset of the script.  Free the memory used by labels in b
744  * and t commands (but not by :).
745  *
746  * TODO: Remove } nodes
747  */
748 static void
749 fixuplabel(cp, end)
750 	struct s_command *cp, *end;
751 {
752 
753 	for (; cp != end; cp = cp->next)
754 		switch (cp->code) {
755 		case 'a':
756 		case 'r':
757 			appendnum++;
758 			break;
759 		case 'b':
760 		case 't':
761 			/* Resolve branch target. */
762 			if (cp->t == NULL) {
763 				cp->u.c = NULL;
764 				break;
765 			}
766 			if ((cp->u.c = findlabel(cp->t)) == NULL)
767 				errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t);
768 			free(cp->t);
769 			break;
770 		case '{':
771 			/* Do interior commands. */
772 			fixuplabel(cp->u.c, cp->next);
773 			break;
774 		}
775 }
776 
777 /*
778  * Associate the given command label for later lookup.
779  */
780 static void
781 enterlabel(cp)
782 	struct s_command *cp;
783 {
784 	register struct labhash **lhp, *lh;
785 	register u_char *p;
786 	register u_int h, c;
787 
788 	for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++)
789 		h = (h << 5) + h + c;
790 	lhp = &labels[h & LHMASK];
791 	for (lh = *lhp; lh != NULL; lh = lh->lh_next)
792 		if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0)
793 			errx(1, "%lu: %s: duplicate label '%s'", linenum, fname, cp->t);
794 	if ((lh = malloc(sizeof *lh)) == NULL)
795 		err(1, "malloc");
796 	lh->lh_next = *lhp;
797 	lh->lh_hash = h;
798 	lh->lh_cmd = cp;
799 	lh->lh_ref = 0;
800 	*lhp = lh;
801 }
802 
803 /*
804  * Find the label contained in the command l in the command linked
805  * list cp.  L is excluded from the search.  Return NULL if not found.
806  */
807 static struct s_command *
808 findlabel(name)
809 	char *name;
810 {
811 	register struct labhash *lh;
812 	register u_char *p;
813 	register u_int h, c;
814 
815 	for (h = 0, p = (u_char *)name; (c = *p) != 0; p++)
816 		h = (h << 5) + h + c;
817 	for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) {
818 		if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) {
819 			lh->lh_ref = 1;
820 			return (lh->lh_cmd);
821 		}
822 	}
823 	return (NULL);
824 }
825 
826 /*
827  * Warn about any unused labels.  As a side effect, release the label hash
828  * table space.
829  */
830 static void
831 uselabel()
832 {
833 	register struct labhash *lh, *next;
834 	register int i;
835 
836 	for (i = 0; i < LHSZ; i++) {
837 		for (lh = labels[i]; lh != NULL; lh = next) {
838 			next = lh->lh_next;
839 			if (!lh->lh_ref)
840 				warnx("%lu: %s: unused label '%s'",
841 				    linenum, fname, lh->lh_cmd->t);
842 			free(lh);
843 		}
844 	}
845 }
846