1 /* $NetBSD: process.c,v 1.45 2014/06/26 02:14:32 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1992 Diomidis Spinellis. 5 * Copyright (c) 1992, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Diomidis Spinellis of Imperial College, University of London. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #if HAVE_NBTOOL_CONFIG_H 37 #include "nbtool_config.h" 38 #endif 39 40 #include <sys/cdefs.h> 41 __RCSID("$NetBSD: process.c,v 1.45 2014/06/26 02:14:32 christos Exp $"); 42 #ifdef __FBSDID 43 __FBSDID("$FreeBSD: head/usr.bin/sed/process.c 192732 2009-05-25 06:45:33Z brian $"); 44 #endif 45 46 #if 0 47 static const char sccsid[] = "@(#)process.c 8.6 (Berkeley) 4/20/94"; 48 #endif 49 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <sys/ioctl.h> 53 #include <sys/uio.h> 54 55 #include <ctype.h> 56 #include <err.h> 57 #include <errno.h> 58 #include <fcntl.h> 59 #include <limits.h> 60 #include <regex.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 #include <wchar.h> 66 #include <wctype.h> 67 68 #include "defs.h" 69 #include "extern.h" 70 71 static SPACE HS, PS, SS, YS; 72 #define pd PS.deleted 73 #define ps PS.space 74 #define psl PS.len 75 #define hs HS.space 76 #define hsl HS.len 77 78 static __inline int applies(struct s_command *); 79 static void do_tr(struct s_tr *); 80 static void flush_appends(void); 81 static void lputs(char *, size_t); 82 static __inline int regexec_e(regex_t *, const char *, int, int, size_t); 83 static void regsub(SPACE *, char *, char *); 84 static int substitute(struct s_command *); 85 86 struct s_appends *appends; /* Array of pointers to strings to append. */ 87 static size_t appendx; /* Index into appends array. */ 88 size_t appendnum; /* Size of appends array. */ 89 90 static int lastaddr; /* Set by applies if last address of a range. */ 91 static int sdone; /* If any substitutes since last line input. */ 92 /* Iov structure for 'w' commands. */ 93 static regex_t *defpreg; 94 size_t maxnsub; 95 regmatch_t *match; 96 97 #define OUT() do {fwrite(ps, 1, psl, outfile); fputc('\n', outfile);} while (0) 98 99 void 100 process(void) 101 { 102 struct s_command *cp; 103 SPACE tspace; 104 size_t oldpsl = 0; 105 char *p; 106 107 p = NULL; 108 109 for (linenum = 0; mf_fgets(&PS, REPLACE);) { 110 pd = 0; 111 top: 112 cp = prog; 113 redirect: 114 while (cp != NULL) { 115 if (!applies(cp)) { 116 cp = cp->next; 117 continue; 118 } 119 switch (cp->code) { 120 case '{': 121 cp = cp->u.c; 122 goto redirect; 123 case 'a': 124 if (appendx >= appendnum) 125 appends = xrealloc(appends, 126 sizeof(struct s_appends) * 127 (appendnum *= 2)); 128 appends[appendx].type = AP_STRING; 129 appends[appendx].s = cp->t; 130 appends[appendx].len = strlen(cp->t); 131 appendx++; 132 break; 133 case 'b': 134 cp = cp->u.c; 135 goto redirect; 136 case 'c': 137 pd = 1; 138 psl = 0; 139 if (cp->a2 == NULL || lastaddr || lastline()) 140 (void)fprintf(outfile, "%s", cp->t); 141 goto new; 142 case 'd': 143 pd = 1; 144 goto new; 145 case 'D': 146 if (pd) 147 goto new; 148 if (psl == 0 || 149 (p = memchr(ps, '\n', psl - 1)) == NULL) { 150 pd = 1; 151 goto new; 152 } else { 153 psl -= (size_t)((p + 1) - ps); 154 memmove(ps, p + 1, psl); 155 goto top; 156 } 157 case 'g': 158 cspace(&PS, hs, hsl, REPLACE); 159 break; 160 case 'G': 161 cspace(&PS, "\n", 1, APPEND); 162 cspace(&PS, hs, hsl, APPEND); 163 break; 164 case 'h': 165 cspace(&HS, ps, psl, REPLACE); 166 break; 167 case 'H': 168 cspace(&HS, "\n", 1, APPEND); 169 cspace(&HS, ps, psl, APPEND); 170 break; 171 case 'i': 172 (void)fprintf(outfile, "%s", cp->t); 173 break; 174 case 'l': 175 lputs(ps, psl); 176 break; 177 case 'n': 178 if (!nflag && !pd) 179 OUT(); 180 flush_appends(); 181 if (!mf_fgets(&PS, REPLACE)) 182 exit(0); 183 pd = 0; 184 break; 185 case 'N': 186 flush_appends(); 187 cspace(&PS, "\n", 1, APPEND); 188 if (!mf_fgets(&PS, APPEND)) 189 exit(0); 190 break; 191 case 'p': 192 if (pd) 193 break; 194 OUT(); 195 break; 196 case 'P': 197 if (pd) 198 break; 199 if ((p = memchr(ps, '\n', psl - 1)) != NULL) { 200 oldpsl = psl; 201 psl = (size_t)(p - ps); 202 } 203 OUT(); 204 if (p != NULL) 205 psl = oldpsl; 206 break; 207 case 'q': 208 if (!nflag && !pd) 209 OUT(); 210 flush_appends(); 211 exit(0); 212 case 'r': 213 if (appendx >= appendnum) 214 appends = xrealloc(appends, 215 sizeof(struct s_appends) * 216 (appendnum *= 2)); 217 appends[appendx].type = AP_FILE; 218 appends[appendx].s = cp->t; 219 appends[appendx].len = strlen(cp->t); 220 appendx++; 221 break; 222 case 's': 223 sdone |= substitute(cp); 224 break; 225 case 't': 226 if (sdone) { 227 sdone = 0; 228 cp = cp->u.c; 229 goto redirect; 230 } 231 break; 232 case 'w': 233 if (pd) 234 break; 235 if (cp->u.fd == -1 && (cp->u.fd = open(cp->t, 236 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 237 DEFFILEMODE)) == -1) 238 err(1, "%s", cp->t); 239 if (write(cp->u.fd, ps, psl) != (ssize_t)psl || 240 write(cp->u.fd, "\n", 1) != 1) 241 err(1, "%s", cp->t); 242 break; 243 case 'x': 244 /* 245 * If the hold space is null, make it empty 246 * but not null. Otherwise the pattern space 247 * will become null after the swap, which is 248 * an abnormal condition. 249 */ 250 if (hs == NULL) 251 cspace(&HS, "", 0, REPLACE); 252 tspace = PS; 253 PS = HS; 254 HS = tspace; 255 break; 256 case 'y': 257 if (pd || psl == 0) 258 break; 259 do_tr(cp->u.y); 260 break; 261 case ':': 262 case '}': 263 break; 264 case '=': 265 (void)fprintf(outfile, "%lu\n", linenum); 266 } 267 cp = cp->next; 268 } /* for all cp */ 269 270 new: if (!nflag && !pd) 271 OUT(); 272 flush_appends(); 273 } /* for all lines */ 274 } 275 276 /* 277 * TRUE if the address passed matches the current program state 278 * (lastline, linenumber, ps). 279 */ 280 #define MATCH(a) \ 281 ((a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, psl) : \ 282 (a)->type == AT_LINE ? linenum == (a)->u.l : lastline()) 283 284 /* 285 * Return TRUE if the command applies to the current line. Sets the start 286 * line for process ranges. Interprets the non-select (``!'') flag. 287 */ 288 static __inline int 289 applies(struct s_command *cp) 290 { 291 int r; 292 293 lastaddr = 0; 294 if (cp->a1 == NULL && cp->a2 == NULL) 295 r = 1; 296 else if (cp->a2) 297 if (cp->startline > 0) { 298 if (MATCH(cp->a2)) { 299 cp->startline = 0; 300 lastaddr = 1; 301 r = 1; 302 } else if (linenum - cp->startline <= cp->a2->u.l) 303 r = 1; 304 else if ((cp->a2->type == AT_LINE && 305 linenum > cp->a2->u.l) || 306 (cp->a2->type == AT_RELLINE && 307 linenum - cp->startline > cp->a2->u.l)) { 308 /* 309 * We missed the 2nd address due to a branch, 310 * so just close the range and return false. 311 */ 312 cp->startline = 0; 313 r = 0; 314 } else 315 r = 1; 316 } else if (cp->a1 && MATCH(cp->a1)) { 317 /* 318 * If the second address is a number less than or 319 * equal to the line number first selected, only 320 * one line shall be selected. 321 * -- POSIX 1003.2 322 * Likewise if the relative second line address is zero. 323 */ 324 if ((cp->a2->type == AT_LINE && 325 linenum >= cp->a2->u.l) || 326 (cp->a2->type == AT_RELLINE && cp->a2->u.l == 0)) 327 lastaddr = 1; 328 else { 329 cp->startline = linenum; 330 } 331 r = 1; 332 } else 333 r = 0; 334 else 335 r = MATCH(cp->a1); 336 return (cp->nonsel ? ! r : r); 337 } 338 339 /* 340 * Reset the sed processor to its initial state. 341 */ 342 void 343 resetstate(void) 344 { 345 struct s_command *cp; 346 347 /* 348 * Reset all in-range markers. 349 */ 350 for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next) 351 if (cp->a2) 352 cp->startline = 0; 353 354 /* 355 * Clear out the hold space. 356 */ 357 cspace(&HS, "", 0, REPLACE); 358 } 359 360 /* 361 * substitute -- 362 * Do substitutions in the pattern space. Currently, we build a 363 * copy of the new pattern space in the substitute space structure 364 * and then swap them. 365 */ 366 static int 367 substitute(struct s_command *cp) 368 { 369 SPACE tspace; 370 regex_t *re; 371 regoff_t re_off, slen; 372 int lastempty, n; 373 char *s; 374 375 s = ps; 376 re = cp->u.s->re; 377 if (re == NULL) { 378 if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) { 379 linenum = cp->u.s->linenum; 380 errx(1, "%lu: %s: \\%u not defined in the RE", 381 linenum, fname, cp->u.s->maxbref); 382 } 383 } 384 if (!regexec_e(re, s, 0, 0, psl)) 385 return (0); 386 387 SS.len = 0; /* Clean substitute space. */ 388 slen = (regoff_t)psl; 389 n = cp->u.s->n; 390 lastempty = 1; 391 392 switch (n) { 393 case 0: /* Global */ 394 do { 395 if (lastempty || match[0].rm_so != match[0].rm_eo) { 396 /* Locate start of replaced string. */ 397 re_off = match[0].rm_so; 398 /* Copy leading retained string. */ 399 cspace(&SS, s, (size_t)re_off, APPEND); 400 /* Add in regular expression. */ 401 regsub(&SS, s, cp->u.s->new); 402 } 403 404 /* Move past this match. */ 405 if (match[0].rm_so != match[0].rm_eo) { 406 s += match[0].rm_eo; 407 slen -= match[0].rm_eo; 408 lastempty = 0; 409 } else { 410 if (match[0].rm_so < slen) 411 cspace(&SS, s + match[0].rm_so, 1, 412 APPEND); 413 s += match[0].rm_so + 1; 414 slen -= match[0].rm_so + 1; 415 lastempty = 1; 416 } 417 } while (slen >= 0 && regexec_e(re, s, REG_NOTBOL, 0, (size_t)slen)); 418 /* Copy trailing retained string. */ 419 if (slen > 0) 420 cspace(&SS, s, (size_t)slen, APPEND); 421 break; 422 default: /* Nth occurrence */ 423 while (--n) { 424 if (match[0].rm_eo == match[0].rm_so) 425 match[0].rm_eo = match[0].rm_so + 1; 426 s += match[0].rm_eo; 427 slen -= match[0].rm_eo; 428 if (slen < 0) 429 return (0); 430 if (!regexec_e(re, s, REG_NOTBOL, 0, (size_t)slen)) 431 return (0); 432 } 433 /* FALLTHROUGH */ 434 case 1: /* 1st occurrence */ 435 /* Locate start of replaced string. */ 436 re_off = match[0].rm_so + (s - ps); 437 /* Copy leading retained string. */ 438 cspace(&SS, ps, (size_t)re_off, APPEND); 439 /* Add in regular expression. */ 440 regsub(&SS, s, cp->u.s->new); 441 /* Copy trailing retained string. */ 442 s += match[0].rm_eo; 443 slen -= match[0].rm_eo; 444 cspace(&SS, s, (size_t)slen, APPEND); 445 break; 446 } 447 448 /* 449 * Swap the substitute space and the pattern space, and make sure 450 * that any leftover pointers into stdio memory get lost. 451 */ 452 tspace = PS; 453 PS = SS; 454 SS = tspace; 455 SS.space = SS.back; 456 457 /* Handle the 'p' flag. */ 458 if (cp->u.s->p) 459 OUT(); 460 461 /* Handle the 'w' flag. */ 462 if (cp->u.s->wfile && !pd) { 463 if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile, 464 O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) 465 err(1, "%s", cp->u.s->wfile); 466 if (write(cp->u.s->wfd, ps, psl) != (ssize_t)psl || 467 write(cp->u.s->wfd, "\n", 1) != 1) 468 err(1, "%s", cp->u.s->wfile); 469 } 470 return (1); 471 } 472 473 /* 474 * do_tr -- 475 * Perform translation ('y' command) in the pattern space. 476 */ 477 static void 478 do_tr(struct s_tr *y) 479 { 480 SPACE tmp; 481 char c, *p; 482 size_t clen, left; 483 size_t i; 484 485 if (MB_CUR_MAX == 1) { 486 /* 487 * Single-byte encoding: perform in-place translation 488 * of the pattern space. 489 */ 490 for (p = ps; p < &ps[psl]; p++) 491 *p = (char)y->bytetab[(u_char)*p]; 492 } else { 493 /* 494 * Multi-byte encoding: perform translation into the 495 * translation space, then swap the translation and 496 * pattern spaces. 497 */ 498 /* Clean translation space. */ 499 YS.len = 0; 500 for (p = ps, left = psl; left > 0; p += clen, left -= clen) { 501 if ((c = (char)y->bytetab[(u_char)*p]) != '\0') { 502 cspace(&YS, &c, 1, APPEND); 503 clen = 1; 504 continue; 505 } 506 for (i = 0; i < y->nmultis; i++) 507 if (left >= y->multis[i].fromlen && 508 memcmp(p, y->multis[i].from, 509 y->multis[i].fromlen) == 0) 510 break; 511 if (i < y->nmultis) { 512 cspace(&YS, y->multis[i].to, 513 y->multis[i].tolen, APPEND); 514 clen = y->multis[i].fromlen; 515 } else { 516 cspace(&YS, p, 1, APPEND); 517 clen = 1; 518 } 519 } 520 /* Swap the translation space and the pattern space. */ 521 tmp = PS; 522 PS = YS; 523 YS = tmp; 524 YS.space = YS.back; 525 } 526 } 527 528 /* 529 * Flush append requests. Always called before reading a line, 530 * therefore it also resets the substitution done (sdone) flag. 531 */ 532 static void 533 flush_appends(void) 534 { 535 FILE *f; 536 size_t count, i; 537 char buf[8 * 1024]; 538 539 for (i = 0; i < appendx; i++) 540 switch (appends[i].type) { 541 case AP_STRING: 542 fwrite(appends[i].s, sizeof(char), appends[i].len, 543 outfile); 544 break; 545 case AP_FILE: 546 /* 547 * Read files probably shouldn't be cached. Since 548 * it's not an error to read a non-existent file, 549 * it's possible that another program is interacting 550 * with the sed script through the filesystem. It 551 * would be truly bizarre, but possible. It's probably 552 * not that big a performance win, anyhow. 553 */ 554 if ((f = fopen(appends[i].s, "r")) == NULL) 555 break; 556 while ((count = fread(buf, sizeof(char), sizeof(buf), f))) 557 (void)fwrite(buf, sizeof(char), count, outfile); 558 (void)fclose(f); 559 break; 560 } 561 if (ferror(outfile)) 562 errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO)); 563 appendx = 0; 564 sdone = 0; 565 } 566 567 static void 568 lputs(char *s, size_t len) 569 { 570 static const char escapes[] = "\\\a\b\f\r\t\v"; 571 int c; 572 size_t col, width; 573 const char *p; 574 #ifdef TIOCGWINSZ 575 struct winsize win; 576 #endif 577 static size_t termwidth = (size_t)-1; 578 size_t clen, i; 579 wchar_t wc; 580 mbstate_t mbs; 581 582 if (outfile != stdout) 583 termwidth = 60; 584 if (termwidth == (size_t)-1) { 585 if ((p = getenv("COLUMNS")) && *p != '\0') 586 termwidth = (size_t)atoi(p); 587 #ifdef TIOCGWINSZ 588 else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 589 win.ws_col > 0) 590 termwidth = win.ws_col; 591 #endif 592 else 593 termwidth = 60; 594 } 595 if (termwidth == 0) 596 termwidth = 1; 597 598 memset(&mbs, 0, sizeof(mbs)); 599 col = 0; 600 while (len != 0) { 601 clen = mbrtowc(&wc, s, len, &mbs); 602 if (clen == 0) 603 clen = 1; 604 if (clen == (size_t)-1 || clen == (size_t)-2) { 605 wc = (unsigned char)*s; 606 clen = 1; 607 memset(&mbs, 0, sizeof(mbs)); 608 } 609 if (wc == '\n') { 610 if (col + 1 >= termwidth) 611 fprintf(outfile, "\\\n"); 612 fputc('$', outfile); 613 fputc('\n', outfile); 614 col = 0; 615 } else if (iswprint(wc)) { 616 width = (size_t)wcwidth(wc); 617 if (col + width >= termwidth) { 618 fprintf(outfile, "\\\n"); 619 col = 0; 620 } 621 fwrite(s, 1, clen, outfile); 622 col += width; 623 } else if (wc != L'\0' && (c = wctob(wc)) != EOF && 624 (p = strchr(escapes, c)) != NULL) { 625 if (col + 2 >= termwidth) { 626 fprintf(outfile, "\\\n"); 627 col = 0; 628 } 629 fprintf(outfile, "\\%c", "\\abfrtv"[p - escapes]); 630 col += 2; 631 } else { 632 if (col + 4 * clen >= termwidth) { 633 fprintf(outfile, "\\\n"); 634 col = 0; 635 } 636 for (i = 0; i < clen; i++) 637 fprintf(outfile, "\\%03o", 638 (int)(unsigned char)s[i]); 639 col += 4 * clen; 640 } 641 s += clen; 642 len -= clen; 643 } 644 if (col + 1 >= termwidth) 645 fprintf(outfile, "\\\n"); 646 (void)fputc('$', outfile); 647 (void)fputc('\n', outfile); 648 if (ferror(outfile)) 649 errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO)); 650 } 651 652 static __inline int 653 regexec_e(regex_t *preg, const char *string, int eflags, int nomatch, 654 size_t slen) 655 { 656 int eval; 657 #ifndef REG_STARTEND 658 char *buf; 659 #endif 660 661 if (preg == NULL) { 662 if (defpreg == NULL) 663 errx(1, "first RE may not be empty"); 664 } else 665 defpreg = preg; 666 667 /* Set anchors */ 668 #ifndef REG_STARTEND 669 buf = xmalloc(slen + 1); 670 (void)memcpy(buf, string, slen); 671 buf[slen] = '\0'; 672 eval = regexec(defpreg, buf, 673 nomatch ? 0 : maxnsub + 1, match, eflags); 674 free(buf); 675 #else 676 match[0].rm_so = 0; 677 match[0].rm_eo = (regoff_t)slen; 678 eval = regexec(defpreg, string, 679 nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND); 680 #endif 681 switch(eval) { 682 case 0: 683 return (1); 684 case REG_NOMATCH: 685 return (0); 686 } 687 errx(1, "RE error: %s", strregerror(eval, defpreg)); 688 /* NOTREACHED */ 689 } 690 691 /* 692 * regsub - perform substitutions after a regexp match 693 * Based on a routine by Henry Spencer 694 */ 695 static void 696 regsub(SPACE *sp, char *string, char *src) 697 { 698 size_t len; 699 int no; 700 char c, *dst; 701 702 #define NEEDSP(reqlen) \ 703 /* XXX What is the +1 for? */ \ 704 if (sp->len + (reqlen) + 1 >= sp->blen) { \ 705 sp->blen += (reqlen) + 1024; \ 706 sp->space = sp->back = xrealloc(sp->back, sp->blen); \ 707 dst = sp->space + sp->len; \ 708 } 709 710 dst = sp->space + sp->len; 711 while ((c = *src++) != '\0') { 712 if (c == '&') 713 no = 0; 714 else if (c == '\\' && isdigit((unsigned char)*src)) 715 no = *src++ - '0'; 716 else 717 no = -1; 718 if (no < 0) { /* Ordinary character. */ 719 if (c == '\\' && (*src == '\\' || *src == '&')) 720 c = *src++; 721 NEEDSP(1); 722 *dst++ = c; 723 ++sp->len; 724 } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) { 725 len = (size_t)(match[no].rm_eo - match[no].rm_so); 726 NEEDSP(len); 727 memmove(dst, string + match[no].rm_so, len); 728 dst += len; 729 sp->len += len; 730 } 731 } 732 NEEDSP(1); 733 *dst = '\0'; 734 } 735 736 /* 737 * cspace -- 738 * Concatenate space: append the source space to the destination space, 739 * allocating new space as necessary. 740 */ 741 void 742 cspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag) 743 { 744 size_t tlen; 745 746 /* Make sure SPACE has enough memory and ramp up quickly. */ 747 tlen = sp->len + len + 1; 748 if (tlen > sp->blen) { 749 sp->blen = tlen + 1024; 750 sp->space = sp->back = xrealloc(sp->back, sp->blen); 751 } 752 753 if (spflag == REPLACE) 754 sp->len = 0; 755 756 memmove(sp->space + sp->len, p, len); 757 758 sp->space[sp->len += len] = '\0'; 759 } 760 761 /* 762 * Close all cached opened files and report any errors 763 */ 764 void 765 cfclose(struct s_command *cp, struct s_command *end) 766 { 767 768 for (; cp != end; cp = cp->next) 769 switch(cp->code) { 770 case 's': 771 if (cp->u.s->wfd != -1 && close(cp->u.s->wfd)) 772 err(1, "%s", cp->u.s->wfile); 773 cp->u.s->wfd = -1; 774 break; 775 case 'w': 776 if (cp->u.fd != -1 && close(cp->u.fd)) 777 err(1, "%s", cp->t); 778 cp->u.fd = -1; 779 break; 780 case '{': 781 cfclose(cp->u.c, cp->next); 782 break; 783 } 784 } 785