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.12 (Berkeley) 01/15/93"; 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 flush_appends __P((void)); 43 static void lputs __P((char *)); 44 static inline int regexec_e __P((regex_t *, const char *, int, int)); 45 static void regsub __P((SPACE *, char *, char *)); 46 static int substitute __P((struct s_command *)); 47 48 struct s_appends *appends; /* Array of pointers to strings to append. */ 49 static int appendx; /* Index into appends array. */ 50 int appendnum; /* Size of appends array. */ 51 52 static int lastaddr; /* Set by applies if last address of a range. */ 53 static int sdone; /* If any substitutes since last line input. */ 54 /* Iov structure for 'w' commands. */ 55 static struct iovec iov[2] = { NULL, 0, "\n", 1 }; 56 57 static regex_t *defpreg; 58 size_t maxnsub; 59 regmatch_t *match; 60 61 void 62 process() 63 { 64 struct s_command *cp; 65 SPACE tspace; 66 size_t len; 67 int r; 68 char oldc, *p; 69 70 for (linenum = 0; mf_fgets(&PS, REPLACE);) { 71 pd = 0; 72 cp = prog; 73 redirect: 74 while (cp != NULL) { 75 if (!applies(cp)) { 76 cp = cp->next; 77 continue; 78 } 79 switch (cp->code) { 80 case '{': 81 cp = cp->u.c; 82 goto redirect; 83 case 'a': 84 if (appendx >= appendnum) 85 appends = xrealloc(appends, 86 sizeof(struct s_appends) * 87 (appendnum *= 2)); 88 appends[appendx].type = AP_STRING; 89 appends[appendx].s = cp->t; 90 appendx++; 91 break; 92 case 'b': 93 cp = cp->u.c; 94 goto redirect; 95 case 'c': 96 pd = 1; 97 psl = 0; 98 if (cp->a2 == NULL || lastaddr) 99 (void)printf("%s", cp->t); 100 break; 101 case 'd': 102 pd = 1; 103 goto new; 104 case 'D': 105 if (pd) 106 goto new; 107 if ((p = strchr(ps, '\n')) == NULL) 108 pd = 1; 109 else { 110 psl -= (p - ps) - 1; 111 memmove(ps, p + 1, psl); 112 } 113 goto new; 114 case 'g': 115 cspace(&PS, hs, hsl, REPLACE); 116 break; 117 case 'G': 118 cspace(&PS, hs, hsl, APPENDNL); 119 break; 120 case 'h': 121 cspace(&HS, ps, psl, REPLACE); 122 break; 123 case 'H': 124 cspace(&HS, ps, psl, APPENDNL); 125 break; 126 case 'i': 127 (void)printf("%s", cp->t); 128 break; 129 case 'l': 130 lputs(ps); 131 break; 132 case 'n': 133 if (!nflag && !pd) 134 (void)printf("%s\n", ps); 135 flush_appends(); 136 r = mf_fgets(&PS, REPLACE); 137 #ifdef HISTORIC_PRACTICE 138 if (!r) 139 exit(0); 140 #endif 141 pd = 0; 142 break; 143 case 'N': 144 flush_appends(); 145 if (!mf_fgets(&PS, APPENDNL)) { 146 if (!nflag && !pd) 147 (void)printf("%s\n", ps); 148 exit(0); 149 } 150 break; 151 case 'p': 152 if (pd) 153 break; 154 (void)printf("%s\n", ps); 155 break; 156 case 'P': 157 if (pd) 158 break; 159 if ((p = strchr(ps, '\n')) != NULL) { 160 oldc = *p; 161 *p = '\0'; 162 } 163 (void)printf("%s\n", ps); 164 if (p != NULL) 165 *p = oldc; 166 break; 167 case 'q': 168 if (!nflag && !pd) 169 (void)printf("%s\n", ps); 170 flush_appends(); 171 exit(0); 172 case 'r': 173 if (appendx >= appendnum) 174 appends = xrealloc(appends, 175 sizeof(struct s_appends) * 176 (appendnum *= 2)); 177 appends[appendx].type = AP_FILE; 178 appends[appendx].s = cp->t; 179 appendx++; 180 break; 181 case 's': 182 sdone |= substitute(cp); 183 break; 184 case 't': 185 if (sdone) { 186 sdone = 0; 187 cp = cp->u.c; 188 goto redirect; 189 } 190 break; 191 case 'w': 192 if (pd) 193 break; 194 if (cp->u.fd == -1 && (cp->u.fd = open(cp->t, 195 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 196 DEFFILEMODE)) == -1) 197 err(FATAL, "%s: %s\n", 198 cp->t, strerror(errno)); 199 iov[0].iov_base = ps; 200 iov[0].iov_len = psl; 201 if (writev(cp->u.fd, iov, 2) != psl + 1) 202 err(FATAL, "%s: %s\n", 203 cp->t, strerror(errno)); 204 break; 205 case 'x': 206 if (hs == NULL) 207 cspace(&HS, "", 0, REPLACE); 208 tspace = PS; 209 PS = HS; 210 HS = tspace; 211 break; 212 case 'y': 213 if (pd) 214 break; 215 for (p = ps, len = psl; len--; ++p) 216 *p = cp->u.y[*p]; 217 break; 218 case ':': 219 case '}': 220 break; 221 case '=': 222 (void)printf("%lu\n", linenum); 223 } 224 cp = cp->next; 225 } /* for all cp */ 226 227 new: if (!nflag && !pd) 228 (void)printf("%s\n", ps); 229 flush_appends(); 230 } /* for all lines */ 231 } 232 233 /* 234 * TRUE if the address passed matches the current program state 235 * (lastline, linenumber, ps). 236 */ 237 #define MATCH(a) \ 238 (a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1) : \ 239 (a)->type == AT_LINE ? linenum == (a)->u.l : lastline 240 241 /* 242 * Return TRUE if the command applies to the current line. Sets the inrange 243 * flag to process ranges. Interprets the non-select (``!'') flag. 244 */ 245 static inline int 246 applies(cp) 247 struct s_command *cp; 248 { 249 int r; 250 251 lastaddr = 0; 252 if (cp->a1 == NULL && cp->a2 == NULL) 253 r = 1; 254 else if (cp->a2) 255 if (cp->inrange) { 256 if (MATCH(cp->a2)) { 257 cp->inrange = 0; 258 lastaddr = 1; 259 } 260 r = 1; 261 } else if (MATCH(cp->a1)) { 262 /* 263 * If the second address is a number less than or 264 * equal to the line number first selected, only 265 * one line shall be selected. 266 * -- POSIX 1003.2 267 */ 268 if (cp->a2->type == AT_LINE && 269 linenum >= cp->a2->u.l) 270 lastaddr = 1; 271 else 272 cp->inrange = 1; 273 r = 1; 274 } else 275 r = 0; 276 else 277 r = MATCH(cp->a1); 278 return (cp->nonsel ? ! r : r); 279 } 280 281 /* 282 * substitute -- 283 * Do substitutions in the pattern space. Currently, we build a 284 * copy of the new pattern space in the substitute space structure 285 * and then swap them. 286 */ 287 static int 288 substitute(cp) 289 struct s_command *cp; 290 { 291 SPACE tspace; 292 regex_t *re; 293 size_t re_off; 294 int n; 295 char *s; 296 297 s = ps; 298 re = cp->u.s->re; 299 if (re == NULL) { 300 if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) { 301 linenum = cp->u.s->linenum; 302 err(COMPILE, "\\%d not defined in the RE", 303 cp->u.s->maxbref); 304 } 305 } 306 if (!regexec_e(re, s, 0, 0)) 307 return (0); 308 309 SS.len = 0; /* Clean substitute space. */ 310 n = cp->u.s->n; 311 switch (n) { 312 case 0: /* Global */ 313 do { 314 /* Locate start of replaced string. */ 315 re_off = match[0].rm_so; 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 = sdone = 0; 407 } 408 409 static void 410 lputs(s) 411 register char *s; 412 { 413 register int count; 414 register char *escapes, *p; 415 struct winsize win; 416 static int termwidth = -1; 417 418 if (termwidth == -1) 419 if (p = getenv("COLUMNS")) 420 termwidth = atoi(p); 421 else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 422 win.ws_col > 0) 423 termwidth = win.ws_col; 424 else 425 termwidth = 60; 426 427 for (count = 0; *s; ++s) { 428 if (count >= termwidth) { 429 (void)printf("\\\n"); 430 count = 0; 431 } 432 if (isascii(*s) && isprint(*s) && *s != '\\') { 433 (void)putchar(*s); 434 count++; 435 } else { 436 escapes = "\\\a\b\f\n\r\t\v"; 437 (void)putchar('\\'); 438 if (p = strchr(escapes, *s)) { 439 (void)putchar("\\abfnrtv"[p - escapes]); 440 count += 2; 441 } else { 442 (void)printf("%03o", (u_char)*s); 443 count += 4; 444 } 445 } 446 } 447 (void)putchar('$'); 448 (void)putchar('\n'); 449 if (ferror(stdout)) 450 err(FATAL, "stdout: %s", strerror(errno ? errno : EIO)); 451 } 452 453 static inline int 454 regexec_e(preg, string, eflags, nomatch) 455 regex_t *preg; 456 const char *string; 457 int eflags, nomatch; 458 { 459 int eval; 460 461 if (preg == NULL) { 462 if (defpreg == NULL) 463 err(FATAL, "first RE may not be empty"); 464 } else 465 defpreg = preg; 466 467 eval = regexec(defpreg, string, 468 nomatch ? 0 : maxnsub + 1, match, eflags); 469 switch(eval) { 470 case 0: 471 return (1); 472 case REG_NOMATCH: 473 return (0); 474 } 475 err(FATAL, "RE error: %s", strregerror(eval, defpreg)); 476 /* NOTREACHED */ 477 } 478 479 /* 480 * regsub - perform substitutions after a regexp match 481 * Based on a routine by Henry Spencer 482 */ 483 static void 484 regsub(sp, string, src) 485 SPACE *sp; 486 char *string, *src; 487 { 488 register int len, no; 489 register char c, *dst; 490 491 #define NEEDSP(reqlen) \ 492 if (sp->len >= sp->blen - (reqlen) - 1) { \ 493 sp->blen += (reqlen) + 1024; \ 494 sp->space = sp->back = xrealloc(sp->back, sp->blen); \ 495 dst = sp->space + sp->len; \ 496 } 497 498 dst = sp->space + sp->len; 499 while ((c = *src++) != '\0') { 500 if (c == '&') 501 no = 0; 502 else if (c == '\\' && isdigit(*src)) 503 no = *src++ - '0'; 504 else 505 no = -1; 506 if (no < 0) { /* Ordinary character. */ 507 if (c == '\\' && (*src == '\\' || *src == '&')) 508 c = *src++; 509 NEEDSP(1); 510 *dst++ = c; 511 ++sp->len; 512 } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) { 513 len = match[no].rm_eo - match[no].rm_so; 514 NEEDSP(len); 515 memmove(dst, string + match[no].rm_so, len); 516 dst += len; 517 sp->len += len; 518 } 519 } 520 NEEDSP(1); 521 *dst = '\0'; 522 } 523 524 /* 525 * aspace -- 526 * Append the source space to the destination space, allocating new 527 * space as necessary. 528 */ 529 void 530 cspace(sp, p, len, spflag) 531 SPACE *sp; 532 char *p; 533 size_t len; 534 enum e_spflag spflag; 535 { 536 size_t tlen; 537 538 /* 539 * Make sure SPACE has enough memory and ramp up quickly. Appends 540 * need two extra bytes, one for the newline, one for a terminating 541 * NULL. 542 */ 543 tlen = sp->len + len + (spflag == APPENDNL ? 2 : 1); 544 if (tlen > sp->blen) { 545 sp->blen = tlen + 1024; 546 sp->space = sp->back = xrealloc(sp->back, sp->blen); 547 } 548 549 if (spflag == APPENDNL) 550 sp->space[sp->len++] = '\n'; 551 else if (spflag == REPLACE) 552 sp->len = 0; 553 554 memmove(sp->space + sp->len, p, len); 555 sp->space[sp->len += len] = '\0'; 556 } 557 558 /* 559 * Close all cached opened files and report any errors 560 */ 561 void 562 cfclose(cp, end) 563 register struct s_command *cp, *end; 564 { 565 566 for (; cp != end; cp = cp->next) 567 switch(cp->code) { 568 case 's': 569 if (cp->u.s->wfd != -1 && close(cp->u.s->wfd)) 570 err(FATAL, 571 "%s: %s", cp->u.s->wfile, strerror(errno)); 572 cp->u.s->wfd = -1; 573 break; 574 case 'w': 575 if (cp->u.fd != -1 && close(cp->u.fd)) 576 err(FATAL, "%s: %s", cp->t, strerror(errno)); 577 cp->u.fd = -1; 578 break; 579 case '{': 580 cfclose(cp->u.c, cp->next); 581 break; 582 } 583 } 584