155992Sbostic /*- 255992Sbostic * Copyright (c) 1992 Diomidis Spinellis. 355992Sbostic * Copyright (c) 1992 The Regents of the University of California. 455992Sbostic * All rights reserved. 555992Sbostic * 655992Sbostic * This code is derived from software contributed to Berkeley by 755992Sbostic * Diomidis Spinellis of Imperial College, University of London. 855992Sbostic * 955992Sbostic * %sccs.include.redist.c% 1055992Sbostic */ 1155992Sbostic 1255992Sbostic #ifndef lint 13*56004Sbostic static char sccsid[] = "@(#)compile.c 5.2 (Berkeley) 08/24/92"; 1455992Sbostic #endif /* not lint */ 1555992Sbostic 1655992Sbostic #include <sys/types.h> 1755992Sbostic #include <sys/stat.h> 1855992Sbostic 1955992Sbostic #include <ctype.h> 2055992Sbostic #include <errno.h> 2155992Sbostic #include <fcntl.h> 2255992Sbostic #include <limits.h> 2355992Sbostic #include <regex.h> 2455992Sbostic #include <stdio.h> 2555992Sbostic #include <stdlib.h> 2655992Sbostic #include <string.h> 2755992Sbostic 2855992Sbostic #include "defs.h" 2955992Sbostic #include "extern.h" 3055992Sbostic 3155992Sbostic static char *compile_addr __P((char *, struct s_addr *)); 3255992Sbostic static char *compile_delimited __P((char *, char *)); 3355992Sbostic static char *compile_flags __P((char *, struct s_subst *)); 3455992Sbostic static char *compile_re __P((char *, regex_t *, int)); 3555992Sbostic static char *compile_subst __P((char *, char **, size_t)); 3655992Sbostic static char *compile_text __P((void)); 3755992Sbostic static char *compile_tr __P((char *, char **)); 3855992Sbostic static struct s_command 3955992Sbostic **compile_stream __P((char *, struct s_command **, char *)); 4055992Sbostic static char *duptoeol __P((char *)); 4155992Sbostic static struct s_command 4255992Sbostic *findlabel __P((struct s_command *, struct s_command *)); 4355992Sbostic static void fixuplabel __P((struct s_command *, struct s_command *)); 4455992Sbostic 4555992Sbostic /* 4655992Sbostic * Command specification. This is used to drive the command parser. 4755992Sbostic */ 4855992Sbostic struct s_format { 4955992Sbostic char code; /* Command code */ 5055992Sbostic int naddr; /* Number of address args */ 5155992Sbostic enum e_args args; /* Argument type */ 5255992Sbostic }; 5355992Sbostic 5455992Sbostic static struct s_format cmd_fmts[] = { 5555992Sbostic {'{', 2, GROUP}, 5655992Sbostic {'a', 1, TEXT}, 5755992Sbostic {'b', 2, BRANCH}, 5855992Sbostic {'c', 2, TEXT}, 5955992Sbostic {'d', 2, EMPTY}, 6055992Sbostic {'D', 2, EMPTY}, 6155992Sbostic {'g', 2, EMPTY}, 6255992Sbostic {'G', 2, EMPTY}, 6355992Sbostic {'h', 2, EMPTY}, 6455992Sbostic {'H', 2, EMPTY}, 6555992Sbostic {'i', 1, TEXT}, 6655992Sbostic {'l', 2, EMPTY}, 6755992Sbostic {'n', 2, EMPTY}, 6855992Sbostic {'N', 2, EMPTY}, 6955992Sbostic {'p', 2, EMPTY}, 7055992Sbostic {'P', 2, EMPTY}, 7155992Sbostic {'q', 1, EMPTY}, 7255992Sbostic {'r', 1, RFILE}, 7355992Sbostic {'s', 2, SUBST}, 7455992Sbostic {'t', 2, BRANCH}, 7555992Sbostic {'w', 2, WFILE}, 7655992Sbostic {'x', 2, EMPTY}, 7755992Sbostic {'y', 2, TR}, 7855992Sbostic {'!', 2, NONSEL}, 7955992Sbostic {':', 0, LABEL}, 8055992Sbostic {'#', 0, COMMENT}, 8155992Sbostic {'=', 1, EMPTY}, 8255992Sbostic {'\0', 0, COMMENT}, 8355992Sbostic }; 8455992Sbostic 8555992Sbostic /* The compiled program */ 8655992Sbostic struct s_command *prog; 8755992Sbostic 8855992Sbostic /* 8955992Sbostic * Compile the program into prog. 9055992Sbostic * Initialise appends 9155992Sbostic */ 9255992Sbostic void 9355992Sbostic compile() 9455992Sbostic { 9555992Sbostic *compile_stream(NULL, &prog, NULL) = NULL; 9655992Sbostic fixuplabel(prog, prog); 9755992Sbostic appends = xmalloc(sizeof(struct s_appends) * appendnum); 9855992Sbostic } 9955992Sbostic 10055992Sbostic #define EATSPACE() do { \ 10155992Sbostic if (p) \ 10255992Sbostic while (*p && isascii(*p) && isspace(*p)) \ 10355992Sbostic p++; \ 10455992Sbostic } while (0) 10555992Sbostic 10655992Sbostic static struct s_command ** 10755992Sbostic compile_stream(terminator, link, p) 10855992Sbostic char *terminator; 10955992Sbostic struct s_command **link; 11055992Sbostic register char *p; 11155992Sbostic { 11255992Sbostic static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ 11355992Sbostic struct s_command *cmd, *cmd2; 11455992Sbostic struct s_format *fp; 11555992Sbostic int naddr; /* Number of addresses */ 11655992Sbostic 11755992Sbostic if (p != NULL) 11855992Sbostic goto semicolon; 11955992Sbostic for (;;) { 12055992Sbostic if ((p = cu_fgets(lbuf, sizeof(lbuf))) == NULL) { 12155992Sbostic if (terminator != NULL) 12255992Sbostic err(COMPILE, "unexpected EOF (pending }'s)"); 12355992Sbostic return (link); 12455992Sbostic } 12555992Sbostic 12655992Sbostic semicolon: EATSPACE(); 12755992Sbostic if (p && (*p == '#' || *p == '\0')) 12855992Sbostic continue; 12955992Sbostic if (*p == '}') { 13055992Sbostic if (terminator == NULL) 13155992Sbostic err(COMPILE, "unexpected }"); 13255992Sbostic return (link); 13355992Sbostic } 13455992Sbostic *link = cmd = xmalloc(sizeof(struct s_command)); 13555992Sbostic link = &cmd->next; 13655992Sbostic cmd->nonsel = cmd->inrange = 0; 13755992Sbostic /* First parse the addresses */ 13855992Sbostic naddr = 0; 13955992Sbostic cmd->a1 = cmd->a2 = NULL; 14055992Sbostic 14155992Sbostic /* Valid characters to start an address */ 14255992Sbostic #define addrchar(c) (strchr("0123456789/\\$", (c))) 14355992Sbostic if (addrchar(*p)) { 14455992Sbostic naddr++; 14555992Sbostic cmd->a1 = xmalloc(sizeof(struct s_addr)); 14655992Sbostic p = compile_addr(p, cmd->a1); 14755992Sbostic EATSPACE(); /* EXTENSION */ 14855992Sbostic if (*p == ',') { 14955992Sbostic naddr++; 15055992Sbostic p++; 15155992Sbostic EATSPACE(); /* EXTENSION */ 15255992Sbostic cmd->a2 = xmalloc(sizeof(struct s_addr)); 15355992Sbostic p = compile_addr(p, cmd->a2); 15455992Sbostic } 15555992Sbostic } 15655992Sbostic 15755992Sbostic nonsel: /* Now parse the command */ 15855992Sbostic EATSPACE(); 15955992Sbostic if (!*p) 16055992Sbostic err(COMPILE, "command expected"); 16155992Sbostic cmd->code = *p; 16255992Sbostic for (fp = cmd_fmts; fp->code; fp++) 16355992Sbostic if (fp->code == *p) 16455992Sbostic break; 16555992Sbostic if (!fp->code) 16655992Sbostic err(COMPILE, "invalid command code %c", *p); 16755992Sbostic if (naddr > fp->naddr) 16855992Sbostic err(COMPILE, 16955992Sbostic "command %c expects up to %d address(es), found %d", *p, fp->naddr, naddr); 17055992Sbostic switch (fp->args) { 17155992Sbostic case NONSEL: /* ! */ 17255992Sbostic cmd->nonsel = ! cmd->nonsel; 17355992Sbostic p++; 17455992Sbostic goto nonsel; 17555992Sbostic case GROUP: /* { */ 17655992Sbostic p++; 17755992Sbostic EATSPACE(); 17855992Sbostic if (!*p) 17955992Sbostic p = NULL; 18055992Sbostic cmd2 = xmalloc(sizeof(struct s_command)); 18155992Sbostic cmd2->code = '}'; 18255992Sbostic *compile_stream("}", &cmd->u.c, p) = cmd2; 18355992Sbostic cmd->next = cmd2; 18455992Sbostic link = &cmd2->next; 18555992Sbostic break; 18655992Sbostic case EMPTY: /* d D g G h H l n N p P q x = \0 */ 18755992Sbostic p++; 18855992Sbostic EATSPACE(); 18955992Sbostic if (*p == ';') { 19055992Sbostic p++; 19155992Sbostic link = &cmd->next; 19255992Sbostic goto semicolon; 19355992Sbostic } 19455992Sbostic if (*p) 19555992Sbostic err(COMPILE, 19655992Sbostic "extra characters at the end of %c command", cmd->code); 19755992Sbostic break; 19855992Sbostic case TEXT: /* a c i */ 19955992Sbostic p++; 20055992Sbostic EATSPACE(); 20155992Sbostic if (*p != '\\') 20255992Sbostic err(COMPILE, 20355992Sbostic "command %c expects \\ followed by text", cmd->code); 20455992Sbostic p++; 20555992Sbostic EATSPACE(); 20655992Sbostic if (*p) 20755992Sbostic err(COMPILE, 20855992Sbostic "extra characters after \\ at the end of %c command", cmd->code); 20955992Sbostic cmd->t = compile_text(); 21055992Sbostic break; 21155992Sbostic case COMMENT: /* \0 # */ 21255992Sbostic break; 21355992Sbostic case WFILE: /* w */ 21455992Sbostic p++; 21555992Sbostic EATSPACE(); 21655992Sbostic if (*p == '\0') 21755992Sbostic err(COMPILE, "filename expected"); 21855992Sbostic cmd->t = duptoeol(p); 21955992Sbostic if (aflag) 22055992Sbostic cmd->u.fd = -1; 22155992Sbostic else if ((cmd->u.fd = open(p, 22255992Sbostic O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 22355992Sbostic DEFFILEMODE)) == -1) 22455992Sbostic err(FATAL, "%s: %s\n", p, strerror(errno)); 22555992Sbostic break; 22655992Sbostic case RFILE: /* r */ 22755992Sbostic p++; 22855992Sbostic EATSPACE(); 22955992Sbostic if (*p == '\0') 23055992Sbostic err(COMPILE, "filename expected"); 23155992Sbostic else 23255992Sbostic cmd->t = duptoeol(p); 23355992Sbostic break; 23455992Sbostic case BRANCH: /* b t */ 23555992Sbostic p++; 23655992Sbostic EATSPACE(); 23755992Sbostic if (*p == '\0') 23855992Sbostic cmd->t = NULL; 23955992Sbostic else 24055992Sbostic cmd->t = duptoeol(p); 24155992Sbostic break; 24255992Sbostic case LABEL: /* : */ 24355992Sbostic p++; 24455992Sbostic EATSPACE(); 24555992Sbostic cmd->t = duptoeol(p); 24655992Sbostic if (strlen(p) == 0) 24755992Sbostic err(COMPILE, "empty label"); 24855992Sbostic break; 24955992Sbostic case SUBST: /* s */ 25055992Sbostic p++; 25155992Sbostic if (*p == '\0' || *p == '\\') 25255992Sbostic err(COMPILE, 25355992Sbostic "substitute pattern can not be delimited by newline or backslash"); 25455992Sbostic cmd->u.s = xmalloc(sizeof(struct s_subst)); 25555992Sbostic p = compile_re(p, &cmd->u.s->re, 0); 25655992Sbostic if (p == NULL) 257*56004Sbostic err(COMPILE, "unterminated substitute pattern"); 25855992Sbostic cmd->u.s->pmatch = xmalloc((cmd->u.s->re.re_nsub + 1) * 25955992Sbostic sizeof(regmatch_t)); 26055992Sbostic p--; 26155992Sbostic p = compile_subst(p, 26255992Sbostic &cmd->u.s->new, cmd->u.s->re.re_nsub); 26355992Sbostic if (p == NULL) 26455992Sbostic err(COMPILE, 26555992Sbostic "unterminated substitute replace in regular expression"); 26655992Sbostic p = compile_flags(p, cmd->u.s); 26755992Sbostic EATSPACE(); 26855992Sbostic if (*p == ';') { 26955992Sbostic p++; 27055992Sbostic link = &cmd->next; 27155992Sbostic goto semicolon; 27255992Sbostic } 27355992Sbostic break; 27455992Sbostic case TR: /* y */ 27555992Sbostic p++; 27655992Sbostic p = compile_tr(p, (char **)&cmd->u.y); 27755992Sbostic EATSPACE(); 27855992Sbostic if (*p == ';') { 27955992Sbostic p++; 28055992Sbostic link = &cmd->next; 28155992Sbostic goto semicolon; 28255992Sbostic } 28355992Sbostic if (*p) 28455992Sbostic err(COMPILE, 28555992Sbostic "extra text at the end of a transform command"); 28655992Sbostic break; 28755992Sbostic } 28855992Sbostic } 28955992Sbostic } 29055992Sbostic 29155992Sbostic /* 29255992Sbostic * Get a delimited string. P points to the delimeter of the string; d points 29355992Sbostic * to a buffer area. Newline and delimiter escapes are processed; other 29455992Sbostic * escapes are ignored. 29555992Sbostic * 29655992Sbostic * Returns a pointer to the first character after the final delimiter or NULL 29755992Sbostic * in the case of a non-terminated string. The character array d is filled 29855992Sbostic * with the processed string. 29955992Sbostic */ 30055992Sbostic static char * 30155992Sbostic compile_delimited(p, d) 30255992Sbostic char *p, *d; 30355992Sbostic { 30455992Sbostic char c; 30555992Sbostic 30655992Sbostic c = *p++; 30755992Sbostic if (c == '\0') 30855992Sbostic return (NULL); 30955992Sbostic else if (c == '\\') 31055992Sbostic err(COMPILE, "\\ can not be used as a string delimiter"); 31155992Sbostic else if (c == '\n') 31255992Sbostic err(COMPILE, "newline can not be used as a string delimiter"); 31355992Sbostic while (*p) { 31455992Sbostic if (*p == '\\' && p[1] == c) 31555992Sbostic p++; 31655992Sbostic else if (*p == '\\' && p[1] == 'n') { 31755992Sbostic *d++ = '\n'; 31855992Sbostic p += 2; 31955992Sbostic continue; 32055992Sbostic } else if (*p == c) { 32155992Sbostic *d = '\0'; 32255992Sbostic return (p + 1); 32355992Sbostic } 32455992Sbostic *d++ = *p++; 32555992Sbostic } 32655992Sbostic return (NULL); 32755992Sbostic } 32855992Sbostic 32955992Sbostic /* 33055992Sbostic * Get a regular expression. P points to the delimeter of the regular 33155992Sbostic * expression; d points a regexp pointer. Newline and delimiter escapes 33255992Sbostic * are processed; other escapes are ignored. 33355992Sbostic * Returns a pointer to the first character after the final delimiter 33455992Sbostic * or NULL in the case of a non terminated regular expression. 33555992Sbostic * The regexp pointer is set to the compiled regular expression. 33655992Sbostic * Cflags are passed to regcomp. 33755992Sbostic */ 33855992Sbostic static char * 33955992Sbostic compile_re(p, rep, cflags) 34055992Sbostic char *p; 34155992Sbostic regex_t *rep; 34255992Sbostic int cflags; 34355992Sbostic { 34455992Sbostic int eval; 34555992Sbostic char re[_POSIX2_LINE_MAX + 1]; 34655992Sbostic 34755992Sbostic p = compile_delimited(p, re); 34855992Sbostic if (p && (eval = regcomp(rep, re, cflags)) != 0) 34955992Sbostic err(COMPILE, "RE error: %s", strregerror(eval, rep)); 35055992Sbostic return (p); 35155992Sbostic } 35255992Sbostic 35355992Sbostic /* 35455992Sbostic * Compile the substitution string of a regular expression and set res to 35555992Sbostic * point to a saved copy of it. Nsub is the number of parenthesized regular 35655992Sbostic * expressions. 35755992Sbostic */ 35855992Sbostic static char * 35955992Sbostic compile_subst(p, res, nsub) 36055992Sbostic char *p, **res; 36155992Sbostic size_t nsub; 36255992Sbostic { 36355992Sbostic static char lbuf[_POSIX2_LINE_MAX + 1]; 36455992Sbostic int asize, size; 36555992Sbostic char c, *text, *op, *s; 36655992Sbostic 36755992Sbostic c = *p++; /* Terminator character */ 36855992Sbostic if (c == '\0') 36955992Sbostic return (NULL); 37055992Sbostic 37155992Sbostic asize = 2 * _POSIX2_LINE_MAX + 1; 37255992Sbostic text = xmalloc(asize); 37355992Sbostic size = 0; 37455992Sbostic do { 37555992Sbostic op = s = text + size; 37655992Sbostic for (; *p; p++) { 37755992Sbostic if (*p == '\\') { 37855992Sbostic p++; 37955992Sbostic if (strchr("123456789", *p) != NULL) { 38055992Sbostic *s++ = '\\'; 38155992Sbostic if (*p - '1' > nsub) 38255992Sbostic err(COMPILE, 38355992Sbostic "\\%c not defined in regular expression (use \\1-\\%d)", *p, nsub + 1); 38455992Sbostic } else if (*p == '&') 38555992Sbostic *s++ = '\\'; 38655992Sbostic } else if (*p == c) { 38755992Sbostic p++; 38855992Sbostic *s++ = '\0'; 38955992Sbostic size += s - op; 39055992Sbostic *res = xrealloc(text, size); 39155992Sbostic return (p); 39255992Sbostic } else if (*p == '\n') { 39355992Sbostic err(COMPILE, 39455992Sbostic "unescaped newline inside substitute pattern"); 39555992Sbostic return (NULL); 39655992Sbostic } 39755992Sbostic *s++ = *p; 39855992Sbostic } 39955992Sbostic size += s - op; 40055992Sbostic if (asize - size < _POSIX2_LINE_MAX + 1) { 40155992Sbostic asize *= 2; 40255992Sbostic text = xmalloc(asize); 40355992Sbostic } 40455992Sbostic } while (cu_fgets(p = lbuf, sizeof(lbuf))); 405*56004Sbostic err(COMPILE, "unterminated substitute pattern"); 40655992Sbostic return (NULL); 40755992Sbostic } 40855992Sbostic 40955992Sbostic /* 41055992Sbostic * Compile the flags of the s command 41155992Sbostic */ 41255992Sbostic static char * 41355992Sbostic compile_flags(p, s) 41455992Sbostic char *p; 41555992Sbostic struct s_subst *s; 41655992Sbostic { 41755992Sbostic int gn; /* True if we have seen g or n */ 41855992Sbostic char wfile[_POSIX2_LINE_MAX + 1], *q; 41955992Sbostic 42055992Sbostic s->n = 1; /* Default */ 42155992Sbostic s->p = 0; 42255992Sbostic s->wfile = NULL; 42355992Sbostic s->wfd = -1; 42455992Sbostic for (gn = 0;;) { 42555992Sbostic EATSPACE(); /* EXTENSION */ 42655992Sbostic switch (*p) { 42755992Sbostic case 'g': 42855992Sbostic if (gn) 429*56004Sbostic err(COMPILE, 430*56004Sbostic "more than one number or 'g' in substitute flags"); 43155992Sbostic gn = 1; 43255992Sbostic s->n = 0; 43355992Sbostic break; 43455992Sbostic case '\0': 43555992Sbostic case '\n': 43655992Sbostic case ';': 43755992Sbostic return (p); 43855992Sbostic case 'p': 43955992Sbostic s->p = 1; 44055992Sbostic break; 44155992Sbostic case '1': case '2': case '3': 44255992Sbostic case '4': case '5': case '6': 44355992Sbostic case '7': case '8': case '9': 44455992Sbostic if (gn) 445*56004Sbostic err(COMPILE, 446*56004Sbostic "more than one number or 'g' in substitute flags"); 44755992Sbostic gn = 1; 44855992Sbostic /* XXX Check for overflow */ 44955992Sbostic s->n = (int)strtol(p, &p, 10); 45055992Sbostic break; 45155992Sbostic case 'w': 45255992Sbostic p++; 45355992Sbostic #ifdef HISTORIC_PRACTICE 45455992Sbostic if (*p != ' ') { 45555992Sbostic err(WARNING, "space missing before w wfile"); 45655992Sbostic return (p); 45755992Sbostic } 45855992Sbostic #endif 45955992Sbostic EATSPACE(); 46055992Sbostic q = wfile; 46155992Sbostic while (*p) { 46255992Sbostic if (*p == '\n') 46355992Sbostic break; 46455992Sbostic *q++ = *p++; 46555992Sbostic } 46655992Sbostic *q = '\0'; 46755992Sbostic if (q == wfile) 468*56004Sbostic err(COMPILE, "no wfile specified"); 46955992Sbostic s->wfile = strdup(wfile); 47055992Sbostic if (!aflag && (s->wfd = open(wfile, 47155992Sbostic O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 47255992Sbostic DEFFILEMODE)) == -1) 47355992Sbostic err(FATAL, "%s: %s\n", wfile, strerror(errno)); 47455992Sbostic return (p); 47555992Sbostic default: 47655992Sbostic err(COMPILE, 477*56004Sbostic "bad flag in substitute command: '%c'", *p); 47855992Sbostic break; 47955992Sbostic } 48055992Sbostic p++; 48155992Sbostic } 48255992Sbostic } 48355992Sbostic 48455992Sbostic /* 48555992Sbostic * Compile a translation set of strings into a lookup table. 48655992Sbostic */ 48755992Sbostic static char * 48855992Sbostic compile_tr(p, transtab) 48955992Sbostic char *p; 49055992Sbostic char **transtab; 49155992Sbostic { 49255992Sbostic int i; 49355992Sbostic char *lt, *op, *np; 49455992Sbostic char old[_POSIX2_LINE_MAX + 1]; 49555992Sbostic char new[_POSIX2_LINE_MAX + 1]; 49655992Sbostic 49755992Sbostic if (*p == '\0' || *p == '\\') 49855992Sbostic err(COMPILE, 49955992Sbostic "transform pattern can not be delimited by newline or backslash"); 50055992Sbostic p = compile_delimited(p, old); 50155992Sbostic if (p == NULL) { 50255992Sbostic err(COMPILE, "unterminated transform source string"); 50355992Sbostic return (NULL); 50455992Sbostic } 50555992Sbostic p = compile_delimited(--p, new); 50655992Sbostic if (p == NULL) { 50755992Sbostic err(COMPILE, "unterminated transform target string"); 50855992Sbostic return (NULL); 50955992Sbostic } 51055992Sbostic EATSPACE(); 51155992Sbostic if (strlen(new) != strlen(old)) { 51255992Sbostic err(COMPILE, "transform strings are not the same length"); 51355992Sbostic return (NULL); 51455992Sbostic } 51555992Sbostic /* We assume characters are 8 bits */ 51655992Sbostic lt = xmalloc(UCHAR_MAX); 51755992Sbostic for (i = 0; i <= UCHAR_MAX; i++) 51855992Sbostic lt[i] = (char)i; 51955992Sbostic for (op = old, np = new; *op; op++, np++) 52055992Sbostic lt[(u_char)*op] = *np; 52155992Sbostic *transtab = lt; 52255992Sbostic return (p); 52355992Sbostic } 52455992Sbostic 52555992Sbostic /* 52655992Sbostic * Compile the text following an a or i command. 52755992Sbostic */ 52855992Sbostic static char * 52955992Sbostic compile_text() 53055992Sbostic { 53155992Sbostic int asize, size; 53255992Sbostic char *text, *p, *op, *s; 53355992Sbostic char lbuf[_POSIX2_LINE_MAX + 1]; 53455992Sbostic 53555992Sbostic asize = 2 * _POSIX2_LINE_MAX + 1; 53655992Sbostic text = xmalloc(asize); 53755992Sbostic size = 0; 53855992Sbostic while (cu_fgets(lbuf, sizeof(lbuf))) { 53955992Sbostic op = s = text + size; 54055992Sbostic p = lbuf; 54155992Sbostic EATSPACE(); 54255992Sbostic for (; *p; p++) { 54355992Sbostic if (*p == '\\') 54455992Sbostic p++; 54555992Sbostic *s++ = *p; 54655992Sbostic } 54755992Sbostic size += s - op; 54855992Sbostic if (p[-2] != '\\') { 54955992Sbostic *s = '\0'; 55055992Sbostic break; 55155992Sbostic } 55255992Sbostic if (asize - size < _POSIX2_LINE_MAX + 1) { 55355992Sbostic asize *= 2; 55455992Sbostic text = xmalloc(asize); 55555992Sbostic } 55655992Sbostic } 55755992Sbostic return (xrealloc(text, size + 1)); 55855992Sbostic } 55955992Sbostic 56055992Sbostic /* 56155992Sbostic * Get an address and return a pointer to the first character after 56255992Sbostic * it. Fill the structure pointed to according to the address. 56355992Sbostic */ 56455992Sbostic static char * 56555992Sbostic compile_addr(p, a) 56655992Sbostic char *p; 56755992Sbostic struct s_addr *a; 56855992Sbostic { 56955992Sbostic regex_t *re; 57055992Sbostic char *end; 57155992Sbostic 57255992Sbostic switch (*p) { 57355992Sbostic case '\\': /* Context address */ 57455992Sbostic re = xmalloc(sizeof(regex_t)); 57555992Sbostic a->u.r = re; 57655992Sbostic p = compile_re(p + 1, re, REG_NOSUB); 57755992Sbostic if (p == NULL) 57855992Sbostic err(COMPILE, "unterminated regular expression"); 57955992Sbostic a->type = AT_RE; 58055992Sbostic return (p); 58155992Sbostic case '/': /* Context address */ 58255992Sbostic re = xmalloc(sizeof(regex_t)); 58355992Sbostic a->u.r = re; 58455992Sbostic p = compile_re(p, a->u.r, REG_NOSUB); 58555992Sbostic if (p == NULL) 58655992Sbostic err(COMPILE, "unterminated regular expression"); 58755992Sbostic a->type = AT_RE; 58855992Sbostic return (p); 58955992Sbostic case '$': /* Last line */ 59055992Sbostic a->type = AT_LAST; 59155992Sbostic return (p + 1); 59255992Sbostic /* Line number */ 59355992Sbostic case '0': case '1': case '2': case '3': case '4': 59455992Sbostic case '5': case '6': case '7': case '8': case '9': 59555992Sbostic a->type = AT_LINE; 59655992Sbostic a->u.l = strtol(p, &end, 10); 59755992Sbostic return (end); 59855992Sbostic default: 59955992Sbostic err(COMPILE, "expected context address"); 60055992Sbostic return (NULL); 60155992Sbostic } 60255992Sbostic } 60355992Sbostic 60455992Sbostic /* 60555992Sbostic * Return a copy of all the characters up to \n or \0 60655992Sbostic */ 60755992Sbostic static char * 60855992Sbostic duptoeol(s) 60955992Sbostic register char *s; 61055992Sbostic { 61155992Sbostic size_t len; 61255992Sbostic char *start; 61355992Sbostic 61455992Sbostic for (start = s; *s != '\0' && *s != '\n'; ++s); 61555992Sbostic *s = '\0'; 61655992Sbostic len = s - start + 1; 61755992Sbostic return (memmove(xmalloc(len), start, len)); 61855992Sbostic } 61955992Sbostic 62055992Sbostic /* 62155992Sbostic * Find the label contained in the command l in the command linked list cp. 62255992Sbostic * L is excluded from the search. Return NULL if not found. 62355992Sbostic */ 62455992Sbostic static struct s_command * 62555992Sbostic findlabel(l, cp) 62655992Sbostic struct s_command *l, *cp; 62755992Sbostic { 62855992Sbostic struct s_command *r; 62955992Sbostic 63055992Sbostic for (; cp; cp = cp->next) 63155992Sbostic if (cp->code == ':' && cp != l && strcmp(l->t, cp->t) == 0) 63255992Sbostic return (cp); 63355992Sbostic else if (cp->code == '{' && (r = findlabel(l, cp->u.c))) 63455992Sbostic return (r); 63555992Sbostic return (NULL); 63655992Sbostic } 63755992Sbostic 63855992Sbostic /* 63955992Sbostic * Convert goto label names to addresses. 64055992Sbostic * Detect duplicate labels. 64155992Sbostic * Set appendnum to the number of a and r commands in the script. 64255992Sbostic * Free the memory used by labels in b and t commands (but not by :) 64355992Sbostic * Root is a pointer to the script linked list; cp points to the 64455992Sbostic * search start. 64555992Sbostic * TODO: Remove } nodes 64655992Sbostic */ 64755992Sbostic static void 64855992Sbostic fixuplabel(root, cp) 64955992Sbostic struct s_command *root, *cp; 65055992Sbostic { 65155992Sbostic struct s_command *cp2; 65255992Sbostic 65355992Sbostic for (; cp; cp = cp->next) 65455992Sbostic switch (cp->code) { 65555992Sbostic case 'a': 65655992Sbostic case 'r': 65755992Sbostic appendnum++; 65855992Sbostic break; 65955992Sbostic case 'b': 66055992Sbostic case 't': 66155992Sbostic if (cp->t == NULL) { 66255992Sbostic cp->u.c = NULL; 66355992Sbostic break; 66455992Sbostic } 66555992Sbostic if ((cp2 = findlabel(cp, root)) == NULL) 666*56004Sbostic err(COMPILE2, "undefined label '%s'", cp->t); 66755992Sbostic free(cp->t); 66855992Sbostic cp->u.c = cp2; 66955992Sbostic break; 67055992Sbostic case '{': 67155992Sbostic fixuplabel(root, cp->u.c); 67255992Sbostic break; 67355992Sbostic case ':': 67455992Sbostic if (findlabel(cp, root)) 67555992Sbostic err(COMPILE2, "duplicate label %s", cp->t); 67655992Sbostic break; 67755992Sbostic } 67855992Sbostic } 679