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