home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / sed / compile.c next >
Encoding:
C/C++ Source or Header  |  1992-12-02  |  16.5 KB  |  708 lines

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