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*56663Sbostic static char sccsid[] = "@(#)compile.c 5.6 (Berkeley) 11/02/92"; 1455992Sbostic #endif /* not lint */ 1555992Sbostic 1655992Sbostic #include <sys/types.h> 1756019Sbostic #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 *)); 3456077Sbostic static char *compile_re __P((char *, regex_t **)); 3556019Sbostic static char *compile_subst __P((char *, struct s_subst *)); 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 *)); 4356093Sbostic static void fixuplabel __P((struct s_command *, struct s_command *, 4456093Sbostic struct s_command *)); 4555992Sbostic 4655992Sbostic /* 4755992Sbostic * Command specification. This is used to drive the command parser. 4855992Sbostic */ 4955992Sbostic struct s_format { 5055992Sbostic char code; /* Command code */ 5155992Sbostic int naddr; /* Number of address args */ 5255992Sbostic enum e_args args; /* Argument type */ 5355992Sbostic }; 5455992Sbostic 5555992Sbostic static struct s_format cmd_fmts[] = { 5655992Sbostic {'{', 2, GROUP}, 5755992Sbostic {'a', 1, TEXT}, 5855992Sbostic {'b', 2, BRANCH}, 5955992Sbostic {'c', 2, TEXT}, 6055992Sbostic {'d', 2, EMPTY}, 6155992Sbostic {'D', 2, EMPTY}, 6255992Sbostic {'g', 2, EMPTY}, 6355992Sbostic {'G', 2, EMPTY}, 6455992Sbostic {'h', 2, EMPTY}, 6555992Sbostic {'H', 2, EMPTY}, 6655992Sbostic {'i', 1, TEXT}, 6755992Sbostic {'l', 2, EMPTY}, 6855992Sbostic {'n', 2, EMPTY}, 6955992Sbostic {'N', 2, EMPTY}, 7055992Sbostic {'p', 2, EMPTY}, 7155992Sbostic {'P', 2, EMPTY}, 7255992Sbostic {'q', 1, EMPTY}, 7355992Sbostic {'r', 1, RFILE}, 7455992Sbostic {'s', 2, SUBST}, 7555992Sbostic {'t', 2, BRANCH}, 7655992Sbostic {'w', 2, WFILE}, 7755992Sbostic {'x', 2, EMPTY}, 7855992Sbostic {'y', 2, TR}, 7955992Sbostic {'!', 2, NONSEL}, 8055992Sbostic {':', 0, LABEL}, 8155992Sbostic {'#', 0, COMMENT}, 8255992Sbostic {'=', 1, EMPTY}, 8355992Sbostic {'\0', 0, COMMENT}, 8455992Sbostic }; 8555992Sbostic 8656019Sbostic /* The compiled program. */ 8755992Sbostic struct s_command *prog; 8855992Sbostic 8955992Sbostic /* 9055992Sbostic * Compile the program into prog. 9156019Sbostic * Initialise appends. 9255992Sbostic */ 9355992Sbostic void 9455992Sbostic compile() 9555992Sbostic { 9655992Sbostic *compile_stream(NULL, &prog, NULL) = NULL; 9756093Sbostic fixuplabel(prog, prog, NULL); 9855992Sbostic appends = xmalloc(sizeof(struct s_appends) * appendnum); 9956077Sbostic match = xmalloc((maxnsub + 1) * sizeof(regmatch_t)); 10055992Sbostic } 10155992Sbostic 10255992Sbostic #define EATSPACE() do { \ 10355992Sbostic if (p) \ 10455992Sbostic while (*p && isascii(*p) && isspace(*p)) \ 10555992Sbostic p++; \ 10655992Sbostic } while (0) 10755992Sbostic 10855992Sbostic static struct s_command ** 10955992Sbostic compile_stream(terminator, link, p) 11055992Sbostic char *terminator; 11155992Sbostic struct s_command **link; 11255992Sbostic register char *p; 11355992Sbostic { 11455992Sbostic static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ 11555992Sbostic struct s_command *cmd, *cmd2; 11655992Sbostic struct s_format *fp; 11755992Sbostic int naddr; /* Number of addresses */ 11855992Sbostic 11955992Sbostic if (p != NULL) 12055992Sbostic goto semicolon; 12155992Sbostic for (;;) { 12255992Sbostic if ((p = cu_fgets(lbuf, sizeof(lbuf))) == NULL) { 12355992Sbostic if (terminator != NULL) 12455992Sbostic err(COMPILE, "unexpected EOF (pending }'s)"); 12555992Sbostic return (link); 12655992Sbostic } 12755992Sbostic 12855992Sbostic semicolon: EATSPACE(); 12955992Sbostic if (p && (*p == '#' || *p == '\0')) 13055992Sbostic continue; 13155992Sbostic if (*p == '}') { 13255992Sbostic if (terminator == NULL) 13355992Sbostic err(COMPILE, "unexpected }"); 13455992Sbostic return (link); 13555992Sbostic } 13655992Sbostic *link = cmd = xmalloc(sizeof(struct s_command)); 13755992Sbostic link = &cmd->next; 13855992Sbostic cmd->nonsel = cmd->inrange = 0; 13955992Sbostic /* First parse the addresses */ 14055992Sbostic naddr = 0; 14155992Sbostic cmd->a1 = cmd->a2 = NULL; 14255992Sbostic 14355992Sbostic /* Valid characters to start an address */ 14455992Sbostic #define addrchar(c) (strchr("0123456789/\\$", (c))) 14555992Sbostic if (addrchar(*p)) { 14655992Sbostic naddr++; 14755992Sbostic cmd->a1 = xmalloc(sizeof(struct s_addr)); 14855992Sbostic p = compile_addr(p, cmd->a1); 14955992Sbostic EATSPACE(); /* EXTENSION */ 15055992Sbostic if (*p == ',') { 15155992Sbostic naddr++; 15255992Sbostic p++; 15355992Sbostic EATSPACE(); /* EXTENSION */ 15455992Sbostic cmd->a2 = xmalloc(sizeof(struct s_addr)); 15555992Sbostic p = compile_addr(p, cmd->a2); 15655992Sbostic } 15755992Sbostic } 15855992Sbostic 15955992Sbostic nonsel: /* Now parse the command */ 16055992Sbostic EATSPACE(); 16155992Sbostic if (!*p) 16255992Sbostic err(COMPILE, "command expected"); 16355992Sbostic cmd->code = *p; 16455992Sbostic for (fp = cmd_fmts; fp->code; fp++) 16555992Sbostic if (fp->code == *p) 16655992Sbostic break; 16755992Sbostic if (!fp->code) 16855992Sbostic err(COMPILE, "invalid command code %c", *p); 16955992Sbostic if (naddr > fp->naddr) 17055992Sbostic err(COMPILE, 17155992Sbostic "command %c expects up to %d address(es), found %d", *p, fp->naddr, naddr); 17255992Sbostic switch (fp->args) { 17355992Sbostic case NONSEL: /* ! */ 17455992Sbostic cmd->nonsel = ! cmd->nonsel; 17555992Sbostic p++; 17655992Sbostic goto nonsel; 17755992Sbostic case GROUP: /* { */ 17855992Sbostic p++; 17955992Sbostic EATSPACE(); 18055992Sbostic if (!*p) 18155992Sbostic p = NULL; 18255992Sbostic cmd2 = xmalloc(sizeof(struct s_command)); 18355992Sbostic cmd2->code = '}'; 18455992Sbostic *compile_stream("}", &cmd->u.c, p) = cmd2; 18555992Sbostic cmd->next = cmd2; 18655992Sbostic link = &cmd2->next; 18755992Sbostic break; 18855992Sbostic case EMPTY: /* d D g G h H l n N p P q x = \0 */ 18955992Sbostic p++; 19055992Sbostic EATSPACE(); 19155992Sbostic if (*p == ';') { 19255992Sbostic p++; 19355992Sbostic link = &cmd->next; 19455992Sbostic goto semicolon; 19555992Sbostic } 19655992Sbostic if (*p) 19755992Sbostic err(COMPILE, 19855992Sbostic "extra characters at the end of %c command", cmd->code); 19955992Sbostic break; 20055992Sbostic case TEXT: /* a c i */ 20155992Sbostic p++; 20255992Sbostic EATSPACE(); 20355992Sbostic if (*p != '\\') 20455992Sbostic err(COMPILE, 20555992Sbostic "command %c expects \\ followed by text", cmd->code); 20655992Sbostic p++; 20755992Sbostic EATSPACE(); 20855992Sbostic if (*p) 20955992Sbostic err(COMPILE, 21055992Sbostic "extra characters after \\ at the end of %c command", cmd->code); 21155992Sbostic cmd->t = compile_text(); 21255992Sbostic break; 21355992Sbostic case COMMENT: /* \0 # */ 21455992Sbostic break; 21555992Sbostic case WFILE: /* w */ 21655992Sbostic p++; 21755992Sbostic EATSPACE(); 21855992Sbostic if (*p == '\0') 21955992Sbostic err(COMPILE, "filename expected"); 22055992Sbostic cmd->t = duptoeol(p); 22155992Sbostic if (aflag) 22255992Sbostic cmd->u.fd = -1; 22355992Sbostic else if ((cmd->u.fd = open(p, 22455992Sbostic O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 22555992Sbostic DEFFILEMODE)) == -1) 22655992Sbostic err(FATAL, "%s: %s\n", p, strerror(errno)); 22755992Sbostic break; 22855992Sbostic case RFILE: /* r */ 22955992Sbostic p++; 23055992Sbostic EATSPACE(); 23155992Sbostic if (*p == '\0') 23255992Sbostic err(COMPILE, "filename expected"); 23355992Sbostic else 23455992Sbostic cmd->t = duptoeol(p); 23555992Sbostic break; 23655992Sbostic case BRANCH: /* b t */ 23755992Sbostic p++; 23855992Sbostic EATSPACE(); 23955992Sbostic if (*p == '\0') 24055992Sbostic cmd->t = NULL; 24155992Sbostic else 24255992Sbostic cmd->t = duptoeol(p); 24355992Sbostic break; 24455992Sbostic case LABEL: /* : */ 24555992Sbostic p++; 24655992Sbostic EATSPACE(); 24755992Sbostic cmd->t = duptoeol(p); 24855992Sbostic if (strlen(p) == 0) 24955992Sbostic err(COMPILE, "empty label"); 25055992Sbostic break; 25155992Sbostic case SUBST: /* s */ 25255992Sbostic p++; 25355992Sbostic if (*p == '\0' || *p == '\\') 25455992Sbostic err(COMPILE, 25555992Sbostic "substitute pattern can not be delimited by newline or backslash"); 25655992Sbostic cmd->u.s = xmalloc(sizeof(struct s_subst)); 25756077Sbostic p = compile_re(p, &cmd->u.s->re); 25855992Sbostic if (p == NULL) 25956004Sbostic err(COMPILE, "unterminated substitute pattern"); 26056019Sbostic --p; 26156019Sbostic p = compile_subst(p, cmd->u.s); 26255992Sbostic p = compile_flags(p, cmd->u.s); 26355992Sbostic EATSPACE(); 26455992Sbostic if (*p == ';') { 26555992Sbostic p++; 26655992Sbostic link = &cmd->next; 26755992Sbostic goto semicolon; 26855992Sbostic } 26955992Sbostic break; 27055992Sbostic case TR: /* y */ 27155992Sbostic p++; 27255992Sbostic p = compile_tr(p, (char **)&cmd->u.y); 27355992Sbostic EATSPACE(); 27455992Sbostic if (*p == ';') { 27555992Sbostic p++; 27655992Sbostic link = &cmd->next; 27755992Sbostic goto semicolon; 27855992Sbostic } 27955992Sbostic if (*p) 28055992Sbostic err(COMPILE, 28155992Sbostic "extra text at the end of a transform command"); 28255992Sbostic break; 28355992Sbostic } 28455992Sbostic } 28555992Sbostic } 28655992Sbostic 28755992Sbostic /* 28855992Sbostic * Get a delimited string. P points to the delimeter of the string; d points 28955992Sbostic * to a buffer area. Newline and delimiter escapes are processed; other 29055992Sbostic * escapes are ignored. 29155992Sbostic * 29255992Sbostic * Returns a pointer to the first character after the final delimiter or NULL 29355992Sbostic * in the case of a non-terminated string. The character array d is filled 29455992Sbostic * with the processed string. 29555992Sbostic */ 29655992Sbostic static char * 29755992Sbostic compile_delimited(p, d) 29855992Sbostic char *p, *d; 29955992Sbostic { 30055992Sbostic char c; 30155992Sbostic 30255992Sbostic c = *p++; 30355992Sbostic if (c == '\0') 30455992Sbostic return (NULL); 30555992Sbostic else if (c == '\\') 30655992Sbostic err(COMPILE, "\\ can not be used as a string delimiter"); 30755992Sbostic else if (c == '\n') 30855992Sbostic err(COMPILE, "newline can not be used as a string delimiter"); 30955992Sbostic while (*p) { 31055992Sbostic if (*p == '\\' && p[1] == c) 31156019Sbostic p++; 31255992Sbostic else if (*p == '\\' && p[1] == 'n') { 31356019Sbostic *d++ = '\n'; 31456019Sbostic p += 2; 31556019Sbostic continue; 316*56663Sbostic } else if (*p == '\\' && p[1] == '\\') 317*56663Sbostic *d++ = *p++; 318*56663Sbostic else if (*p == c) { 31955992Sbostic *d = '\0'; 32055992Sbostic return (p + 1); 32155992Sbostic } 32255992Sbostic *d++ = *p++; 32355992Sbostic } 32455992Sbostic return (NULL); 32555992Sbostic } 32655992Sbostic 32755992Sbostic /* 32856019Sbostic * Get a regular expression. P points to the delimiter of the regular 32956019Sbostic * expression; repp points to the address of a regexp pointer. Newline 33056019Sbostic * and delimiter escapes are processed; other escapes are ignored. 33155992Sbostic * Returns a pointer to the first character after the final delimiter 33256019Sbostic * or NULL in the case of a non terminated regular expression. The regexp 33356019Sbostic * pointer is set to the compiled regular expression. 33455992Sbostic * Cflags are passed to regcomp. 33555992Sbostic */ 33655992Sbostic static char * 33756077Sbostic compile_re(p, repp) 33855992Sbostic char *p; 33956019Sbostic regex_t **repp; 34055992Sbostic { 34155992Sbostic int eval; 34255992Sbostic char re[_POSIX2_LINE_MAX + 1]; 34355992Sbostic 34455992Sbostic p = compile_delimited(p, re); 34556019Sbostic if (p && strlen(re) == 0) { 34656019Sbostic *repp = NULL; 34756019Sbostic return (p); 34856019Sbostic } 34956019Sbostic *repp = xmalloc(sizeof(regex_t)); 35056077Sbostic if (p && (eval = regcomp(*repp, re, 0)) != 0) 35156019Sbostic err(COMPILE, "RE error: %s", strregerror(eval, *repp)); 35256077Sbostic if (maxnsub < (*repp)->re_nsub) 35356077Sbostic maxnsub = (*repp)->re_nsub; 35455992Sbostic return (p); 35555992Sbostic } 35655992Sbostic 35755992Sbostic /* 35855992Sbostic * Compile the substitution string of a regular expression and set res to 35955992Sbostic * point to a saved copy of it. Nsub is the number of parenthesized regular 36055992Sbostic * expressions. 36155992Sbostic */ 36255992Sbostic static char * 36356019Sbostic compile_subst(p, s) 36456019Sbostic char *p; 36556019Sbostic struct s_subst *s; 36655992Sbostic { 36755992Sbostic static char lbuf[_POSIX2_LINE_MAX + 1]; 36856019Sbostic int asize, ref, size; 36956019Sbostic char c, *text, *op, *sp; 37055992Sbostic 37155992Sbostic c = *p++; /* Terminator character */ 37255992Sbostic if (c == '\0') 37355992Sbostic return (NULL); 37455992Sbostic 37556019Sbostic s->maxbref = 0; 37656019Sbostic s->linenum = linenum; 37755992Sbostic asize = 2 * _POSIX2_LINE_MAX + 1; 37855992Sbostic text = xmalloc(asize); 37955992Sbostic size = 0; 38055992Sbostic do { 38156019Sbostic op = sp = text + size; 38255992Sbostic for (; *p; p++) { 38355992Sbostic if (*p == '\\') { 38455992Sbostic p++; 38555992Sbostic if (strchr("123456789", *p) != NULL) { 38656019Sbostic *sp++ = '\\'; 38756019Sbostic ref = *p - '0'; 38856019Sbostic if (s->re != NULL && 38956019Sbostic ref > s->re->re_nsub) 39055992Sbostic err(COMPILE, 39156019Sbostic "\\%c not defined in the RE", *p); 39256077Sbostic if (s->maxbref < ref) 39356077Sbostic s->maxbref = ref; 394*56663Sbostic } else if (*p == '&' || *p == '\\') 39556019Sbostic *sp++ = '\\'; 39655992Sbostic } else if (*p == c) { 39755992Sbostic p++; 39856019Sbostic *sp++ = '\0'; 39956019Sbostic size += sp - op; 40056019Sbostic s->new = xrealloc(text, size); 40155992Sbostic return (p); 40255992Sbostic } else if (*p == '\n') { 40355992Sbostic err(COMPILE, 40455992Sbostic "unescaped newline inside substitute pattern"); 40556019Sbostic /* NOTREACHED */ 40655992Sbostic } 40756019Sbostic *sp++ = *p; 40855992Sbostic } 40956019Sbostic size += sp - op; 41055992Sbostic if (asize - size < _POSIX2_LINE_MAX + 1) { 41155992Sbostic asize *= 2; 41255992Sbostic text = xmalloc(asize); 41355992Sbostic } 41455992Sbostic } while (cu_fgets(p = lbuf, sizeof(lbuf))); 41556019Sbostic err(COMPILE, "unterminated substitute in regular expression"); 41656019Sbostic /* NOTREACHED */ 41755992Sbostic } 41855992Sbostic 41955992Sbostic /* 42055992Sbostic * Compile the flags of the s command 42155992Sbostic */ 42255992Sbostic static char * 42355992Sbostic compile_flags(p, s) 42455992Sbostic char *p; 42555992Sbostic struct s_subst *s; 42655992Sbostic { 42755992Sbostic int gn; /* True if we have seen g or n */ 42855992Sbostic char wfile[_POSIX2_LINE_MAX + 1], *q; 42955992Sbostic 43055992Sbostic s->n = 1; /* Default */ 43155992Sbostic s->p = 0; 43255992Sbostic s->wfile = NULL; 43355992Sbostic s->wfd = -1; 43455992Sbostic for (gn = 0;;) { 43555992Sbostic EATSPACE(); /* EXTENSION */ 43655992Sbostic switch (*p) { 43755992Sbostic case 'g': 43855992Sbostic if (gn) 43956004Sbostic err(COMPILE, 44056004Sbostic "more than one number or 'g' in substitute flags"); 44155992Sbostic gn = 1; 44255992Sbostic s->n = 0; 44355992Sbostic break; 44455992Sbostic case '\0': 44555992Sbostic case '\n': 44655992Sbostic case ';': 44755992Sbostic return (p); 44855992Sbostic case 'p': 44955992Sbostic s->p = 1; 45055992Sbostic break; 45155992Sbostic case '1': case '2': case '3': 45255992Sbostic case '4': case '5': case '6': 45355992Sbostic case '7': case '8': case '9': 45455992Sbostic if (gn) 45556004Sbostic err(COMPILE, 45656004Sbostic "more than one number or 'g' in substitute flags"); 45755992Sbostic gn = 1; 45855992Sbostic /* XXX Check for overflow */ 45955992Sbostic s->n = (int)strtol(p, &p, 10); 46055992Sbostic break; 46155992Sbostic case 'w': 46255992Sbostic p++; 46355992Sbostic #ifdef HISTORIC_PRACTICE 46455992Sbostic if (*p != ' ') { 46555992Sbostic err(WARNING, "space missing before w wfile"); 46655992Sbostic return (p); 46755992Sbostic } 46855992Sbostic #endif 46955992Sbostic EATSPACE(); 47055992Sbostic q = wfile; 47155992Sbostic while (*p) { 47255992Sbostic if (*p == '\n') 47355992Sbostic break; 47455992Sbostic *q++ = *p++; 47555992Sbostic } 47655992Sbostic *q = '\0'; 47755992Sbostic if (q == wfile) 47856004Sbostic err(COMPILE, "no wfile specified"); 47955992Sbostic s->wfile = strdup(wfile); 48055992Sbostic if (!aflag && (s->wfd = open(wfile, 48155992Sbostic O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 48255992Sbostic DEFFILEMODE)) == -1) 48355992Sbostic err(FATAL, "%s: %s\n", wfile, strerror(errno)); 48455992Sbostic return (p); 48555992Sbostic default: 48655992Sbostic err(COMPILE, 48756004Sbostic "bad flag in substitute command: '%c'", *p); 48855992Sbostic break; 48955992Sbostic } 49055992Sbostic p++; 49155992Sbostic } 49255992Sbostic } 49355992Sbostic 49455992Sbostic /* 49555992Sbostic * Compile a translation set of strings into a lookup table. 49655992Sbostic */ 49755992Sbostic static char * 49855992Sbostic compile_tr(p, transtab) 49955992Sbostic char *p; 50055992Sbostic char **transtab; 50155992Sbostic { 50255992Sbostic int i; 50355992Sbostic char *lt, *op, *np; 50455992Sbostic char old[_POSIX2_LINE_MAX + 1]; 50555992Sbostic char new[_POSIX2_LINE_MAX + 1]; 50655992Sbostic 50755992Sbostic if (*p == '\0' || *p == '\\') 50855992Sbostic err(COMPILE, 50955992Sbostic "transform pattern can not be delimited by newline or backslash"); 51055992Sbostic p = compile_delimited(p, old); 51155992Sbostic if (p == NULL) { 51255992Sbostic err(COMPILE, "unterminated transform source string"); 51355992Sbostic return (NULL); 51455992Sbostic } 51555992Sbostic p = compile_delimited(--p, new); 51655992Sbostic if (p == NULL) { 51755992Sbostic err(COMPILE, "unterminated transform target string"); 51855992Sbostic return (NULL); 51955992Sbostic } 52055992Sbostic EATSPACE(); 52155992Sbostic if (strlen(new) != strlen(old)) { 52255992Sbostic err(COMPILE, "transform strings are not the same length"); 52355992Sbostic return (NULL); 52455992Sbostic } 52555992Sbostic /* We assume characters are 8 bits */ 52655992Sbostic lt = xmalloc(UCHAR_MAX); 52755992Sbostic for (i = 0; i <= UCHAR_MAX; i++) 52855992Sbostic lt[i] = (char)i; 52955992Sbostic for (op = old, np = new; *op; op++, np++) 53055992Sbostic lt[(u_char)*op] = *np; 53155992Sbostic *transtab = lt; 53255992Sbostic return (p); 53355992Sbostic } 53455992Sbostic 53555992Sbostic /* 53655992Sbostic * Compile the text following an a or i command. 53755992Sbostic */ 53855992Sbostic static char * 53955992Sbostic compile_text() 54055992Sbostic { 54155992Sbostic int asize, size; 54255992Sbostic char *text, *p, *op, *s; 54355992Sbostic char lbuf[_POSIX2_LINE_MAX + 1]; 54455992Sbostic 54555992Sbostic asize = 2 * _POSIX2_LINE_MAX + 1; 54655992Sbostic text = xmalloc(asize); 54755992Sbostic size = 0; 54855992Sbostic while (cu_fgets(lbuf, sizeof(lbuf))) { 54955992Sbostic op = s = text + size; 55055992Sbostic p = lbuf; 55155992Sbostic EATSPACE(); 55255992Sbostic for (; *p; p++) { 55355992Sbostic if (*p == '\\') 55455992Sbostic p++; 55555992Sbostic *s++ = *p; 55655992Sbostic } 55755992Sbostic size += s - op; 55855992Sbostic if (p[-2] != '\\') { 55955992Sbostic *s = '\0'; 56055992Sbostic break; 56155992Sbostic } 56255992Sbostic if (asize - size < _POSIX2_LINE_MAX + 1) { 56355992Sbostic asize *= 2; 56455992Sbostic text = xmalloc(asize); 56555992Sbostic } 56655992Sbostic } 56755992Sbostic return (xrealloc(text, size + 1)); 56855992Sbostic } 56955992Sbostic 57055992Sbostic /* 57155992Sbostic * Get an address and return a pointer to the first character after 57255992Sbostic * it. Fill the structure pointed to according to the address. 57355992Sbostic */ 57455992Sbostic static char * 57555992Sbostic compile_addr(p, a) 57655992Sbostic char *p; 57755992Sbostic struct s_addr *a; 57855992Sbostic { 57955992Sbostic char *end; 58055992Sbostic 58155992Sbostic switch (*p) { 58255992Sbostic case '\\': /* Context address */ 58356019Sbostic ++p; 58456019Sbostic /* FALLTHROUGH */ 58555992Sbostic case '/': /* Context address */ 58656077Sbostic p = compile_re(p, &a->u.r); 58755992Sbostic if (p == NULL) 58855992Sbostic err(COMPILE, "unterminated regular expression"); 58955992Sbostic a->type = AT_RE; 59055992Sbostic return (p); 59156019Sbostic 59255992Sbostic case '$': /* Last line */ 59355992Sbostic a->type = AT_LAST; 59455992Sbostic return (p + 1); 59555992Sbostic /* Line number */ 59655992Sbostic case '0': case '1': case '2': case '3': case '4': 59755992Sbostic case '5': case '6': case '7': case '8': case '9': 59855992Sbostic a->type = AT_LINE; 59955992Sbostic a->u.l = strtol(p, &end, 10); 60055992Sbostic return (end); 60155992Sbostic default: 60255992Sbostic err(COMPILE, "expected context address"); 60355992Sbostic return (NULL); 60455992Sbostic } 60555992Sbostic } 60655992Sbostic 60755992Sbostic /* 60855992Sbostic * Return a copy of all the characters up to \n or \0 60955992Sbostic */ 61055992Sbostic static char * 61155992Sbostic duptoeol(s) 61255992Sbostic register char *s; 61355992Sbostic { 61455992Sbostic size_t len; 61555992Sbostic char *start; 61655992Sbostic 61755992Sbostic for (start = s; *s != '\0' && *s != '\n'; ++s); 61855992Sbostic *s = '\0'; 61955992Sbostic len = s - start + 1; 62055992Sbostic return (memmove(xmalloc(len), start, len)); 62155992Sbostic } 62255992Sbostic 62355992Sbostic /* 62455992Sbostic * Find the label contained in the command l in the command linked list cp. 62555992Sbostic * L is excluded from the search. Return NULL if not found. 62655992Sbostic */ 62755992Sbostic static struct s_command * 62855992Sbostic findlabel(l, cp) 62955992Sbostic struct s_command *l, *cp; 63055992Sbostic { 63155992Sbostic struct s_command *r; 63255992Sbostic 63355992Sbostic for (; cp; cp = cp->next) 63455992Sbostic if (cp->code == ':' && cp != l && strcmp(l->t, cp->t) == 0) 63555992Sbostic return (cp); 63655992Sbostic else if (cp->code == '{' && (r = findlabel(l, cp->u.c))) 63755992Sbostic return (r); 63855992Sbostic return (NULL); 63955992Sbostic } 64055992Sbostic 64155992Sbostic /* 64255992Sbostic * Convert goto label names to addresses. 64355992Sbostic * Detect duplicate labels. 64455992Sbostic * Set appendnum to the number of a and r commands in the script. 64555992Sbostic * Free the memory used by labels in b and t commands (but not by :) 64655992Sbostic * Root is a pointer to the script linked list; cp points to the 64755992Sbostic * search start. 64855992Sbostic * TODO: Remove } nodes 64955992Sbostic */ 65055992Sbostic static void 65156093Sbostic fixuplabel(root, cp, end) 65256093Sbostic struct s_command *root, *cp, *end; 65355992Sbostic { 65455992Sbostic struct s_command *cp2; 65555992Sbostic 65656093Sbostic for (; cp != end; cp = cp->next) 65755992Sbostic switch (cp->code) { 65856019Sbostic case ':': 65956019Sbostic if (findlabel(cp, root)) 66056019Sbostic err(COMPILE2, "duplicate label %s", cp->t); 66156019Sbostic break; 66255992Sbostic case 'a': 66355992Sbostic case 'r': 66455992Sbostic appendnum++; 66555992Sbostic break; 66655992Sbostic case 'b': 66755992Sbostic case 't': 66855992Sbostic if (cp->t == NULL) { 66955992Sbostic cp->u.c = NULL; 67055992Sbostic break; 67155992Sbostic } 67255992Sbostic if ((cp2 = findlabel(cp, root)) == NULL) 67356004Sbostic err(COMPILE2, "undefined label '%s'", cp->t); 67455992Sbostic free(cp->t); 67555992Sbostic cp->u.c = cp2; 67655992Sbostic break; 67755992Sbostic case '{': 67856093Sbostic fixuplabel(root, cp->u.c, cp->next); 67955992Sbostic break; 68055992Sbostic } 68155992Sbostic } 682