1 /*- 2 * Copyright (c) 1992 Diomidis Spinellis. 3 * Copyright (c) 1992 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * Diomidis Spinellis of Imperial College, University of London. 8 * 9 * %sccs.include.redist.c% 10 */ 11 12 #ifndef lint 13 static char sccsid[] = "@(#)process.c 5.6 (Berkeley) 08/28/92"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/stat.h> 18 #include <sys/ioctl.h> 19 #include <sys/uio.h> 20 21 #include <ctype.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <limits.h> 25 #include <regex.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "defs.h" 32 #include "extern.h" 33 34 static SPACE HS, PS, SS; 35 #define pd PS.deleted 36 #define ps PS.space 37 #define psl PS.len 38 #define hs HS.space 39 #define hsl HS.len 40 41 static inline int applies __P((struct s_command *)); 42 static void cspace __P((SPACE *, char *, size_t, enum e_spflag)); 43 static void flush_appends __P((void)); 44 static void lputs __P((char *)); 45 static inline int regexec_e __P((regex_t *, const char *, int, int)); 46 static void regsub __P((SPACE *, char *, char *)); 47 static int substitute __P((struct s_command *)); 48 49 struct s_appends *appends; /* Array of pointers to strings to append. */ 50 static int appendx; /* Index into appends array. */ 51 int appendnum; /* Size of appends array. */ 52 53 static int lastaddr; /* Set by applies if last address of a range. */ 54 static int sdone; /* If any substitutes since last line input. */ 55 /* Iov structure for 'w' commands. */ 56 static struct iovec iov[2] = { NULL, 0, "\n", 1 }; 57 58 static regex_t *defpreg; 59 size_t maxnsub; 60 regmatch_t *match; 61 62 void 63 process() 64 { 65 struct s_command *cp; 66 SPACE tspace; 67 size_t len; 68 int r; 69 char oldc, *p; 70 71 for (linenum = 0; mf_fgets(&PS, REPLACE);) { 72 pd = 0; 73 cp = prog; 74 redirect: 75 while (cp != NULL) { 76 if (!applies(cp)) { 77 cp = cp->next; 78 continue; 79 } 80 switch (cp->code) { 81 case '{': 82 cp = cp->u.c; 83 goto redirect; 84 case 'a': 85 if (appendx >= appendnum) 86 appends = xrealloc(appends, 87 sizeof(struct s_appends) * 88 (appendnum *= 2)); 89 appends[appendx].type = AP_STRING; 90 appends[appendx].s = cp->t; 91 appendx++; 92 break; 93 case 'b': 94 cp = cp->u.c; 95 goto redirect; 96 case 'c': 97 pd = 1; 98 psl = 0; 99 if (cp->a2 == NULL || lastaddr) 100 (void)printf("%s", cp->t); 101 break; 102 case 'd': 103 pd = 1; 104 goto new; 105 case 'D': 106 if (pd) 107 goto new; 108 if ((p = strchr(ps, '\n')) == NULL) 109 pd = 1; 110 else { 111 psl -= (p - ps) - 1; 112 memmove(ps, p + 1, psl); 113 } 114 goto new; 115 case 'g': 116 cspace(&PS, hs, hsl, REPLACE); 117 break; 118 case 'G': 119 cspace(&PS, hs, hsl, APPENDNL); 120 break; 121 case 'h': 122 cspace(&HS, ps, psl, REPLACE); 123 break; 124 case 'H': 125 cspace(&HS, ps, psl, APPENDNL); 126 break; 127 case 'i': 128 (void)printf("%s", cp->t); 129 break; 130 case 'l': 131 lputs(ps); 132 break; 133 case 'n': 134 if (!nflag && !pd) 135 (void)printf("%s\n", ps); 136 flush_appends(); 137 r = mf_fgets(&PS, REPLACE); 138 #ifdef HISTORIC_PRACTICE 139 if (!r) 140 exit(0); 141 #endif 142 pd = 0; 143 break; 144 case 'N': 145 flush_appends(); 146 if (!mf_fgets(&PS, APPENDNL)) { 147 if (!nflag && !pd) 148 (void)printf("%s\n", ps); 149 exit(0); 150 } 151 break; 152 case 'p': 153 if (pd) 154 break; 155 (void)printf("%s\n", ps); 156 break; 157 case 'P': 158 if (pd) 159 break; 160 if ((p = strchr(ps, '\n')) != NULL) { 161 oldc = *p; 162 *p = '\0'; 163 } 164 (void)printf("%s\n", ps); 165 if (p != NULL) 166 *p = oldc; 167 break; 168 case 'q': 169 if (!nflag && !pd) 170 (void)printf("%s\n", ps); 171 flush_appends(); 172 exit(0); 173 case 'r': 174 if (appendx >= appendnum) 175 appends = xrealloc(appends, 176 sizeof(struct s_appends) * 177 (appendnum *= 2)); 178 appends[appendx].type = AP_FILE; 179 appends[appendx].s = cp->t; 180 appendx++; 181 break; 182 case 's': 183 sdone = substitute(cp); 184 break; 185 case 't': 186 if (sdone) { 187 sdone = 0; 188 cp = cp->u.c; 189 goto redirect; 190 } 191 break; 192 case 'w': 193 if (pd) 194 break; 195 if (cp->u.fd == -1 && (cp->u.fd = open(cp->t, 196 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 197 DEFFILEMODE)) == -1) 198 err(FATAL, "%s: %s\n", 199 cp->t, strerror(errno)); 200 iov[0].iov_base = ps; 201 iov[0].iov_len = psl; 202 if (writev(cp->u.fd, iov, 2) != psl + 1) 203 err(FATAL, "%s: %s\n", 204 cp->t, strerror(errno)); 205 break; 206 case 'x': 207 tspace = PS; 208 PS = HS; 209 HS = tspace; 210 break; 211 case 'y': 212 if (pd) 213 break; 214 for (p = ps, len = psl; len--; ++p) 215 *p = cp->u.y[*p]; 216 break; 217 case ':': 218 case '}': 219 break; 220 case '=': 221 (void)printf("%lu\n", linenum); 222 } 223 cp = cp->next; 224 } /* for all cp */ 225 226 new: if (!nflag && !pd) 227 (void)printf("%s\n", ps); 228 flush_appends(); 229 } /* for all lines */ 230 } 231 232 /* 233 * TRUE if the address passed matches the current program state 234 * (lastline, linenumber, ps). 235 */ 236 #define MATCH(a) \ 237 (a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1) : \ 238 (a)->type == AT_LINE ? linenum == (a)->u.l : lastline 239 240 /* 241 * Return TRUE if the command applies to the current line. Sets the inrange 242 * flag to process ranges. Interprets the non-select (``!'') flag. 243 */ 244 static inline int 245 applies(cp) 246 struct s_command *cp; 247 { 248 int r; 249 250 lastaddr = 0; 251 if (cp->a1 == NULL && cp->a2 == NULL) 252 r = 1; 253 else if (cp->a2) 254 if (cp->inrange) { 255 if (MATCH(cp->a2)) { 256 cp->inrange = 0; 257 lastaddr = 1; 258 } 259 r = 1; 260 } else if (MATCH(cp->a1)) { 261 /* 262 * If the second address is a number less than or 263 * equal to the line number first selected, only 264 * one line shall be selected. 265 * -- POSIX 1003.2 266 */ 267 if (cp->a2->type == AT_LINE && 268 linenum >= cp->a2->u.l) 269 lastaddr = 1; 270 else 271 cp->inrange = 1; 272 r = 1; 273 } else 274 r = 0; 275 else 276 r = MATCH(cp->a1); 277 return (cp->nonsel ? ! r : r); 278 } 279 280 /* 281 * substitute -- 282 * Do substitutions in the pattern space. Currently, we build a 283 * copy of the new pattern space in the substitute space structure 284 * and then swap them. 285 */ 286 static int 287 substitute(cp) 288 struct s_command *cp; 289 { 290 SPACE tspace; 291 regex_t *re; 292 int n, re_off; 293 char *endp, *s; 294 295 s = ps; 296 re = cp->u.s->re; 297 if (re == NULL) { 298 if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) { 299 linenum = cp->u.s->linenum; 300 err(COMPILE, "\\%d not defined in the RE", 301 cp->u.s->maxbref); 302 } 303 } 304 if (!regexec_e(re, s, 0, 0)) 305 return (0); 306 307 SS.len = 0; /* Clean substitute space. */ 308 n = cp->u.s->n; 309 switch (n) { 310 case 0: /* Global */ 311 do { 312 /* Locate start of replaced string. */ 313 re_off = match[0].rm_so; 314 /* Locate end of replaced string + 1. */ 315 endp = s + match[0].rm_eo; 316 /* Copy leading retained string. */ 317 cspace(&SS, s, re_off, APPEND); 318 /* Add in regular expression. */ 319 regsub(&SS, s, cp->u.s->new); 320 /* Move past this match. */ 321 s += match[0].rm_eo; 322 } while(regexec_e(re, s, REG_NOTBOL, 0)); 323 /* Copy trailing retained string. */ 324 cspace(&SS, s, strlen(s), APPEND); 325 break; 326 default: /* Nth occurrence */ 327 while (--n) { 328 s += match[0].rm_eo; 329 if (!regexec_e(re, s, REG_NOTBOL, 0)) 330 return (0); 331 } 332 /* FALLTHROUGH */ 333 case 1: /* 1st occurrence */ 334 /* Locate start of replaced string. */ 335 re_off = match[0].rm_so + (s - ps); 336 /* Copy leading retained string. */ 337 cspace(&SS, ps, re_off, APPEND); 338 /* Add in regular expression. */ 339 regsub(&SS, s, cp->u.s->new); 340 /* Copy trailing retained string. */ 341 s += match[0].rm_eo; 342 cspace(&SS, s, strlen(s), APPEND); 343 break; 344 } 345 346 /* 347 * Swap the substitute space and the pattern space, and make sure 348 * that any leftover pointers into stdio memory get lost. 349 */ 350 tspace = PS; 351 PS = SS; 352 SS = tspace; 353 SS.space = SS.back; 354 355 /* Handle the 'p' flag. */ 356 if (cp->u.s->p) 357 (void)printf("%s\n", ps); 358 359 /* Handle the 'w' flag. */ 360 if (cp->u.s->wfile && !pd) { 361 if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile, 362 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) 363 err(FATAL, "%s: %s\n", cp->u.s->wfile, strerror(errno)); 364 iov[0].iov_base = ps; 365 iov[0].iov_len = psl; 366 if (writev(cp->u.s->wfd, iov, 2) != psl + 1) 367 err(FATAL, "%s: %s\n", cp->u.s->wfile, strerror(errno)); 368 } 369 return (1); 370 } 371 372 /* 373 * Flush append requests. Always called before reading a line, 374 * therefore it also resets the substitution done (sdone) flag. 375 */ 376 static void 377 flush_appends() 378 { 379 FILE *f; 380 int count, i; 381 char buf[8 * 1024]; 382 383 for (i = 0; i < appendx; i++) 384 switch (appends[i].type) { 385 case AP_STRING: 386 (void)printf("%s", appends[i].s); 387 break; 388 case AP_FILE: 389 /* 390 * Read files probably shouldn't be cached. Since 391 * it's not an error to read a non-existent file, 392 * it's possible that another program is interacting 393 * with the sed script through the file system. It 394 * would be truly bizarre, but possible. It's probably 395 * not that big a performance win, anyhow. 396 */ 397 if ((f = fopen(appends[i].s, "r")) == NULL) 398 break; 399 while (count = fread(buf, 1, sizeof(buf), f)) 400 (void)fwrite(buf, 1, count, stdout); 401 (void)fclose(f); 402 break; 403 } 404 if (ferror(stdout)) 405 err(FATAL, "stdout: %s", strerror(errno ? errno : EIO)); 406 appendx = 0; 407 sdone = 0; 408 } 409 410 static void 411 lputs(s) 412 register char *s; 413 { 414 register int count; 415 register char *escapes, *p; 416 struct winsize win; 417 static int termwidth = -1; 418 419 if (termwidth == -1) 420 if (p = getenv("COLUMNS")) 421 termwidth = atoi(p); 422 else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 423 win.ws_col > 0) 424 termwidth = win.ws_col; 425 else 426 termwidth = 60; 427 428 for (count = 0; *s; ++s) { 429 if (count >= termwidth) { 430 (void)printf("\\\n"); 431 count = 0; 432 } 433 if (isascii(*s) && isprint(*s) && *s != '\\') { 434 (void)putchar(*s); 435 count++; 436 } else { 437 escapes = "\\\a\b\f\n\r\t\v"; 438 (void)putchar('\\'); 439 if (p = strchr(escapes, *s)) { 440 (void)putchar("\\abfnrtv"[p - escapes]); 441 count += 2; 442 } else { 443 (void)printf("%03o", (u_char)*s); 444 count += 4; 445 } 446 } 447 } 448 (void)putchar('$'); 449 (void)putchar('\n'); 450 if (ferror(stdout)) 451 err(FATAL, "stdout: %s", strerror(errno ? errno : EIO)); 452 } 453 454 static inline int 455 regexec_e(preg, string, eflags, nomatch) 456 regex_t *preg; 457 const char *string; 458 int eflags, nomatch; 459 { 460 int eval; 461 462 if (preg == NULL) { 463 if (defpreg == NULL) 464 err(FATAL, "first RE may not be empty"); 465 } else 466 defpreg = preg; 467 468 eval = regexec(defpreg, string, 469 nomatch ? 0 : maxnsub + 1, match, eflags); 470 switch(eval) { 471 case 0: 472 return (1); 473 case REG_NOMATCH: 474 return (0); 475 } 476 err(FATAL, "RE error: %s", strregerror(eval, defpreg)); 477 /* NOTREACHED */ 478 } 479 480 /* 481 * regsub - perform substitutions after a regexp match 482 * Based on a routine by Henry Spencer 483 */ 484 static void 485 regsub(sp, string, src) 486 SPACE *sp; 487 char *string, *src; 488 { 489 register int len, no; 490 register char c, *dst; 491 492 #define NEEDSP(reqlen) \ 493 if (sp->len >= sp->blen - (reqlen) - 1) { \ 494 sp->blen += (reqlen) + 1024; \ 495 sp->space = sp->back = xrealloc(sp->back, sp->blen); \ 496 dst = sp->space + sp->len; \ 497 } 498 499 dst = sp->space + sp->len; 500 while ((c = *src++) != '\0') { 501 if (c == '&') 502 no = 0; 503 else if (c == '\\' && isdigit(*src)) 504 no = *src++ - '0'; 505 else 506 no = -1; 507 if (no < 0) { /* Ordinary character. */ 508 if (c == '\\' && (*src == '\\' || *src == '&')) 509 c = *src++; 510 NEEDSP(1); 511 *dst++ = c; 512 ++sp->len; 513 } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) { 514 len = match[no].rm_eo - match[no].rm_so; 515 NEEDSP(len); 516 memmove(dst, string + match[no].rm_so, len); 517 dst += len; 518 sp->len += len; 519 } 520 } 521 NEEDSP(1); 522 *dst = '\0'; 523 } 524 525 /* 526 * aspace -- 527 * Append the source space to the destination space, allocating new 528 * space as necessary. 529 */ 530 void 531 cspace(sp, p, len, spflag) 532 SPACE *sp; 533 char *p; 534 size_t len; 535 enum e_spflag spflag; 536 { 537 size_t tlen; 538 539 /* 540 * Make sure SPACE has enough memory and ramp up quickly. Appends 541 * need two extra bytes, one for the newline, one for a terminating 542 * NULL. 543 */ 544 tlen = sp->len + len + spflag == APPENDNL ? 2 : 1; 545 if (tlen > sp->blen) { 546 sp->blen = tlen + 1024; 547 sp->space = sp->back = xrealloc(sp->back, sp->blen); 548 } 549 550 if (spflag == APPENDNL) 551 sp->space[sp->len++] = '\n'; 552 else if (spflag == REPLACE) 553 sp->len = 0; 554 555 memmove(sp->space + sp->len, p, len); 556 sp->space[sp->len += len] = '\0'; 557 } 558 559 /* 560 * Close all cached opened files and report any errors 561 */ 562 void 563 cfclose(cp) 564 register struct s_command *cp; 565 { 566 567 for (; cp != NULL; cp = cp->next) 568 switch(cp->code) { 569 case 's': 570 if (cp->u.s->wfd != -1 && close(cp->u.s->wfd)) 571 err(FATAL, 572 "%s: %s", cp->u.s->wfile, strerror(errno)); 573 cp->u.s->wfd = -1; 574 break; 575 case 'w': 576 if (cp->u.fd != -1 && close(cp->u.fd)) 577 err(FATAL, "%s: %s", cp->t, strerror(errno)); 578 cp->u.fd = -1; 579 break; 580 case '{': 581 cfclose(cp->u.c); 582 break; 583 } 584 } 585