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