1 1.55 martin /* $NetBSD: compile.c,v 1.55 2025/06/03 19:02:29 martin Exp $ */ 2 1.15 tls 3 1.1 alm /*- 4 1.41 christos * Copyright (c) 1992 Diomidis Spinellis. 5 1.9 cgd * Copyright (c) 1992, 1993 6 1.9 cgd * The Regents of the University of California. All rights reserved. 7 1.1 alm * 8 1.1 alm * This code is derived from software contributed to Berkeley by 9 1.1 alm * Diomidis Spinellis of Imperial College, University of London. 10 1.1 alm * 11 1.1 alm * Redistribution and use in source and binary forms, with or without 12 1.1 alm * modification, are permitted provided that the following conditions 13 1.1 alm * are met: 14 1.1 alm * 1. Redistributions of source code must retain the above copyright 15 1.1 alm * notice, this list of conditions and the following disclaimer. 16 1.1 alm * 2. Redistributions in binary form must reproduce the above copyright 17 1.1 alm * notice, this list of conditions and the following disclaimer in the 18 1.1 alm * documentation and/or other materials provided with the distribution. 19 1.25 agc * 3. Neither the name of the University nor the names of its contributors 20 1.25 agc * may be used to endorse or promote products derived from this software 21 1.25 agc * without specific prior written permission. 22 1.25 agc * 23 1.25 agc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 1.25 agc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 1.25 agc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 1.25 agc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 1.25 agc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 1.25 agc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 1.25 agc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 1.25 agc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 1.25 agc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 1.25 agc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 1.25 agc * SUCH DAMAGE. 34 1.25 agc */ 35 1.25 agc 36 1.34 gdamore #if HAVE_NBTOOL_CONFIG_H 37 1.34 gdamore #include "nbtool_config.h" 38 1.34 gdamore #endif 39 1.34 gdamore 40 1.17 lukem #include <sys/cdefs.h> 41 1.55 martin __RCSID("$NetBSD: compile.c,v 1.55 2025/06/03 19:02:29 martin Exp $"); 42 1.41 christos #ifdef __FBSDID 43 1.41 christos __FBSDID("$FreeBSD: head/usr.bin/sed/compile.c 259132 2013-12-09 18:57:20Z eadler $"); 44 1.41 christos #endif 45 1.41 christos 46 1.43 christos #if 0 47 1.43 christos static const char sccsid[] = "@(#)compile.c 8.1 (Berkeley) 6/6/93"; 48 1.43 christos #endif 49 1.43 christos 50 1.1 alm #include <sys/types.h> 51 1.1 alm #include <sys/stat.h> 52 1.1 alm 53 1.1 alm #include <ctype.h> 54 1.41 christos #include <err.h> 55 1.1 alm #include <errno.h> 56 1.1 alm #include <fcntl.h> 57 1.1 alm #include <limits.h> 58 1.1 alm #include <regex.h> 59 1.1 alm #include <stdio.h> 60 1.1 alm #include <stdlib.h> 61 1.1 alm #include <string.h> 62 1.41 christos #include <wchar.h> 63 1.1 alm 64 1.1 alm #include "defs.h" 65 1.1 alm #include "extern.h" 66 1.1 alm 67 1.9 cgd #define LHSZ 128 68 1.9 cgd #define LHMASK (LHSZ - 1) 69 1.9 cgd static struct labhash { 70 1.9 cgd struct labhash *lh_next; 71 1.9 cgd u_int lh_hash; 72 1.9 cgd struct s_command *lh_cmd; 73 1.9 cgd int lh_ref; 74 1.9 cgd } *labels[LHSZ]; 75 1.9 cgd 76 1.24 wiz static char *compile_addr(char *, struct s_addr *); 77 1.24 wiz static char *compile_ccl(char **, char *); 78 1.41 christos static char *compile_delimited(char *, char *, int); 79 1.24 wiz static char *compile_flags(char *, struct s_subst *); 80 1.41 christos static regex_t *compile_re(char *, int); 81 1.24 wiz static char *compile_subst(char *, struct s_subst *); 82 1.24 wiz static char *compile_text(void); 83 1.41 christos static char *compile_tr(char *, struct s_tr **); 84 1.1 alm static struct s_command 85 1.24 wiz **compile_stream(struct s_command **); 86 1.36 lukem static char *duptoeol(char *, const char *); 87 1.24 wiz static void enterlabel(struct s_command *); 88 1.1 alm static struct s_command 89 1.24 wiz *findlabel(char *); 90 1.24 wiz static void fixuplabel(struct s_command *, struct s_command *); 91 1.24 wiz static void uselabel(void); 92 1.48 christos static void parse_escapes(char *); 93 1.1 alm 94 1.1 alm /* 95 1.1 alm * Command specification. This is used to drive the command parser. 96 1.1 alm */ 97 1.1 alm struct s_format { 98 1.1 alm char code; /* Command code */ 99 1.1 alm int naddr; /* Number of address args */ 100 1.1 alm enum e_args args; /* Argument type */ 101 1.1 alm }; 102 1.1 alm 103 1.1 alm static struct s_format cmd_fmts[] = { 104 1.1 alm {'{', 2, GROUP}, 105 1.14 mycroft {'}', 0, ENDGROUP}, 106 1.1 alm {'a', 1, TEXT}, 107 1.1 alm {'b', 2, BRANCH}, 108 1.1 alm {'c', 2, TEXT}, 109 1.1 alm {'d', 2, EMPTY}, 110 1.1 alm {'D', 2, EMPTY}, 111 1.1 alm {'g', 2, EMPTY}, 112 1.1 alm {'G', 2, EMPTY}, 113 1.1 alm {'h', 2, EMPTY}, 114 1.1 alm {'H', 2, EMPTY}, 115 1.1 alm {'i', 1, TEXT}, 116 1.1 alm {'l', 2, EMPTY}, 117 1.1 alm {'n', 2, EMPTY}, 118 1.1 alm {'N', 2, EMPTY}, 119 1.1 alm {'p', 2, EMPTY}, 120 1.1 alm {'P', 2, EMPTY}, 121 1.1 alm {'q', 1, EMPTY}, 122 1.1 alm {'r', 1, RFILE}, 123 1.1 alm {'s', 2, SUBST}, 124 1.1 alm {'t', 2, BRANCH}, 125 1.1 alm {'w', 2, WFILE}, 126 1.1 alm {'x', 2, EMPTY}, 127 1.1 alm {'y', 2, TR}, 128 1.1 alm {'!', 2, NONSEL}, 129 1.1 alm {':', 0, LABEL}, 130 1.1 alm {'#', 0, COMMENT}, 131 1.1 alm {'=', 1, EMPTY}, 132 1.1 alm {'\0', 0, COMMENT}, 133 1.1 alm }; 134 1.1 alm 135 1.1 alm /* The compiled program. */ 136 1.1 alm struct s_command *prog; 137 1.1 alm 138 1.1 alm /* 139 1.1 alm * Compile the program into prog. 140 1.1 alm * Initialise appends. 141 1.1 alm */ 142 1.1 alm void 143 1.24 wiz compile(void) 144 1.1 alm { 145 1.14 mycroft *compile_stream(&prog) = NULL; 146 1.9 cgd fixuplabel(prog, NULL); 147 1.9 cgd uselabel(); 148 1.46 christos if (appendnum > 0) 149 1.46 christos appends = xmalloc(sizeof(struct s_appends) * appendnum); 150 1.46 christos match = xmalloc((maxnsub + 1) * sizeof(regmatch_t)); 151 1.1 alm } 152 1.1 alm 153 1.41 christos #define EATSPACE() do { \ 154 1.41 christos if (p) \ 155 1.41 christos while (*p && isspace((unsigned char)*p)) \ 156 1.41 christos p++; \ 157 1.41 christos } while (0) 158 1.1 alm 159 1.1 alm static struct s_command ** 160 1.24 wiz compile_stream(struct s_command **link) 161 1.14 mycroft { 162 1.17 lukem char *p; 163 1.41 christos static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ 164 1.14 mycroft struct s_command *cmd, *cmd2, *stack; 165 1.1 alm struct s_format *fp; 166 1.41 christos char re[_POSIX2_LINE_MAX + 1]; 167 1.1 alm int naddr; /* Number of addresses */ 168 1.1 alm 169 1.14 mycroft stack = 0; 170 1.1 alm for (;;) { 171 1.41 christos if ((p = cu_fgets(lbuf, sizeof(lbuf), NULL)) == NULL) { 172 1.14 mycroft if (stack != 0) 173 1.41 christos errx(1, "%lu: %s: unexpected EOF (pending }'s)", 174 1.41 christos linenum, fname); 175 1.1 alm return (link); 176 1.1 alm } 177 1.1 alm 178 1.1 alm semicolon: EATSPACE(); 179 1.41 christos if (p) { 180 1.41 christos if (*p == '#' || *p == '\0') 181 1.41 christos continue; 182 1.41 christos else if (*p == ';') { 183 1.41 christos p++; 184 1.41 christos goto semicolon; 185 1.41 christos } 186 1.20 kleink } 187 1.1 alm *link = cmd = xmalloc(sizeof(struct s_command)); 188 1.1 alm link = &cmd->next; 189 1.41 christos cmd->startline = cmd->nonsel = 0; 190 1.1 alm /* First parse the addresses */ 191 1.1 alm naddr = 0; 192 1.1 alm 193 1.1 alm /* Valid characters to start an address */ 194 1.1 alm #define addrchar(c) (strchr("0123456789/\\$", (c))) 195 1.1 alm if (addrchar(*p)) { 196 1.1 alm naddr++; 197 1.1 alm cmd->a1 = xmalloc(sizeof(struct s_addr)); 198 1.1 alm p = compile_addr(p, cmd->a1); 199 1.1 alm EATSPACE(); /* EXTENSION */ 200 1.1 alm if (*p == ',') { 201 1.1 alm p++; 202 1.1 alm EATSPACE(); /* EXTENSION */ 203 1.13 mycroft naddr++; 204 1.1 alm cmd->a2 = xmalloc(sizeof(struct s_addr)); 205 1.1 alm p = compile_addr(p, cmd->a2); 206 1.13 mycroft EATSPACE(); 207 1.12 mycroft } else 208 1.12 mycroft cmd->a2 = 0; 209 1.12 mycroft } else 210 1.13 mycroft cmd->a1 = cmd->a2 = 0; 211 1.1 alm 212 1.1 alm nonsel: /* Now parse the command */ 213 1.1 alm if (!*p) 214 1.41 christos errx(1, "%lu: %s: command expected", linenum, fname); 215 1.1 alm cmd->code = *p; 216 1.1 alm for (fp = cmd_fmts; fp->code; fp++) 217 1.1 alm if (fp->code == *p) 218 1.1 alm break; 219 1.1 alm if (!fp->code) 220 1.41 christos errx(1, "%lu: %s: invalid command code %c", linenum, fname, *p); 221 1.1 alm if (naddr > fp->naddr) 222 1.41 christos errx(1, 223 1.41 christos "%lu: %s: command %c expects up to %d address(es), found %d", 224 1.41 christos linenum, fname, *p, fp->naddr, naddr); 225 1.1 alm switch (fp->args) { 226 1.1 alm case NONSEL: /* ! */ 227 1.13 mycroft p++; 228 1.13 mycroft EATSPACE(); 229 1.1 alm cmd->nonsel = ! cmd->nonsel; 230 1.1 alm goto nonsel; 231 1.1 alm case GROUP: /* { */ 232 1.1 alm p++; 233 1.1 alm EATSPACE(); 234 1.14 mycroft cmd->next = stack; 235 1.14 mycroft stack = cmd; 236 1.14 mycroft link = &cmd->u.c; 237 1.14 mycroft if (*p) 238 1.14 mycroft goto semicolon; 239 1.14 mycroft break; 240 1.14 mycroft case ENDGROUP: 241 1.12 mycroft /* 242 1.12 mycroft * Short-circuit command processing, since end of 243 1.12 mycroft * group is really just a noop. 244 1.12 mycroft */ 245 1.14 mycroft cmd->nonsel = 1; 246 1.14 mycroft if (stack == 0) 247 1.41 christos errx(1, "%lu: %s: unexpected }", linenum, fname); 248 1.14 mycroft cmd2 = stack; 249 1.14 mycroft stack = cmd2->next; 250 1.14 mycroft cmd2->next = cmd; 251 1.14 mycroft /*FALLTHROUGH*/ 252 1.1 alm case EMPTY: /* d D g G h H l n N p P q x = \0 */ 253 1.1 alm p++; 254 1.1 alm EATSPACE(); 255 1.40 christos switch (*p) { 256 1.40 christos case ';': 257 1.1 alm p++; 258 1.1 alm link = &cmd->next; 259 1.1 alm goto semicolon; 260 1.40 christos case '}': 261 1.40 christos goto semicolon; 262 1.40 christos case '\0': 263 1.40 christos break; 264 1.40 christos default: 265 1.41 christos errx(1, "%lu: %s: extra characters at the end of %c command", 266 1.41 christos linenum, fname, cmd->code); 267 1.40 christos } 268 1.1 alm break; 269 1.1 alm case TEXT: /* a c i */ 270 1.1 alm p++; 271 1.1 alm EATSPACE(); 272 1.1 alm if (*p != '\\') 273 1.41 christos errx(1, 274 1.41 christos "%lu: %s: command %c expects \\ followed by text", linenum, fname, cmd->code); 275 1.1 alm p++; 276 1.1 alm EATSPACE(); 277 1.1 alm if (*p) 278 1.41 christos errx(1, 279 1.41 christos "%lu: %s: extra characters after \\ at the end of %c command", 280 1.41 christos linenum, fname, cmd->code); 281 1.1 alm cmd->t = compile_text(); 282 1.1 alm break; 283 1.1 alm case COMMENT: /* \0 # */ 284 1.1 alm break; 285 1.1 alm case WFILE: /* w */ 286 1.1 alm p++; 287 1.1 alm EATSPACE(); 288 1.1 alm if (*p == '\0') 289 1.41 christos errx(1, "%lu: %s: filename expected", linenum, fname); 290 1.9 cgd cmd->t = duptoeol(p, "w command"); 291 1.1 alm if (aflag) 292 1.1 alm cmd->u.fd = -1; 293 1.41 christos else if ((cmd->u.fd = open(p, 294 1.1 alm O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 295 1.1 alm DEFFILEMODE)) == -1) 296 1.41 christos err(1, "%s", p); 297 1.1 alm break; 298 1.1 alm case RFILE: /* r */ 299 1.1 alm p++; 300 1.1 alm EATSPACE(); 301 1.1 alm if (*p == '\0') 302 1.41 christos errx(1, "%lu: %s: filename expected", linenum, fname); 303 1.1 alm else 304 1.9 cgd cmd->t = duptoeol(p, "read command"); 305 1.1 alm break; 306 1.1 alm case BRANCH: /* b t */ 307 1.1 alm p++; 308 1.1 alm EATSPACE(); 309 1.1 alm if (*p == '\0') 310 1.1 alm cmd->t = NULL; 311 1.1 alm else 312 1.9 cgd cmd->t = duptoeol(p, "branch"); 313 1.1 alm break; 314 1.1 alm case LABEL: /* : */ 315 1.1 alm p++; 316 1.1 alm EATSPACE(); 317 1.9 cgd cmd->t = duptoeol(p, "label"); 318 1.1 alm if (strlen(p) == 0) 319 1.41 christos errx(1, "%lu: %s: empty label", linenum, fname); 320 1.9 cgd enterlabel(cmd); 321 1.1 alm break; 322 1.1 alm case SUBST: /* s */ 323 1.1 alm p++; 324 1.1 alm if (*p == '\0' || *p == '\\') 325 1.41 christos errx(1, 326 1.41 christos "%lu: %s: substitute pattern can not be delimited by newline or backslash", 327 1.41 christos linenum, fname); 328 1.41 christos cmd->u.s = xcalloc(1, sizeof(struct s_subst)); 329 1.41 christos p = compile_delimited(p, re, 0); 330 1.1 alm if (p == NULL) 331 1.41 christos errx(1, 332 1.41 christos "%lu: %s: unterminated substitute pattern", linenum, fname); 333 1.41 christos 334 1.41 christos /* Compile RE with no case sensitivity temporarily */ 335 1.41 christos if (*re == '\0') 336 1.41 christos cmd->u.s->re = NULL; 337 1.41 christos else 338 1.41 christos cmd->u.s->re = compile_re(re, 0); 339 1.1 alm --p; 340 1.1 alm p = compile_subst(p, cmd->u.s); 341 1.1 alm p = compile_flags(p, cmd->u.s); 342 1.41 christos 343 1.41 christos /* Recompile RE with case sensitivity from "I" flag if any */ 344 1.41 christos if (*re == '\0') 345 1.41 christos cmd->u.s->re = NULL; 346 1.41 christos else 347 1.41 christos cmd->u.s->re = compile_re(re, cmd->u.s->icase); 348 1.1 alm EATSPACE(); 349 1.1 alm if (*p == ';') { 350 1.1 alm p++; 351 1.1 alm link = &cmd->next; 352 1.1 alm goto semicolon; 353 1.1 alm } 354 1.1 alm break; 355 1.1 alm case TR: /* y */ 356 1.1 alm p++; 357 1.41 christos p = compile_tr(p, &cmd->u.y); 358 1.1 alm EATSPACE(); 359 1.40 christos switch (*p) { 360 1.40 christos case ';': 361 1.1 alm p++; 362 1.1 alm link = &cmd->next; 363 1.1 alm goto semicolon; 364 1.40 christos case '}': 365 1.40 christos goto semicolon; 366 1.40 christos case '\0': 367 1.40 christos break; 368 1.40 christos default: 369 1.41 christos errx(1, 370 1.41 christos "%lu: %s: extra text at the end of a transform command", linenum, fname); 371 1.40 christos } 372 1.41 christos if (*p) 373 1.1 alm break; 374 1.1 alm } 375 1.1 alm } 376 1.1 alm } 377 1.1 alm 378 1.1 alm /* 379 1.51 msaitoh * Get a delimited string. P points to the delimiter of the string; d points 380 1.1 alm * to a buffer area. Newline and delimiter escapes are processed; other 381 1.1 alm * escapes are ignored. 382 1.1 alm * 383 1.1 alm * Returns a pointer to the first character after the final delimiter or NULL 384 1.1 alm * in the case of a non-terminated string. The character array d is filled 385 1.1 alm * with the processed string. 386 1.1 alm */ 387 1.1 alm static char * 388 1.41 christos compile_delimited(char *p, char *d, int is_tr) 389 1.1 alm { 390 1.1 alm char c; 391 1.1 alm 392 1.1 alm c = *p++; 393 1.1 alm if (c == '\0') 394 1.1 alm return (NULL); 395 1.1 alm else if (c == '\\') 396 1.41 christos errx(1, "%lu: %s: \\ can not be used as a string delimiter", 397 1.41 christos linenum, fname); 398 1.1 alm else if (c == '\n') 399 1.41 christos errx(1, "%lu: %s: newline can not be used as a string delimiter", 400 1.41 christos linenum, fname); 401 1.1 alm while (*p) { 402 1.41 christos if (*p == '[' && *p != c) { 403 1.11 alm if ((d = compile_ccl(&p, d)) == NULL) 404 1.41 christos errx(1, "%lu: %s: unbalanced brackets ([])", linenum, fname); 405 1.11 alm continue; 406 1.11 alm } else if (*p == '\\' && p[1] == '[') { 407 1.11 alm *d++ = *p++; 408 1.11 alm } else if (*p == '\\' && p[1] == c) 409 1.1 alm p++; 410 1.1 alm else if (*p == '\\' && p[1] == 'n') { 411 1.1 alm *d++ = '\n'; 412 1.1 alm p += 2; 413 1.1 alm continue; 414 1.41 christos } else if (*p == '\\' && p[1] == '\\') { 415 1.41 christos if (is_tr) 416 1.41 christos p++; 417 1.41 christos else 418 1.41 christos *d++ = *p++; 419 1.41 christos } else if (*p == c) { 420 1.1 alm *d = '\0'; 421 1.1 alm return (p + 1); 422 1.1 alm } 423 1.1 alm *d++ = *p++; 424 1.1 alm } 425 1.1 alm return (NULL); 426 1.11 alm } 427 1.11 alm 428 1.11 alm 429 1.11 alm /* compile_ccl: expand a POSIX character class */ 430 1.11 alm static char * 431 1.24 wiz compile_ccl(char **sp, char *t) 432 1.11 alm { 433 1.11 alm int c, d; 434 1.11 alm char *s = *sp; 435 1.11 alm 436 1.11 alm *t++ = *s++; 437 1.11 alm if (*s == '^') 438 1.11 alm *t++ = *s++; 439 1.11 alm if (*s == ']') 440 1.11 alm *t++ = *s++; 441 1.11 alm for (; *s && (*t = *s) != ']'; s++, t++) 442 1.11 alm if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) { 443 1.11 alm *++t = *++s, t++, s++; 444 1.11 alm for (c = *s; (*t = *s) != ']' || c != d; s++, t++) 445 1.11 alm if ((c = *s) == '\0') 446 1.11 alm return NULL; 447 1.41 christos } 448 1.11 alm return (*s == ']') ? *sp = ++s, ++t : NULL; 449 1.2 alm } 450 1.2 alm 451 1.1 alm /* 452 1.41 christos * Compiles the regular expression in RE and returns a pointer to the compiled 453 1.41 christos * regular expression. 454 1.1 alm * Cflags are passed to regcomp. 455 1.1 alm */ 456 1.41 christos static regex_t * 457 1.41 christos compile_re(char *re, int case_insensitive) 458 1.1 alm { 459 1.41 christos regex_t *rep; 460 1.41 christos int eval, flags; 461 1.41 christos 462 1.1 alm 463 1.41 christos flags = rflags; 464 1.41 christos if (case_insensitive) 465 1.41 christos flags |= REG_ICASE; 466 1.41 christos rep = xmalloc(sizeof(regex_t)); 467 1.48 christos parse_escapes(re); 468 1.41 christos if ((eval = regcomp(rep, re, flags)) != 0) 469 1.41 christos errx(1, "%lu: %s: RE error: %s", 470 1.41 christos linenum, fname, strregerror(eval, rep)); 471 1.41 christos if (maxnsub < rep->re_nsub) 472 1.41 christos maxnsub = rep->re_nsub; 473 1.41 christos return (rep); 474 1.1 alm } 475 1.1 alm 476 1.48 christos static char 477 1.48 christos cton(char c, int base) 478 1.48 christos { 479 1.48 christos switch (c) { 480 1.48 christos case '0': case '1': case '2': case '3': case '4': 481 1.48 christos case '5': case '6': case '7': 482 1.48 christos return (char)(c - '0'); 483 1.48 christos case '8': case '9': 484 1.48 christos return base == 8 ? '?' : (char)(c - '0'); 485 1.48 christos case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 486 1.48 christos return base == 16 ? (char)(c - 'a' + 10) : '?'; 487 1.48 christos case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 488 1.48 christos return base == 16 ? (char)(c - 'A' + 10) : '?'; 489 1.48 christos default: 490 1.48 christos return '?'; 491 1.48 christos } 492 1.48 christos } 493 1.48 christos 494 1.48 christos static int 495 1.48 christos ston(char **pp, char *sp, int base) 496 1.48 christos { 497 1.48 christos char *p = *pp, n; 498 1.48 christos int r = cton(p[1], base); 499 1.55 martin char *ep = p + (base == 16 ? 2 : 3); 500 1.48 christos 501 1.48 christos if (r == '?') 502 1.48 christos return 0; 503 1.54 christos for (p++; p < ep && (n = cton(p[1], base)) != '?'; p++) { 504 1.52 christos int nr = r * base + n; 505 1.52 christos if (nr > 255) 506 1.52 christos break; 507 1.52 christos r = nr; 508 1.48 christos } 509 1.48 christos *sp = (char)r; 510 1.48 christos *pp = p; 511 1.48 christos return 1; 512 1.48 christos } 513 1.48 christos 514 1.48 christos static int 515 1.48 christos unescape(char **pp, char **spp) 516 1.48 christos { 517 1.48 christos char *p = *pp; 518 1.48 christos char *sp = *spp; 519 1.48 christos 520 1.48 christos switch (*p) { 521 1.48 christos case 'o': 522 1.48 christos if (!ston(&p, sp, 8)) 523 1.48 christos return 0; 524 1.48 christos break; 525 1.48 christos case 'd': 526 1.48 christos if (!ston(&p, sp, 10)) 527 1.48 christos return 0; 528 1.48 christos break; 529 1.48 christos case 'x': 530 1.48 christos if (!ston(&p, sp, 16)) 531 1.48 christos return 0; 532 1.48 christos break; 533 1.48 christos case 'a': 534 1.48 christos *sp = '\a'; 535 1.48 christos break; 536 1.48 christos #if 0 537 1.48 christos // No, \b RE 538 1.48 christos case 'b': 539 1.48 christos *sp = '\b'; 540 1.48 christos break; 541 1.48 christos #endif 542 1.48 christos case 'f': 543 1.48 christos *sp = '\f'; 544 1.48 christos break; 545 1.48 christos case 'n': 546 1.48 christos *sp = '\n'; 547 1.48 christos break; 548 1.48 christos case 'r': 549 1.48 christos *sp = '\r'; 550 1.48 christos break; 551 1.50 christos case 't': 552 1.50 christos *sp = '\t'; 553 1.50 christos break; 554 1.48 christos case 'v': 555 1.48 christos *sp = '\v'; 556 1.48 christos break; 557 1.48 christos default: 558 1.48 christos return 0; 559 1.48 christos } 560 1.48 christos *spp = sp + 1; 561 1.48 christos *pp = p; 562 1.48 christos return 1; 563 1.48 christos } 564 1.48 christos 565 1.48 christos static void 566 1.48 christos parse_escapes(char *buf) 567 1.48 christos { 568 1.48 christos char bracket = '\0'; 569 1.48 christos char *p, *q; 570 1.48 christos 571 1.48 christos p = q = buf; 572 1.48 christos 573 1.48 christos for (p = q = buf; *p; p++) { 574 1.48 christos if (*p == '\\' && p[1] && !bracket) { 575 1.48 christos p++; 576 1.48 christos if (unescape(&p, &q)) 577 1.48 christos continue; 578 1.48 christos *q++ = '\\'; 579 1.48 christos } 580 1.48 christos switch (*p) { 581 1.48 christos case '[': 582 1.48 christos if (!bracket) 583 1.48 christos bracket = *p; 584 1.48 christos break; 585 1.48 christos case '.': 586 1.48 christos case ':': 587 1.48 christos case '=': 588 1.48 christos if (bracket == '[' && p[-1] == '[') 589 1.48 christos bracket = *p; 590 1.48 christos break; 591 1.48 christos case ']': 592 1.48 christos if (!bracket) 593 1.48 christos break; 594 1.48 christos if (bracket == '[') 595 1.48 christos bracket = '\0'; 596 1.48 christos else if (p[-2] != bracket && p[-1] == bracket) 597 1.48 christos bracket = '['; 598 1.48 christos break; 599 1.48 christos default: 600 1.48 christos break; 601 1.48 christos } 602 1.48 christos *q++ = *p; 603 1.48 christos } 604 1.48 christos *q = '\0'; 605 1.48 christos } 606 1.48 christos 607 1.1 alm /* 608 1.1 alm * Compile the substitution string of a regular expression and set res to 609 1.1 alm * point to a saved copy of it. Nsub is the number of parenthesized regular 610 1.1 alm * expressions. 611 1.1 alm */ 612 1.1 alm static char * 613 1.24 wiz compile_subst(char *p, struct s_subst *s) 614 1.1 alm { 615 1.41 christos static char lbuf[_POSIX2_LINE_MAX + 1]; 616 1.41 christos size_t asize, size; 617 1.41 christos u_char ref; 618 1.1 alm char c, *text, *op, *sp; 619 1.41 christos int more = 1, sawesc = 0; 620 1.1 alm 621 1.1 alm c = *p++; /* Terminator character */ 622 1.1 alm if (c == '\0') 623 1.1 alm return (NULL); 624 1.1 alm 625 1.1 alm s->maxbref = 0; 626 1.1 alm s->linenum = linenum; 627 1.41 christos asize = 2 * _POSIX2_LINE_MAX + 1; 628 1.41 christos text = xmalloc(asize); 629 1.41 christos size = 0; 630 1.1 alm do { 631 1.1 alm op = sp = text + size; 632 1.1 alm for (; *p; p++) { 633 1.26 minskim if (*p == '\\' || sawesc) { 634 1.26 minskim /* 635 1.26 minskim * If this is a continuation from the last 636 1.26 minskim * buffer, we won't have a character to 637 1.26 minskim * skip over. 638 1.26 minskim */ 639 1.26 minskim if (sawesc) 640 1.26 minskim sawesc = 0; 641 1.26 minskim else 642 1.26 minskim p++; 643 1.26 minskim 644 1.48 christos switch (*p) { 645 1.48 christos case '\0': 646 1.26 minskim /* 647 1.26 minskim * This escaped character is continued 648 1.26 minskim * in the next part of the line. Note 649 1.26 minskim * this fact, then cause the loop to 650 1.26 minskim * exit w/ normal EOL case and reenter 651 1.26 minskim * above with the new buffer. 652 1.26 minskim */ 653 1.26 minskim sawesc = 1; 654 1.26 minskim p--; 655 1.26 minskim continue; 656 1.48 christos case '0': case '1': case '2': case '3': 657 1.48 christos case '4': case '5': case '6': case '7': 658 1.48 christos case '8': case '9': 659 1.1 alm *sp++ = '\\'; 660 1.41 christos ref = (u_char)(*p - '0'); 661 1.1 alm if (s->re != NULL && 662 1.41 christos ref > s->re->re_nsub) 663 1.41 christos errx(1, "%lu: %s: \\%c not defined in the RE", 664 1.41 christos linenum, fname, *p); 665 1.1 alm if (s->maxbref < ref) 666 1.1 alm s->maxbref = ref; 667 1.48 christos break; 668 1.48 christos case '&': 669 1.48 christos case '\\': 670 1.1 alm *sp++ = '\\'; 671 1.48 christos break; 672 1.48 christos default: 673 1.48 christos if (unescape(&p, &sp)) 674 1.48 christos continue; 675 1.48 christos break; 676 1.48 christos } 677 1.1 alm } else if (*p == c) { 678 1.41 christos if (*++p == '\0' && more) { 679 1.41 christos if (cu_fgets(lbuf, sizeof(lbuf), &more)) 680 1.41 christos p = lbuf; 681 1.41 christos } 682 1.1 alm *sp++ = '\0'; 683 1.41 christos size += (size_t)(sp - op); 684 1.1 alm s->new = xrealloc(text, size); 685 1.1 alm return (p); 686 1.1 alm } else if (*p == '\n') { 687 1.41 christos errx(1, 688 1.41 christos "%lu: %s: unescaped newline inside substitute pattern", linenum, fname); 689 1.1 alm /* NOTREACHED */ 690 1.1 alm } 691 1.1 alm *sp++ = *p; 692 1.1 alm } 693 1.41 christos size += (size_t)(sp - op); 694 1.41 christos if (asize - size < _POSIX2_LINE_MAX + 1) { 695 1.41 christos asize *= 2; 696 1.41 christos text = xrealloc(text, asize); 697 1.41 christos } 698 1.41 christos } while (cu_fgets(p = lbuf, sizeof(lbuf), &more)); 699 1.41 christos errx(1, "%lu: %s: unterminated substitute in regular expression", 700 1.41 christos linenum, fname); 701 1.1 alm /* NOTREACHED */ 702 1.1 alm } 703 1.1 alm 704 1.1 alm /* 705 1.1 alm * Compile the flags of the s command 706 1.1 alm */ 707 1.1 alm static char * 708 1.24 wiz compile_flags(char *p, struct s_subst *s) 709 1.1 alm { 710 1.1 alm int gn; /* True if we have seen g or n */ 711 1.41 christos unsigned long nval; 712 1.41 christos char wfile[_POSIX2_LINE_MAX + 1], *q; 713 1.1 alm 714 1.1 alm s->n = 1; /* Default */ 715 1.1 alm s->p = 0; 716 1.1 alm s->wfile = NULL; 717 1.1 alm s->wfd = -1; 718 1.41 christos s->icase = 0; 719 1.1 alm for (gn = 0;;) { 720 1.1 alm EATSPACE(); /* EXTENSION */ 721 1.1 alm switch (*p) { 722 1.1 alm case 'g': 723 1.1 alm if (gn) 724 1.41 christos errx(1, 725 1.41 christos "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); 726 1.1 alm gn = 1; 727 1.1 alm s->n = 0; 728 1.1 alm break; 729 1.1 alm case '\0': 730 1.1 alm case '\n': 731 1.1 alm case ';': 732 1.1 alm return (p); 733 1.1 alm case 'p': 734 1.1 alm s->p = 1; 735 1.1 alm break; 736 1.41 christos case 'i': 737 1.41 christos case 'I': 738 1.41 christos s->icase = 1; 739 1.41 christos break; 740 1.1 alm case '1': case '2': case '3': 741 1.1 alm case '4': case '5': case '6': 742 1.1 alm case '7': case '8': case '9': 743 1.1 alm if (gn) 744 1.41 christos errx(1, 745 1.41 christos "%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); 746 1.1 alm gn = 1; 747 1.41 christos errno = 0; 748 1.41 christos nval = strtoul(p, &p, 10); 749 1.41 christos if (errno == ERANGE || nval > INT_MAX) 750 1.41 christos errx(1, 751 1.41 christos "%lu: %s: overflow in the 'N' substitute flag", linenum, fname); 752 1.41 christos s->n = (int)nval; 753 1.27 grant p--; 754 1.1 alm break; 755 1.1 alm case 'w': 756 1.1 alm p++; 757 1.9 cgd #ifdef HISTORIC_PRACTICE 758 1.1 alm if (*p != ' ') { 759 1.41 christos warnx("%lu: %s: space missing before w wfile", linenum, fname); 760 1.1 alm return (p); 761 1.1 alm } 762 1.1 alm #endif 763 1.1 alm EATSPACE(); 764 1.1 alm q = wfile; 765 1.1 alm while (*p) { 766 1.1 alm if (*p == '\n') 767 1.1 alm break; 768 1.1 alm *q++ = *p++; 769 1.1 alm } 770 1.1 alm *q = '\0'; 771 1.1 alm if (q == wfile) 772 1.41 christos errx(1, "%lu: %s: no wfile specified", linenum, fname); 773 1.1 alm s->wfile = strdup(wfile); 774 1.1 alm if (!aflag && (s->wfd = open(wfile, 775 1.1 alm O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 776 1.1 alm DEFFILEMODE)) == -1) 777 1.41 christos err(1, "%s", wfile); 778 1.1 alm return (p); 779 1.1 alm default: 780 1.41 christos errx(1, "%lu: %s: bad flag in substitute command: '%c'", 781 1.41 christos linenum, fname, *p); 782 1.1 alm break; 783 1.1 alm } 784 1.1 alm p++; 785 1.1 alm } 786 1.1 alm } 787 1.1 alm 788 1.1 alm /* 789 1.1 alm * Compile a translation set of strings into a lookup table. 790 1.1 alm */ 791 1.1 alm static char * 792 1.41 christos compile_tr(char *p, struct s_tr **py) 793 1.1 alm { 794 1.41 christos struct s_tr *y; 795 1.41 christos size_t i; 796 1.41 christos const char *op, *np; 797 1.41 christos char old[_POSIX2_LINE_MAX + 1]; 798 1.41 christos char new[_POSIX2_LINE_MAX + 1]; 799 1.41 christos size_t oclen, oldlen, nclen, newlen; 800 1.41 christos mbstate_t mbs1, mbs2; 801 1.41 christos 802 1.41 christos *py = y = xmalloc(sizeof(*y)); 803 1.41 christos y->multis = NULL; 804 1.41 christos y->nmultis = 0; 805 1.1 alm 806 1.1 alm if (*p == '\0' || *p == '\\') 807 1.41 christos errx(1, 808 1.41 christos "%lu: %s: transform pattern can not be delimited by newline or backslash", 809 1.41 christos linenum, fname); 810 1.41 christos p = compile_delimited(p, old, 1); 811 1.41 christos if (p == NULL) 812 1.41 christos errx(1, "%lu: %s: unterminated transform source string", 813 1.41 christos linenum, fname); 814 1.41 christos p = compile_delimited(p - 1, new, 1); 815 1.41 christos if (p == NULL) 816 1.41 christos errx(1, "%lu: %s: unterminated transform target string", 817 1.41 christos linenum, fname); 818 1.1 alm EATSPACE(); 819 1.41 christos op = old; 820 1.41 christos oldlen = mbsrtowcs(NULL, &op, 0, NULL); 821 1.41 christos if (oldlen == (size_t)-1) 822 1.41 christos err(1, NULL); 823 1.41 christos np = new; 824 1.41 christos newlen = mbsrtowcs(NULL, &np, 0, NULL); 825 1.41 christos if (newlen == (size_t)-1) 826 1.41 christos err(1, NULL); 827 1.41 christos if (newlen != oldlen) 828 1.41 christos errx(1, "%lu: %s: transform strings are not the same length", 829 1.41 christos linenum, fname); 830 1.41 christos if (MB_CUR_MAX == 1) { 831 1.41 christos /* 832 1.41 christos * The single-byte encoding case is easy: generate a 833 1.41 christos * lookup table. 834 1.41 christos */ 835 1.41 christos for (i = 0; i <= UCHAR_MAX; i++) 836 1.41 christos y->bytetab[i] = (u_char)i; 837 1.41 christos for (; *op; op++, np++) 838 1.41 christos y->bytetab[(u_char)*op] = (u_char)*np; 839 1.41 christos } else { 840 1.41 christos /* 841 1.41 christos * Multi-byte encoding case: generate a lookup table as 842 1.41 christos * above, but only for single-byte characters. The first 843 1.41 christos * bytes of multi-byte characters have their lookup table 844 1.41 christos * entries set to 0, which causes do_tr() to search through 845 1.41 christos * an auxiliary vector of multi-byte mappings. 846 1.41 christos */ 847 1.41 christos memset(&mbs1, 0, sizeof(mbs1)); 848 1.41 christos memset(&mbs2, 0, sizeof(mbs2)); 849 1.41 christos for (i = 0; i <= UCHAR_MAX; i++) 850 1.41 christos y->bytetab[i] = (u_char)((btowc((int)i) != WEOF) ? i : 0); 851 1.41 christos while (*op != '\0') { 852 1.41 christos oclen = mbrlen(op, MB_LEN_MAX, &mbs1); 853 1.41 christos if (oclen == (size_t)-1 || oclen == (size_t)-2) 854 1.41 christos errc(1, EILSEQ, NULL); 855 1.41 christos nclen = mbrlen(np, MB_LEN_MAX, &mbs2); 856 1.41 christos if (nclen == (size_t)-1 || nclen == (size_t)-2) 857 1.41 christos errc(1, EILSEQ, NULL); 858 1.41 christos if (oclen == 1 && nclen == 1) 859 1.41 christos y->bytetab[(u_char)*op] = (u_char)*np; 860 1.41 christos else { 861 1.41 christos y->bytetab[(u_char)*op] = 0; 862 1.41 christos y->multis = xrealloc(y->multis, 863 1.41 christos (y->nmultis + 1) * sizeof(*y->multis)); 864 1.41 christos i = y->nmultis++; 865 1.41 christos y->multis[i].fromlen = oclen; 866 1.41 christos memcpy(y->multis[i].from, op, oclen); 867 1.41 christos y->multis[i].tolen = nclen; 868 1.41 christos memcpy(y->multis[i].to, np, nclen); 869 1.41 christos } 870 1.41 christos op += oclen; 871 1.41 christos np += nclen; 872 1.41 christos } 873 1.1 alm } 874 1.1 alm return (p); 875 1.1 alm } 876 1.1 alm 877 1.1 alm /* 878 1.41 christos * Compile the text following an a or i command. 879 1.1 alm */ 880 1.1 alm static char * 881 1.24 wiz compile_text(void) 882 1.1 alm { 883 1.41 christos size_t asize, size; 884 1.41 christos int esc_nl; 885 1.41 christos char *text, *p, *op, *s; 886 1.41 christos char lbuf[_POSIX2_LINE_MAX + 1]; 887 1.41 christos 888 1.41 christos asize = 2 * _POSIX2_LINE_MAX + 1; 889 1.41 christos text = xmalloc(asize); 890 1.41 christos size = 0; 891 1.41 christos while (cu_fgets(lbuf, sizeof(lbuf), NULL)) { 892 1.1 alm op = s = text + size; 893 1.41 christos p = lbuf; 894 1.41 christos for (esc_nl = 0; *p != '\0'; p++) { 895 1.41 christos if (*p == '\\' && p[1] != '\0' && *++p == '\n') 896 1.41 christos esc_nl = 1; 897 1.1 alm *s++ = *p; 898 1.1 alm } 899 1.41 christos size += (size_t)(s - op); 900 1.41 christos if (!esc_nl) { 901 1.1 alm *s = '\0'; 902 1.1 alm break; 903 1.1 alm } 904 1.41 christos if (asize - size < _POSIX2_LINE_MAX + 1) { 905 1.41 christos asize *= 2; 906 1.41 christos text = xrealloc(text, asize); 907 1.41 christos } 908 1.1 alm } 909 1.41 christos text[size] = '\0'; 910 1.41 christos p = xrealloc(text, size + 1); 911 1.41 christos return (p); 912 1.1 alm } 913 1.1 alm 914 1.1 alm /* 915 1.1 alm * Get an address and return a pointer to the first character after 916 1.1 alm * it. Fill the structure pointed to according to the address. 917 1.1 alm */ 918 1.1 alm static char * 919 1.24 wiz compile_addr(char *p, struct s_addr *a) 920 1.1 alm { 921 1.41 christos char *end, re[_POSIX2_LINE_MAX + 1]; 922 1.41 christos int icase; 923 1.1 alm 924 1.41 christos icase = 0; 925 1.41 christos 926 1.41 christos a->type = 0; 927 1.1 alm switch (*p) { 928 1.1 alm case '\\': /* Context address */ 929 1.1 alm ++p; 930 1.1 alm /* FALLTHROUGH */ 931 1.1 alm case '/': /* Context address */ 932 1.41 christos p = compile_delimited(p, re, 0); 933 1.1 alm if (p == NULL) 934 1.41 christos errx(1, "%lu: %s: unterminated regular expression", linenum, fname); 935 1.41 christos /* Check for case insensitive regexp flag */ 936 1.41 christos if (*p == 'I') { 937 1.41 christos icase = 1; 938 1.41 christos p++; 939 1.41 christos } 940 1.41 christos if (*re == '\0') 941 1.41 christos a->u.r = NULL; 942 1.41 christos else 943 1.41 christos a->u.r = compile_re(re, icase); 944 1.1 alm a->type = AT_RE; 945 1.1 alm return (p); 946 1.1 alm 947 1.1 alm case '$': /* Last line */ 948 1.1 alm a->type = AT_LAST; 949 1.1 alm return (p + 1); 950 1.41 christos 951 1.41 christos case '+': /* Relative line number */ 952 1.41 christos a->type = AT_RELLINE; 953 1.41 christos p++; 954 1.41 christos /* FALLTHROUGH */ 955 1.1 alm /* Line number */ 956 1.41 christos case '0': case '1': case '2': case '3': case '4': 957 1.1 alm case '5': case '6': case '7': case '8': case '9': 958 1.41 christos if (a->type == 0) 959 1.41 christos a->type = AT_LINE; 960 1.41 christos a->u.l = strtoul(p, &end, 10); 961 1.1 alm return (end); 962 1.1 alm default: 963 1.41 christos errx(1, "%lu: %s: expected context address", linenum, fname); 964 1.1 alm return (NULL); 965 1.1 alm } 966 1.1 alm } 967 1.1 alm 968 1.1 alm /* 969 1.9 cgd * duptoeol -- 970 1.9 cgd * Return a copy of all the characters up to \n or \0. 971 1.1 alm */ 972 1.1 alm static char * 973 1.36 lukem duptoeol(char *s, const char *ctype) 974 1.1 alm { 975 1.1 alm size_t len; 976 1.9 cgd int ws; 977 1.41 christos char *p, *start; 978 1.1 alm 979 1.9 cgd ws = 0; 980 1.9 cgd for (start = s; *s != '\0' && *s != '\n'; ++s) 981 1.19 christos ws = isspace((unsigned char)*s); 982 1.1 alm *s = '\0'; 983 1.9 cgd if (ws) 984 1.41 christos warnx("%lu: %s: whitespace after %s", linenum, fname, ctype); 985 1.41 christos len = (size_t)(s - start + 1); 986 1.41 christos p = xmalloc(len); 987 1.41 christos return (memmove(p, start, len)); 988 1.1 alm } 989 1.1 alm 990 1.1 alm /* 991 1.9 cgd * Convert goto label names to addresses, and count a and r commands, in 992 1.9 cgd * the given subset of the script. Free the memory used by labels in b 993 1.9 cgd * and t commands (but not by :). 994 1.9 cgd * 995 1.1 alm * TODO: Remove } nodes 996 1.1 alm */ 997 1.1 alm static void 998 1.24 wiz fixuplabel(struct s_command *cp, struct s_command *end) 999 1.1 alm { 1000 1.1 alm 1001 1.1 alm for (; cp != end; cp = cp->next) 1002 1.1 alm switch (cp->code) { 1003 1.1 alm case 'a': 1004 1.1 alm case 'r': 1005 1.1 alm appendnum++; 1006 1.1 alm break; 1007 1.1 alm case 'b': 1008 1.1 alm case 't': 1009 1.9 cgd /* Resolve branch target. */ 1010 1.1 alm if (cp->t == NULL) { 1011 1.1 alm cp->u.c = NULL; 1012 1.1 alm break; 1013 1.1 alm } 1014 1.9 cgd if ((cp->u.c = findlabel(cp->t)) == NULL) 1015 1.41 christos errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t); 1016 1.1 alm free(cp->t); 1017 1.1 alm break; 1018 1.1 alm case '{': 1019 1.9 cgd /* Do interior commands. */ 1020 1.9 cgd fixuplabel(cp->u.c, cp->next); 1021 1.1 alm break; 1022 1.1 alm } 1023 1.9 cgd } 1024 1.9 cgd 1025 1.9 cgd /* 1026 1.9 cgd * Associate the given command label for later lookup. 1027 1.9 cgd */ 1028 1.9 cgd static void 1029 1.24 wiz enterlabel(struct s_command *cp) 1030 1.9 cgd { 1031 1.17 lukem struct labhash **lhp, *lh; 1032 1.17 lukem u_char *p; 1033 1.17 lukem u_int h, c; 1034 1.9 cgd 1035 1.9 cgd for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++) 1036 1.9 cgd h = (h << 5) + h + c; 1037 1.9 cgd lhp = &labels[h & LHMASK]; 1038 1.9 cgd for (lh = *lhp; lh != NULL; lh = lh->lh_next) 1039 1.9 cgd if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0) 1040 1.41 christos errx(1, "%lu: %s: duplicate label '%s'", linenum, fname, cp->t); 1041 1.9 cgd lh = xmalloc(sizeof *lh); 1042 1.9 cgd lh->lh_next = *lhp; 1043 1.9 cgd lh->lh_hash = h; 1044 1.9 cgd lh->lh_cmd = cp; 1045 1.9 cgd lh->lh_ref = 0; 1046 1.9 cgd *lhp = lh; 1047 1.9 cgd } 1048 1.9 cgd 1049 1.9 cgd /* 1050 1.9 cgd * Find the label contained in the command l in the command linked 1051 1.9 cgd * list cp. L is excluded from the search. Return NULL if not found. 1052 1.9 cgd */ 1053 1.9 cgd static struct s_command * 1054 1.24 wiz findlabel(char *name) 1055 1.9 cgd { 1056 1.17 lukem struct labhash *lh; 1057 1.17 lukem u_char *p; 1058 1.17 lukem u_int h, c; 1059 1.9 cgd 1060 1.9 cgd for (h = 0, p = (u_char *)name; (c = *p) != 0; p++) 1061 1.9 cgd h = (h << 5) + h + c; 1062 1.9 cgd for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { 1063 1.9 cgd if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { 1064 1.9 cgd lh->lh_ref = 1; 1065 1.9 cgd return (lh->lh_cmd); 1066 1.9 cgd } 1067 1.9 cgd } 1068 1.9 cgd return (NULL); 1069 1.9 cgd } 1070 1.9 cgd 1071 1.41 christos /* 1072 1.9 cgd * Warn about any unused labels. As a side effect, release the label hash 1073 1.9 cgd * table space. 1074 1.9 cgd */ 1075 1.9 cgd static void 1076 1.24 wiz uselabel(void) 1077 1.9 cgd { 1078 1.17 lukem struct labhash *lh, *next; 1079 1.17 lukem int i; 1080 1.9 cgd 1081 1.9 cgd for (i = 0; i < LHSZ; i++) { 1082 1.9 cgd for (lh = labels[i]; lh != NULL; lh = next) { 1083 1.9 cgd next = lh->lh_next; 1084 1.9 cgd if (!lh->lh_ref) 1085 1.41 christos warnx("%lu: %s: unused label '%s'", 1086 1.41 christos linenum, fname, lh->lh_cmd->t); 1087 1.9 cgd free(lh); 1088 1.9 cgd } 1089 1.9 cgd } 1090 1.1 alm } 1091