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*58540Sbostic static char sccsid[] = "@(#)compile.c 5.7 (Berkeley) 03/07/93"; 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 *)); 40*58540Sbostic static char *duptoeol __P((char *, 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 *)); 45*58540Sbostic static void uselabel __P((struct s_command *)); 4655992Sbostic 4755992Sbostic /* 4855992Sbostic * Command specification. This is used to drive the command parser. 4955992Sbostic */ 5055992Sbostic struct s_format { 5155992Sbostic char code; /* Command code */ 5255992Sbostic int naddr; /* Number of address args */ 5355992Sbostic enum e_args args; /* Argument type */ 5455992Sbostic }; 5555992Sbostic 5655992Sbostic static struct s_format cmd_fmts[] = { 5755992Sbostic {'{', 2, GROUP}, 5855992Sbostic {'a', 1, TEXT}, 5955992Sbostic {'b', 2, BRANCH}, 6055992Sbostic {'c', 2, TEXT}, 6155992Sbostic {'d', 2, EMPTY}, 6255992Sbostic {'D', 2, EMPTY}, 6355992Sbostic {'g', 2, EMPTY}, 6455992Sbostic {'G', 2, EMPTY}, 6555992Sbostic {'h', 2, EMPTY}, 6655992Sbostic {'H', 2, EMPTY}, 6755992Sbostic {'i', 1, TEXT}, 6855992Sbostic {'l', 2, EMPTY}, 6955992Sbostic {'n', 2, EMPTY}, 7055992Sbostic {'N', 2, EMPTY}, 7155992Sbostic {'p', 2, EMPTY}, 7255992Sbostic {'P', 2, EMPTY}, 7355992Sbostic {'q', 1, EMPTY}, 7455992Sbostic {'r', 1, RFILE}, 7555992Sbostic {'s', 2, SUBST}, 7655992Sbostic {'t', 2, BRANCH}, 7755992Sbostic {'w', 2, WFILE}, 7855992Sbostic {'x', 2, EMPTY}, 7955992Sbostic {'y', 2, TR}, 8055992Sbostic {'!', 2, NONSEL}, 8155992Sbostic {':', 0, LABEL}, 8255992Sbostic {'#', 0, COMMENT}, 8355992Sbostic {'=', 1, EMPTY}, 8455992Sbostic {'\0', 0, COMMENT}, 8555992Sbostic }; 8655992Sbostic 8756019Sbostic /* The compiled program. */ 8855992Sbostic struct s_command *prog; 8955992Sbostic 9055992Sbostic /* 9155992Sbostic * Compile the program into prog. 9256019Sbostic * Initialise appends. 9355992Sbostic */ 9455992Sbostic void 9555992Sbostic compile() 9655992Sbostic { 9755992Sbostic *compile_stream(NULL, &prog, NULL) = NULL; 9856093Sbostic fixuplabel(prog, prog, NULL); 9955992Sbostic appends = xmalloc(sizeof(struct s_appends) * appendnum); 10056077Sbostic match = xmalloc((maxnsub + 1) * sizeof(regmatch_t)); 10155992Sbostic } 10255992Sbostic 10355992Sbostic #define EATSPACE() do { \ 10455992Sbostic if (p) \ 10555992Sbostic while (*p && isascii(*p) && isspace(*p)) \ 10655992Sbostic p++; \ 10755992Sbostic } while (0) 10855992Sbostic 10955992Sbostic static struct s_command ** 11055992Sbostic compile_stream(terminator, link, p) 11155992Sbostic char *terminator; 11255992Sbostic struct s_command **link; 11355992Sbostic register char *p; 11455992Sbostic { 11555992Sbostic static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ 11655992Sbostic struct s_command *cmd, *cmd2; 11755992Sbostic struct s_format *fp; 11855992Sbostic int naddr; /* Number of addresses */ 11955992Sbostic 12055992Sbostic if (p != NULL) 12155992Sbostic goto semicolon; 12255992Sbostic for (;;) { 12355992Sbostic if ((p = cu_fgets(lbuf, sizeof(lbuf))) == NULL) { 12455992Sbostic if (terminator != NULL) 12555992Sbostic err(COMPILE, "unexpected EOF (pending }'s)"); 12655992Sbostic return (link); 12755992Sbostic } 12855992Sbostic 12955992Sbostic semicolon: EATSPACE(); 13055992Sbostic if (p && (*p == '#' || *p == '\0')) 13155992Sbostic continue; 13255992Sbostic if (*p == '}') { 13355992Sbostic if (terminator == NULL) 13455992Sbostic err(COMPILE, "unexpected }"); 13555992Sbostic return (link); 13655992Sbostic } 13755992Sbostic *link = cmd = xmalloc(sizeof(struct s_command)); 13855992Sbostic link = &cmd->next; 139*58540Sbostic cmd->lused = cmd->nonsel = cmd->inrange = 0; 14055992Sbostic /* First parse the addresses */ 14155992Sbostic naddr = 0; 14255992Sbostic cmd->a1 = cmd->a2 = NULL; 14355992Sbostic 14455992Sbostic /* Valid characters to start an address */ 14555992Sbostic #define addrchar(c) (strchr("0123456789/\\$", (c))) 14655992Sbostic if (addrchar(*p)) { 14755992Sbostic naddr++; 14855992Sbostic cmd->a1 = xmalloc(sizeof(struct s_addr)); 14955992Sbostic p = compile_addr(p, cmd->a1); 15055992Sbostic EATSPACE(); /* EXTENSION */ 15155992Sbostic if (*p == ',') { 15255992Sbostic naddr++; 15355992Sbostic p++; 15455992Sbostic EATSPACE(); /* EXTENSION */ 15555992Sbostic cmd->a2 = xmalloc(sizeof(struct s_addr)); 15655992Sbostic p = compile_addr(p, cmd->a2); 15755992Sbostic } 15855992Sbostic } 15955992Sbostic 16055992Sbostic nonsel: /* Now parse the command */ 16155992Sbostic EATSPACE(); 16255992Sbostic if (!*p) 16355992Sbostic err(COMPILE, "command expected"); 16455992Sbostic cmd->code = *p; 16555992Sbostic for (fp = cmd_fmts; fp->code; fp++) 16655992Sbostic if (fp->code == *p) 16755992Sbostic break; 16855992Sbostic if (!fp->code) 16955992Sbostic err(COMPILE, "invalid command code %c", *p); 17055992Sbostic if (naddr > fp->naddr) 17155992Sbostic err(COMPILE, 17255992Sbostic "command %c expects up to %d address(es), found %d", *p, fp->naddr, naddr); 17355992Sbostic switch (fp->args) { 17455992Sbostic case NONSEL: /* ! */ 17555992Sbostic cmd->nonsel = ! cmd->nonsel; 17655992Sbostic p++; 17755992Sbostic goto nonsel; 17855992Sbostic case GROUP: /* { */ 17955992Sbostic p++; 18055992Sbostic EATSPACE(); 18155992Sbostic if (!*p) 18255992Sbostic p = NULL; 18355992Sbostic cmd2 = xmalloc(sizeof(struct s_command)); 18455992Sbostic cmd2->code = '}'; 18555992Sbostic *compile_stream("}", &cmd->u.c, p) = cmd2; 18655992Sbostic cmd->next = cmd2; 18755992Sbostic link = &cmd2->next; 18855992Sbostic break; 18955992Sbostic case EMPTY: /* d D g G h H l n N p P q x = \0 */ 19055992Sbostic p++; 19155992Sbostic EATSPACE(); 19255992Sbostic if (*p == ';') { 19355992Sbostic p++; 19455992Sbostic link = &cmd->next; 19555992Sbostic goto semicolon; 19655992Sbostic } 19755992Sbostic if (*p) 19855992Sbostic err(COMPILE, 19955992Sbostic "extra characters at the end of %c command", cmd->code); 20055992Sbostic break; 20155992Sbostic case TEXT: /* a c i */ 20255992Sbostic p++; 20355992Sbostic EATSPACE(); 20455992Sbostic if (*p != '\\') 20555992Sbostic err(COMPILE, 20655992Sbostic "command %c expects \\ followed by text", cmd->code); 20755992Sbostic p++; 20855992Sbostic EATSPACE(); 20955992Sbostic if (*p) 21055992Sbostic err(COMPILE, 21155992Sbostic "extra characters after \\ at the end of %c command", cmd->code); 21255992Sbostic cmd->t = compile_text(); 21355992Sbostic break; 21455992Sbostic case COMMENT: /* \0 # */ 21555992Sbostic break; 21655992Sbostic case WFILE: /* w */ 21755992Sbostic p++; 21855992Sbostic EATSPACE(); 21955992Sbostic if (*p == '\0') 22055992Sbostic err(COMPILE, "filename expected"); 221*58540Sbostic cmd->t = duptoeol(p, "w command"); 22255992Sbostic if (aflag) 22355992Sbostic cmd->u.fd = -1; 22455992Sbostic else if ((cmd->u.fd = open(p, 22555992Sbostic O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 22655992Sbostic DEFFILEMODE)) == -1) 22755992Sbostic err(FATAL, "%s: %s\n", p, strerror(errno)); 22855992Sbostic break; 22955992Sbostic case RFILE: /* r */ 23055992Sbostic p++; 23155992Sbostic EATSPACE(); 23255992Sbostic if (*p == '\0') 23355992Sbostic err(COMPILE, "filename expected"); 23455992Sbostic else 235*58540Sbostic cmd->t = duptoeol(p, "read command"); 23655992Sbostic break; 23755992Sbostic case BRANCH: /* b t */ 23855992Sbostic p++; 23955992Sbostic EATSPACE(); 24055992Sbostic if (*p == '\0') 24155992Sbostic cmd->t = NULL; 24255992Sbostic else 243*58540Sbostic cmd->t = duptoeol(p, "branch"); 24455992Sbostic break; 24555992Sbostic case LABEL: /* : */ 24655992Sbostic p++; 24755992Sbostic EATSPACE(); 248*58540Sbostic cmd->t = duptoeol(p, "label"); 24955992Sbostic if (strlen(p) == 0) 25055992Sbostic err(COMPILE, "empty label"); 25155992Sbostic break; 25255992Sbostic case SUBST: /* s */ 25355992Sbostic p++; 25455992Sbostic if (*p == '\0' || *p == '\\') 25555992Sbostic err(COMPILE, 25655992Sbostic "substitute pattern can not be delimited by newline or backslash"); 25755992Sbostic cmd->u.s = xmalloc(sizeof(struct s_subst)); 25856077Sbostic p = compile_re(p, &cmd->u.s->re); 25955992Sbostic if (p == NULL) 26056004Sbostic err(COMPILE, "unterminated substitute pattern"); 26156019Sbostic --p; 26256019Sbostic p = compile_subst(p, cmd->u.s); 26355992Sbostic p = compile_flags(p, cmd->u.s); 26455992Sbostic EATSPACE(); 26555992Sbostic if (*p == ';') { 26655992Sbostic p++; 26755992Sbostic link = &cmd->next; 26855992Sbostic goto semicolon; 26955992Sbostic } 27055992Sbostic break; 27155992Sbostic case TR: /* y */ 27255992Sbostic p++; 27355992Sbostic p = compile_tr(p, (char **)&cmd->u.y); 27455992Sbostic EATSPACE(); 27555992Sbostic if (*p == ';') { 27655992Sbostic p++; 27755992Sbostic link = &cmd->next; 27855992Sbostic goto semicolon; 27955992Sbostic } 28055992Sbostic if (*p) 28155992Sbostic err(COMPILE, 28255992Sbostic "extra text at the end of a transform command"); 28355992Sbostic break; 28455992Sbostic } 28555992Sbostic } 28655992Sbostic } 28755992Sbostic 28855992Sbostic /* 28955992Sbostic * Get a delimited string. P points to the delimeter of the string; d points 29055992Sbostic * to a buffer area. Newline and delimiter escapes are processed; other 29155992Sbostic * escapes are ignored. 29255992Sbostic * 29355992Sbostic * Returns a pointer to the first character after the final delimiter or NULL 29455992Sbostic * in the case of a non-terminated string. The character array d is filled 29555992Sbostic * with the processed string. 29655992Sbostic */ 29755992Sbostic static char * 29855992Sbostic compile_delimited(p, d) 29955992Sbostic char *p, *d; 30055992Sbostic { 30155992Sbostic char c; 30255992Sbostic 30355992Sbostic c = *p++; 30455992Sbostic if (c == '\0') 30555992Sbostic return (NULL); 30655992Sbostic else if (c == '\\') 30755992Sbostic err(COMPILE, "\\ can not be used as a string delimiter"); 30855992Sbostic else if (c == '\n') 30955992Sbostic err(COMPILE, "newline can not be used as a string delimiter"); 31055992Sbostic while (*p) { 31155992Sbostic if (*p == '\\' && p[1] == c) 31256019Sbostic p++; 31355992Sbostic else if (*p == '\\' && p[1] == 'n') { 31456019Sbostic *d++ = '\n'; 31556019Sbostic p += 2; 31656019Sbostic continue; 31756663Sbostic } else if (*p == '\\' && p[1] == '\\') 31856663Sbostic *d++ = *p++; 31956663Sbostic else if (*p == c) { 32055992Sbostic *d = '\0'; 32155992Sbostic return (p + 1); 32255992Sbostic } 32355992Sbostic *d++ = *p++; 32455992Sbostic } 32555992Sbostic return (NULL); 32655992Sbostic } 32755992Sbostic 32855992Sbostic /* 32956019Sbostic * Get a regular expression. P points to the delimiter of the regular 33056019Sbostic * expression; repp points to the address of a regexp pointer. Newline 33156019Sbostic * and delimiter escapes are processed; other escapes are ignored. 33255992Sbostic * Returns a pointer to the first character after the final delimiter 33356019Sbostic * or NULL in the case of a non terminated regular expression. The regexp 33456019Sbostic * pointer is set to the compiled regular expression. 33555992Sbostic * Cflags are passed to regcomp. 33655992Sbostic */ 33755992Sbostic static char * 33856077Sbostic compile_re(p, repp) 33955992Sbostic char *p; 34056019Sbostic regex_t **repp; 34155992Sbostic { 34255992Sbostic int eval; 34355992Sbostic char re[_POSIX2_LINE_MAX + 1]; 34455992Sbostic 34555992Sbostic p = compile_delimited(p, re); 34656019Sbostic if (p && strlen(re) == 0) { 34756019Sbostic *repp = NULL; 34856019Sbostic return (p); 34956019Sbostic } 35056019Sbostic *repp = xmalloc(sizeof(regex_t)); 35156077Sbostic if (p && (eval = regcomp(*repp, re, 0)) != 0) 35256019Sbostic err(COMPILE, "RE error: %s", strregerror(eval, *repp)); 35356077Sbostic if (maxnsub < (*repp)->re_nsub) 35456077Sbostic maxnsub = (*repp)->re_nsub; 35555992Sbostic return (p); 35655992Sbostic } 35755992Sbostic 35855992Sbostic /* 35955992Sbostic * Compile the substitution string of a regular expression and set res to 36055992Sbostic * point to a saved copy of it. Nsub is the number of parenthesized regular 36155992Sbostic * expressions. 36255992Sbostic */ 36355992Sbostic static char * 36456019Sbostic compile_subst(p, s) 36556019Sbostic char *p; 36656019Sbostic struct s_subst *s; 36755992Sbostic { 36855992Sbostic static char lbuf[_POSIX2_LINE_MAX + 1]; 36956019Sbostic int asize, ref, size; 37056019Sbostic char c, *text, *op, *sp; 37155992Sbostic 37255992Sbostic c = *p++; /* Terminator character */ 37355992Sbostic if (c == '\0') 37455992Sbostic return (NULL); 37555992Sbostic 37656019Sbostic s->maxbref = 0; 37756019Sbostic s->linenum = linenum; 37855992Sbostic asize = 2 * _POSIX2_LINE_MAX + 1; 37955992Sbostic text = xmalloc(asize); 38055992Sbostic size = 0; 38155992Sbostic do { 38256019Sbostic op = sp = text + size; 38355992Sbostic for (; *p; p++) { 38455992Sbostic if (*p == '\\') { 38555992Sbostic p++; 38655992Sbostic if (strchr("123456789", *p) != NULL) { 38756019Sbostic *sp++ = '\\'; 38856019Sbostic ref = *p - '0'; 38956019Sbostic if (s->re != NULL && 39056019Sbostic ref > s->re->re_nsub) 39155992Sbostic err(COMPILE, 39256019Sbostic "\\%c not defined in the RE", *p); 39356077Sbostic if (s->maxbref < ref) 39456077Sbostic s->maxbref = ref; 39556663Sbostic } else if (*p == '&' || *p == '\\') 39656019Sbostic *sp++ = '\\'; 39755992Sbostic } else if (*p == c) { 39855992Sbostic p++; 39956019Sbostic *sp++ = '\0'; 40056019Sbostic size += sp - op; 40156019Sbostic s->new = xrealloc(text, size); 40255992Sbostic return (p); 40355992Sbostic } else if (*p == '\n') { 40455992Sbostic err(COMPILE, 40555992Sbostic "unescaped newline inside substitute pattern"); 40656019Sbostic /* NOTREACHED */ 40755992Sbostic } 40856019Sbostic *sp++ = *p; 40955992Sbostic } 41056019Sbostic size += sp - op; 41155992Sbostic if (asize - size < _POSIX2_LINE_MAX + 1) { 41255992Sbostic asize *= 2; 41355992Sbostic text = xmalloc(asize); 41455992Sbostic } 41555992Sbostic } while (cu_fgets(p = lbuf, sizeof(lbuf))); 41656019Sbostic err(COMPILE, "unterminated substitute in regular expression"); 41756019Sbostic /* NOTREACHED */ 41855992Sbostic } 41955992Sbostic 42055992Sbostic /* 42155992Sbostic * Compile the flags of the s command 42255992Sbostic */ 42355992Sbostic static char * 42455992Sbostic compile_flags(p, s) 42555992Sbostic char *p; 42655992Sbostic struct s_subst *s; 42755992Sbostic { 42855992Sbostic int gn; /* True if we have seen g or n */ 42955992Sbostic char wfile[_POSIX2_LINE_MAX + 1], *q; 43055992Sbostic 43155992Sbostic s->n = 1; /* Default */ 43255992Sbostic s->p = 0; 43355992Sbostic s->wfile = NULL; 43455992Sbostic s->wfd = -1; 43555992Sbostic for (gn = 0;;) { 43655992Sbostic EATSPACE(); /* EXTENSION */ 43755992Sbostic switch (*p) { 43855992Sbostic case 'g': 43955992Sbostic if (gn) 44056004Sbostic err(COMPILE, 44156004Sbostic "more than one number or 'g' in substitute flags"); 44255992Sbostic gn = 1; 44355992Sbostic s->n = 0; 44455992Sbostic break; 44555992Sbostic case '\0': 44655992Sbostic case '\n': 44755992Sbostic case ';': 44855992Sbostic return (p); 44955992Sbostic case 'p': 45055992Sbostic s->p = 1; 45155992Sbostic break; 45255992Sbostic case '1': case '2': case '3': 45355992Sbostic case '4': case '5': case '6': 45455992Sbostic case '7': case '8': case '9': 45555992Sbostic if (gn) 45656004Sbostic err(COMPILE, 45756004Sbostic "more than one number or 'g' in substitute flags"); 45855992Sbostic gn = 1; 45955992Sbostic /* XXX Check for overflow */ 46055992Sbostic s->n = (int)strtol(p, &p, 10); 46155992Sbostic break; 46255992Sbostic case 'w': 46355992Sbostic p++; 46455992Sbostic #ifdef HISTORIC_PRACTICE 46555992Sbostic if (*p != ' ') { 46655992Sbostic err(WARNING, "space missing before w wfile"); 46755992Sbostic return (p); 46855992Sbostic } 46955992Sbostic #endif 47055992Sbostic EATSPACE(); 47155992Sbostic q = wfile; 47255992Sbostic while (*p) { 47355992Sbostic if (*p == '\n') 47455992Sbostic break; 47555992Sbostic *q++ = *p++; 47655992Sbostic } 47755992Sbostic *q = '\0'; 47855992Sbostic if (q == wfile) 47956004Sbostic err(COMPILE, "no wfile specified"); 48055992Sbostic s->wfile = strdup(wfile); 48155992Sbostic if (!aflag && (s->wfd = open(wfile, 48255992Sbostic O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 48355992Sbostic DEFFILEMODE)) == -1) 48455992Sbostic err(FATAL, "%s: %s\n", wfile, strerror(errno)); 48555992Sbostic return (p); 48655992Sbostic default: 48755992Sbostic err(COMPILE, 48856004Sbostic "bad flag in substitute command: '%c'", *p); 48955992Sbostic break; 49055992Sbostic } 49155992Sbostic p++; 49255992Sbostic } 49355992Sbostic } 49455992Sbostic 49555992Sbostic /* 49655992Sbostic * Compile a translation set of strings into a lookup table. 49755992Sbostic */ 49855992Sbostic static char * 49955992Sbostic compile_tr(p, transtab) 50055992Sbostic char *p; 50155992Sbostic char **transtab; 50255992Sbostic { 50355992Sbostic int i; 50455992Sbostic char *lt, *op, *np; 50555992Sbostic char old[_POSIX2_LINE_MAX + 1]; 50655992Sbostic char new[_POSIX2_LINE_MAX + 1]; 50755992Sbostic 50855992Sbostic if (*p == '\0' || *p == '\\') 50955992Sbostic err(COMPILE, 51055992Sbostic "transform pattern can not be delimited by newline or backslash"); 51155992Sbostic p = compile_delimited(p, old); 51255992Sbostic if (p == NULL) { 51355992Sbostic err(COMPILE, "unterminated transform source string"); 51455992Sbostic return (NULL); 51555992Sbostic } 51655992Sbostic p = compile_delimited(--p, new); 51755992Sbostic if (p == NULL) { 51855992Sbostic err(COMPILE, "unterminated transform target string"); 51955992Sbostic return (NULL); 52055992Sbostic } 52155992Sbostic EATSPACE(); 52255992Sbostic if (strlen(new) != strlen(old)) { 52355992Sbostic err(COMPILE, "transform strings are not the same length"); 52455992Sbostic return (NULL); 52555992Sbostic } 52655992Sbostic /* We assume characters are 8 bits */ 52755992Sbostic lt = xmalloc(UCHAR_MAX); 52855992Sbostic for (i = 0; i <= UCHAR_MAX; i++) 52955992Sbostic lt[i] = (char)i; 53055992Sbostic for (op = old, np = new; *op; op++, np++) 53155992Sbostic lt[(u_char)*op] = *np; 53255992Sbostic *transtab = lt; 53355992Sbostic return (p); 53455992Sbostic } 53555992Sbostic 53655992Sbostic /* 53755992Sbostic * Compile the text following an a or i command. 53855992Sbostic */ 53955992Sbostic static char * 54055992Sbostic compile_text() 54155992Sbostic { 54255992Sbostic int asize, size; 54355992Sbostic char *text, *p, *op, *s; 54455992Sbostic char lbuf[_POSIX2_LINE_MAX + 1]; 54555992Sbostic 54655992Sbostic asize = 2 * _POSIX2_LINE_MAX + 1; 54755992Sbostic text = xmalloc(asize); 54855992Sbostic size = 0; 54955992Sbostic while (cu_fgets(lbuf, sizeof(lbuf))) { 55055992Sbostic op = s = text + size; 55155992Sbostic p = lbuf; 55255992Sbostic EATSPACE(); 55355992Sbostic for (; *p; p++) { 55455992Sbostic if (*p == '\\') 55555992Sbostic p++; 55655992Sbostic *s++ = *p; 55755992Sbostic } 55855992Sbostic size += s - op; 55955992Sbostic if (p[-2] != '\\') { 56055992Sbostic *s = '\0'; 56155992Sbostic break; 56255992Sbostic } 56355992Sbostic if (asize - size < _POSIX2_LINE_MAX + 1) { 56455992Sbostic asize *= 2; 56555992Sbostic text = xmalloc(asize); 56655992Sbostic } 56755992Sbostic } 56855992Sbostic return (xrealloc(text, size + 1)); 56955992Sbostic } 57055992Sbostic 57155992Sbostic /* 57255992Sbostic * Get an address and return a pointer to the first character after 57355992Sbostic * it. Fill the structure pointed to according to the address. 57455992Sbostic */ 57555992Sbostic static char * 57655992Sbostic compile_addr(p, a) 57755992Sbostic char *p; 57855992Sbostic struct s_addr *a; 57955992Sbostic { 58055992Sbostic char *end; 58155992Sbostic 58255992Sbostic switch (*p) { 58355992Sbostic case '\\': /* Context address */ 58456019Sbostic ++p; 58556019Sbostic /* FALLTHROUGH */ 58655992Sbostic case '/': /* Context address */ 58756077Sbostic p = compile_re(p, &a->u.r); 58855992Sbostic if (p == NULL) 58955992Sbostic err(COMPILE, "unterminated regular expression"); 59055992Sbostic a->type = AT_RE; 59155992Sbostic return (p); 59256019Sbostic 59355992Sbostic case '$': /* Last line */ 59455992Sbostic a->type = AT_LAST; 59555992Sbostic return (p + 1); 59655992Sbostic /* Line number */ 59755992Sbostic case '0': case '1': case '2': case '3': case '4': 59855992Sbostic case '5': case '6': case '7': case '8': case '9': 59955992Sbostic a->type = AT_LINE; 60055992Sbostic a->u.l = strtol(p, &end, 10); 60155992Sbostic return (end); 60255992Sbostic default: 60355992Sbostic err(COMPILE, "expected context address"); 60455992Sbostic return (NULL); 60555992Sbostic } 60655992Sbostic } 60755992Sbostic 60855992Sbostic /* 609*58540Sbostic * duptoeol -- 610*58540Sbostic * Return a copy of all the characters up to \n or \0. 61155992Sbostic */ 61255992Sbostic static char * 613*58540Sbostic duptoeol(s, ctype) 61455992Sbostic register char *s; 615*58540Sbostic char *ctype; 61655992Sbostic { 61755992Sbostic size_t len; 618*58540Sbostic int ws; 61955992Sbostic char *start; 62055992Sbostic 621*58540Sbostic ws = 0; 622*58540Sbostic for (start = s; *s != '\0' && *s != '\n'; ++s) 623*58540Sbostic ws = isspace(*s); 62455992Sbostic *s = '\0'; 625*58540Sbostic if (ws) 626*58540Sbostic err(WARNING, "whitespace after %s", ctype); 62755992Sbostic len = s - start + 1; 62855992Sbostic return (memmove(xmalloc(len), start, len)); 62955992Sbostic } 63055992Sbostic 63155992Sbostic /* 632*58540Sbostic * Convert goto label names to addresses. Detect unused and duplicate labels. 633*58540Sbostic * Set appendnum to the number of a and r commands in the script. Free the 634*58540Sbostic * memory used by labels in b and t commands (but not by :). Root is a pointer 635*58540Sbostic * to the script linked list; cp points to the search start. 636*58540Sbostic * 63755992Sbostic * TODO: Remove } nodes 63855992Sbostic */ 63955992Sbostic static void 64056093Sbostic fixuplabel(root, cp, end) 64156093Sbostic struct s_command *root, *cp, *end; 64255992Sbostic { 64355992Sbostic 64456093Sbostic for (; cp != end; cp = cp->next) 64555992Sbostic switch (cp->code) { 64656019Sbostic case ':': 64756019Sbostic if (findlabel(cp, root)) 64856019Sbostic err(COMPILE2, "duplicate label %s", cp->t); 64956019Sbostic break; 65055992Sbostic case 'a': 65155992Sbostic case 'r': 65255992Sbostic appendnum++; 65355992Sbostic break; 65455992Sbostic case 'b': 65555992Sbostic case 't': 65655992Sbostic if (cp->t == NULL) { 65755992Sbostic cp->u.c = NULL; 65855992Sbostic break; 65955992Sbostic } 660*58540Sbostic if ((cp->u.c = findlabel(cp, root)) == NULL) 66156004Sbostic err(COMPILE2, "undefined label '%s'", cp->t); 662*58540Sbostic cp->u.c->lused = 1; 66355992Sbostic free(cp->t); 66455992Sbostic break; 66555992Sbostic case '{': 66656093Sbostic fixuplabel(root, cp->u.c, cp->next); 66755992Sbostic break; 66855992Sbostic } 669*58540Sbostic uselabel(root); 67055992Sbostic } 671*58540Sbostic 672*58540Sbostic /* 673*58540Sbostic * Find the label contained in the command l in the command linked 674*58540Sbostic * list cp. L is excluded from the search. Return NULL if not found. 675*58540Sbostic */ 676*58540Sbostic static struct s_command * 677*58540Sbostic findlabel(l, cp) 678*58540Sbostic struct s_command *l, *cp; 679*58540Sbostic { 680*58540Sbostic struct s_command *r; 681*58540Sbostic 682*58540Sbostic for (; cp; cp = cp->next) { 683*58540Sbostic if (cp->code == ':' && cp != l && strcmp(l->t, cp->t) == 0) 684*58540Sbostic return (cp); 685*58540Sbostic if (cp->code == '{' && (r = findlabel(l, cp->u.c))) 686*58540Sbostic return (r); 687*58540Sbostic } 688*58540Sbostic return (NULL); 689*58540Sbostic } 690*58540Sbostic 691*58540Sbostic /* 692*58540Sbostic * Find any unused labels. This is because we want to warn the user if they 693*58540Sbostic * accidentally put whitespace on a label name causing it be a different label 694*58540Sbostic * than they intended. 695*58540Sbostic */ 696*58540Sbostic static void 697*58540Sbostic uselabel(cp) 698*58540Sbostic struct s_command *cp; 699*58540Sbostic { 700*58540Sbostic for (; cp; cp = cp->next) { 701*58540Sbostic if (cp->code == ':' && cp->lused == 0) 702*58540Sbostic err(WARNING, "unused label '%s'", cp->t); 703*58540Sbostic if (cp->code == '{') 704*58540Sbostic uselabel(cp->u.c); 705*58540Sbostic } 706*58540Sbostic } 707