11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1992 Diomidis Spinellis. 31590Srgrimes * Copyright (c) 1992, 1993, 1994 41590Srgrimes * The Regents of the University of California. All rights reserved. 51590Srgrimes * 61590Srgrimes * This code is derived from software contributed to Berkeley by 71590Srgrimes * Diomidis Spinellis of Imperial College, University of London. 81590Srgrimes * 91590Srgrimes * Redistribution and use in source and binary forms, with or without 101590Srgrimes * modification, are permitted provided that the following conditions 111590Srgrimes * are met: 121590Srgrimes * 1. Redistributions of source code must retain the above copyright 131590Srgrimes * notice, this list of conditions and the following disclaimer. 141590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 151590Srgrimes * notice, this list of conditions and the following disclaimer in the 161590Srgrimes * documentation and/or other materials provided with the distribution. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 3487766Smarkm#include <sys/cdefs.h> 3587766Smarkm__FBSDID("$FreeBSD: stable/10/usr.bin/sed/process.c 337367 2018-08-06 02:10:52Z pfg $"); 3687766Smarkm 371590Srgrimes#ifndef lint 3887766Smarkmstatic const char sccsid[] = "@(#)process.c 8.6 (Berkeley) 4/20/94"; 3928066Scharnier#endif 401590Srgrimes 411590Srgrimes#include <sys/types.h> 421590Srgrimes#include <sys/stat.h> 431590Srgrimes#include <sys/ioctl.h> 441590Srgrimes#include <sys/uio.h> 451590Srgrimes 461590Srgrimes#include <ctype.h> 4728066Scharnier#include <err.h> 481590Srgrimes#include <errno.h> 491590Srgrimes#include <fcntl.h> 501590Srgrimes#include <limits.h> 511590Srgrimes#include <regex.h> 521590Srgrimes#include <stdio.h> 531590Srgrimes#include <stdlib.h> 541590Srgrimes#include <string.h> 551590Srgrimes#include <unistd.h> 56132083Stjr#include <wchar.h> 57132083Stjr#include <wctype.h> 581590Srgrimes 591590Srgrimes#include "defs.h" 601590Srgrimes#include "extern.h" 611590Srgrimes 62132145Stjrstatic SPACE HS, PS, SS, YS; 631590Srgrimes#define pd PS.deleted 641590Srgrimes#define ps PS.space 651590Srgrimes#define psl PS.len 661590Srgrimes#define hs HS.space 671590Srgrimes#define hsl HS.len 681590Srgrimes 69277538Spfgstatic inline int applies(struct s_command *); 70132145Stjrstatic void do_tr(struct s_tr *); 7192922Simpstatic void flush_appends(void); 72132083Stjrstatic void lputs(char *, size_t); 73302228Spfgstatic int regexec_e(regex_t *, const char *, int, int, size_t, 74302228Spfg size_t); 7592922Simpstatic void regsub(SPACE *, char *, char *); 7692922Simpstatic int substitute(struct s_command *); 771590Srgrimes 781590Srgrimesstruct s_appends *appends; /* Array of pointers to strings to append. */ 79337367Spfgstatic unsigned int appendx; /* Index into appends array. */ 80337367Spfgunsigned int appendnum; /* Size of appends array. */ 811590Srgrimes 821590Srgrimesstatic int lastaddr; /* Set by applies if last address of a range. */ 831590Srgrimesstatic int sdone; /* If any substitutes since last line input. */ 841590Srgrimes /* Iov structure for 'w' commands. */ 851590Srgrimesstatic regex_t *defpreg; 861590Srgrimessize_t maxnsub; 871590Srgrimesregmatch_t *match; 881590Srgrimes 89170609Syar#define OUT() do {fwrite(ps, 1, psl, outfile); fputc('\n', outfile);} while (0) 901590Srgrimes 911590Srgrimesvoid 92122044Sdesprocess(void) 931590Srgrimes{ 941590Srgrimes struct s_command *cp; 951590Srgrimes SPACE tspace; 96144840Sstefanf size_t oldpsl = 0; 9710075Sjkh char *p; 981590Srgrimes 99103715Seric p = NULL; 100103715Seric 1011590Srgrimes for (linenum = 0; mf_fgets(&PS, REPLACE);) { 1021590Srgrimes pd = 0; 10310075Sjkhtop: 1041590Srgrimes cp = prog; 1051590Srgrimesredirect: 1061590Srgrimes while (cp != NULL) { 1071590Srgrimes if (!applies(cp)) { 1081590Srgrimes cp = cp->next; 1091590Srgrimes continue; 1101590Srgrimes } 1111590Srgrimes switch (cp->code) { 1121590Srgrimes case '{': 1131590Srgrimes cp = cp->u.c; 1141590Srgrimes goto redirect; 1151590Srgrimes case 'a': 1161590Srgrimes if (appendx >= appendnum) 11780286Sobrien if ((appends = realloc(appends, 1181590Srgrimes sizeof(struct s_appends) * 11980286Sobrien (appendnum *= 2))) == NULL) 12080286Sobrien err(1, "realloc"); 1211590Srgrimes appends[appendx].type = AP_STRING; 1221590Srgrimes appends[appendx].s = cp->t; 1231590Srgrimes appends[appendx].len = strlen(cp->t); 1241590Srgrimes appendx++; 1251590Srgrimes break; 1261590Srgrimes case 'b': 1271590Srgrimes cp = cp->u.c; 1281590Srgrimes goto redirect; 1291590Srgrimes case 'c': 1301590Srgrimes pd = 1; 1311590Srgrimes psl = 0; 132168211Syar if (cp->a2 == NULL || lastaddr || lastline()) 133122049Sdes (void)fprintf(outfile, "%s", cp->t); 1341590Srgrimes break; 1351590Srgrimes case 'd': 1361590Srgrimes pd = 1; 1371590Srgrimes goto new; 1381590Srgrimes case 'D': 1391590Srgrimes if (pd) 1401590Srgrimes goto new; 14194012Sjmallett if (psl == 0 || 142101668Stjr (p = memchr(ps, '\n', psl)) == NULL) { 1431590Srgrimes pd = 1; 14410075Sjkh goto new; 14510075Sjkh } else { 14610075Sjkh psl -= (p + 1) - ps; 1471590Srgrimes memmove(ps, p + 1, psl); 14810075Sjkh goto top; 1491590Srgrimes } 1501590Srgrimes case 'g': 1511590Srgrimes cspace(&PS, hs, hsl, REPLACE); 1521590Srgrimes break; 1531590Srgrimes case 'G': 154170605Syar cspace(&PS, "\n", 1, APPEND); 155170605Syar cspace(&PS, hs, hsl, APPEND); 1561590Srgrimes break; 1571590Srgrimes case 'h': 1581590Srgrimes cspace(&HS, ps, psl, REPLACE); 1591590Srgrimes break; 1601590Srgrimes case 'H': 161170605Syar cspace(&HS, "\n", 1, APPEND); 162170605Syar cspace(&HS, ps, psl, APPEND); 1631590Srgrimes break; 1641590Srgrimes case 'i': 165122049Sdes (void)fprintf(outfile, "%s", cp->t); 1661590Srgrimes break; 1671590Srgrimes case 'l': 168132083Stjr lputs(ps, psl); 1691590Srgrimes break; 1701590Srgrimes case 'n': 1711590Srgrimes if (!nflag && !pd) 172170609Syar OUT(); 1731590Srgrimes flush_appends(); 1741590Srgrimes if (!mf_fgets(&PS, REPLACE)) 1751590Srgrimes exit(0); 1761590Srgrimes pd = 0; 1771590Srgrimes break; 1781590Srgrimes case 'N': 1791590Srgrimes flush_appends(); 180170605Syar cspace(&PS, "\n", 1, APPEND); 181170605Syar if (!mf_fgets(&PS, APPEND)) 1821590Srgrimes exit(0); 1831590Srgrimes break; 1841590Srgrimes case 'p': 1851590Srgrimes if (pd) 1861590Srgrimes break; 187170609Syar OUT(); 1881590Srgrimes break; 1891590Srgrimes case 'P': 1901590Srgrimes if (pd) 1911590Srgrimes break; 192158989Skrion if ((p = memchr(ps, '\n', psl)) != NULL) { 19310075Sjkh oldpsl = psl; 19498601Stjr psl = p - ps; 1951590Srgrimes } 196170609Syar OUT(); 1971590Srgrimes if (p != NULL) 19810075Sjkh psl = oldpsl; 1991590Srgrimes break; 2001590Srgrimes case 'q': 2011590Srgrimes if (!nflag && !pd) 202170609Syar OUT(); 2031590Srgrimes flush_appends(); 2041590Srgrimes exit(0); 2051590Srgrimes case 'r': 2061590Srgrimes if (appendx >= appendnum) 20780286Sobrien if ((appends = realloc(appends, 2081590Srgrimes sizeof(struct s_appends) * 20980286Sobrien (appendnum *= 2))) == NULL) 21080286Sobrien err(1, "realloc"); 2111590Srgrimes appends[appendx].type = AP_FILE; 2121590Srgrimes appends[appendx].s = cp->t; 2131590Srgrimes appends[appendx].len = strlen(cp->t); 2141590Srgrimes appendx++; 2151590Srgrimes break; 2161590Srgrimes case 's': 2171590Srgrimes sdone |= substitute(cp); 2181590Srgrimes break; 2191590Srgrimes case 't': 2201590Srgrimes if (sdone) { 2211590Srgrimes sdone = 0; 2221590Srgrimes cp = cp->u.c; 2231590Srgrimes goto redirect; 2241590Srgrimes } 2251590Srgrimes break; 2261590Srgrimes case 'w': 2271590Srgrimes if (pd) 2281590Srgrimes break; 2291590Srgrimes if (cp->u.fd == -1 && (cp->u.fd = open(cp->t, 2301590Srgrimes O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 2311590Srgrimes DEFFILEMODE)) == -1) 23228066Scharnier err(1, "%s", cp->t); 233176126Sdwmalone if (write(cp->u.fd, ps, psl) != (ssize_t)psl || 23498601Stjr write(cp->u.fd, "\n", 1) != 1) 23528066Scharnier err(1, "%s", cp->t); 2361590Srgrimes break; 2371590Srgrimes case 'x': 238170608Syar /* 239170608Syar * If the hold space is null, make it empty 240170608Syar * but not null. Otherwise the pattern space 241170608Syar * will become null after the swap, which is 242170608Syar * an abnormal condition. 243170608Syar */ 24499351Stjr if (hs == NULL) 24599351Stjr cspace(&HS, "", 0, REPLACE); 2461590Srgrimes tspace = PS; 2471590Srgrimes PS = HS; 2481590Srgrimes HS = tspace; 2491590Srgrimes break; 2501590Srgrimes case 'y': 25194012Sjmallett if (pd || psl == 0) 2521590Srgrimes break; 253132145Stjr do_tr(cp->u.y); 2541590Srgrimes break; 2551590Srgrimes case ':': 2561590Srgrimes case '}': 2571590Srgrimes break; 2581590Srgrimes case '=': 259122049Sdes (void)fprintf(outfile, "%lu\n", linenum); 2601590Srgrimes } 2611590Srgrimes cp = cp->next; 2621590Srgrimes } /* for all cp */ 2631590Srgrimes 2641590Srgrimesnew: if (!nflag && !pd) 265170609Syar OUT(); 2661590Srgrimes flush_appends(); 2671590Srgrimes } /* for all lines */ 2681590Srgrimes} 2691590Srgrimes 2701590Srgrimes/* 2711590Srgrimes * TRUE if the address passed matches the current program state 2721590Srgrimes * (lastline, linenumber, ps). 2731590Srgrimes */ 274168255Syar#define MATCH(a) \ 275302228Spfg ((a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, 0, psl) : \ 276168255Syar (a)->type == AT_LINE ? linenum == (a)->u.l : lastline()) 2771590Srgrimes 2781590Srgrimes/* 279192732Sbrian * Return TRUE if the command applies to the current line. Sets the start 280192732Sbrian * line for process ranges. Interprets the non-select (``!'') flag. 2811590Srgrimes */ 282277538Spfgstatic inline int 283122044Sdesapplies(struct s_command *cp) 2841590Srgrimes{ 2851590Srgrimes int r; 2861590Srgrimes 2871590Srgrimes lastaddr = 0; 2881590Srgrimes if (cp->a1 == NULL && cp->a2 == NULL) 2891590Srgrimes r = 1; 2901590Srgrimes else if (cp->a2) 291192732Sbrian if (cp->startline > 0) { 292269837Sjlh switch (cp->a2->type) { 293269837Sjlh case AT_RELLINE: 294269837Sjlh if (linenum - cp->startline <= cp->a2->u.l) 295269837Sjlh r = 1; 296269837Sjlh else { 297269837Sjlh cp->startline = 0; 298269837Sjlh r = 0; 299269837Sjlh } 300269837Sjlh break; 301269837Sjlh default: 302269837Sjlh if (MATCH(cp->a2)) { 303269837Sjlh cp->startline = 0; 304269837Sjlh lastaddr = 1; 305269837Sjlh r = 1; 306269837Sjlh } else if (cp->a2->type == AT_LINE && 307269837Sjlh linenum > cp->a2->u.l) { 308269837Sjlh /* 309269837Sjlh * We missed the 2nd address due to a 310269837Sjlh * branch, so just close the range and 311269837Sjlh * return false. 312269837Sjlh */ 313269837Sjlh cp->startline = 0; 314269837Sjlh r = 0; 315269837Sjlh } else 316269837Sjlh r = 1; 317269837Sjlh } 318277933Spfg } else if (cp->a1 && MATCH(cp->a1)) { 3191590Srgrimes /* 3201590Srgrimes * If the second address is a number less than or 3211590Srgrimes * equal to the line number first selected, only 3221590Srgrimes * one line shall be selected. 3231590Srgrimes * -- POSIX 1003.2 324192732Sbrian * Likewise if the relative second line address is zero. 3251590Srgrimes */ 326192732Sbrian if ((cp->a2->type == AT_LINE && 327192732Sbrian linenum >= cp->a2->u.l) || 328192732Sbrian (cp->a2->type == AT_RELLINE && cp->a2->u.l == 0)) 3291590Srgrimes lastaddr = 1; 330192732Sbrian else { 331192732Sbrian cp->startline = linenum; 332192732Sbrian } 3331590Srgrimes r = 1; 3341590Srgrimes } else 3351590Srgrimes r = 0; 3361590Srgrimes else 3371590Srgrimes r = MATCH(cp->a1); 3381590Srgrimes return (cp->nonsel ? ! r : r); 3391590Srgrimes} 3401590Srgrimes 3411590Srgrimes/* 342170608Syar * Reset the sed processor to its initial state. 343168921Syar */ 344168921Syarvoid 345170608Syarresetstate(void) 346168921Syar{ 347168921Syar struct s_command *cp; 348168921Syar 349170608Syar /* 350192732Sbrian * Reset all in-range markers. 351170608Syar */ 352168921Syar for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next) 353168921Syar if (cp->a2) 354192732Sbrian cp->startline = 0; 355170608Syar 356170608Syar /* 357170608Syar * Clear out the hold space. 358170608Syar */ 359170608Syar cspace(&HS, "", 0, REPLACE); 360168921Syar} 361168921Syar 362168921Syar/* 3631590Srgrimes * substitute -- 3641590Srgrimes * Do substitutions in the pattern space. Currently, we build a 3651590Srgrimes * copy of the new pattern space in the substitute space structure 3661590Srgrimes * and then swap them. 3671590Srgrimes */ 3681590Srgrimesstatic int 369122044Sdessubstitute(struct s_command *cp) 3701590Srgrimes{ 3711590Srgrimes SPACE tspace; 3721590Srgrimes regex_t *re; 373300555Spfg regoff_t slen; 3741590Srgrimes int lastempty, n; 375337367Spfg regoff_t le = 0; 3761590Srgrimes char *s; 3771590Srgrimes 3781590Srgrimes s = ps; 3791590Srgrimes re = cp->u.s->re; 3801590Srgrimes if (re == NULL) { 3811590Srgrimes if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) { 3821590Srgrimes linenum = cp->u.s->linenum; 383176126Sdwmalone errx(1, "%lu: %s: \\%u not defined in the RE", 38428066Scharnier linenum, fname, cp->u.s->maxbref); 3851590Srgrimes } 3861590Srgrimes } 387302244Spfg if (!regexec_e(re, ps, 0, 0, 0, psl)) 3881590Srgrimes return (0); 3891590Srgrimes 390122045Sdes SS.len = 0; /* Clean substitute space. */ 391122045Sdes slen = psl; 392122045Sdes n = cp->u.s->n; 3931590Srgrimes lastempty = 1; 3941590Srgrimes 395300555Spfg do { 396300555Spfg /* Copy the leading retained string. */ 397302244Spfg if (n <= 1 && (match[0].rm_so > le)) 398302228Spfg cspace(&SS, s, match[0].rm_so - le, APPEND); 399300555Spfg 400300555Spfg /* Skip zero-length matches right after other matches. */ 401302228Spfg if (lastempty || (match[0].rm_so - le) || 402300555Spfg match[0].rm_so != match[0].rm_eo) { 403300555Spfg if (n <= 1) { 404300555Spfg /* Want this match: append replacement. */ 405302228Spfg regsub(&SS, ps, cp->u.s->new); 406300555Spfg if (n == 1) 407300555Spfg n = -1; 4081590Srgrimes } else { 409300555Spfg /* Want a later match: append original. */ 410302228Spfg if (match[0].rm_eo - le) 411302228Spfg cspace(&SS, s, match[0].rm_eo - le, 412302228Spfg APPEND); 413300555Spfg n--; 4141590Srgrimes } 4151590Srgrimes } 416300555Spfg 417300555Spfg /* Move past this match. */ 418302244Spfg s = ps + match[0].rm_eo; 419302244Spfg slen = psl - match[0].rm_eo; 420302228Spfg le = match[0].rm_eo; 421300555Spfg 422300555Spfg /* 423300555Spfg * After a zero-length match, advance one byte, 424300555Spfg * and at the end of the line, terminate. 425300555Spfg */ 426300555Spfg if (match[0].rm_so == match[0].rm_eo) { 427300555Spfg if (*s == '\0' || *s == '\n') 428300555Spfg slen = -1; 429300555Spfg else 430300555Spfg slen--; 431302228Spfg if (*s != '\0') { 432300555Spfg cspace(&SS, s++, 1, APPEND); 433302228Spfg le++; 434302228Spfg } 435300555Spfg lastempty = 1; 436300555Spfg } else 437300555Spfg lastempty = 0; 438300555Spfg 439302244Spfg } while (n >= 0 && slen >= 0 && 440302244Spfg regexec_e(re, ps, REG_NOTBOL, 0, le, psl)); 441300555Spfg 442300555Spfg /* Did not find the requested number of matches. */ 443303065Spfg if (n > 0) 444300555Spfg return (0); 445300555Spfg 446300555Spfg /* Copy the trailing retained string. */ 447300555Spfg if (slen > 0) 4481590Srgrimes cspace(&SS, s, slen, APPEND); 4491590Srgrimes 4501590Srgrimes /* 4511590Srgrimes * Swap the substitute space and the pattern space, and make sure 4521590Srgrimes * that any leftover pointers into stdio memory get lost. 4531590Srgrimes */ 4541590Srgrimes tspace = PS; 4551590Srgrimes PS = SS; 4561590Srgrimes SS = tspace; 4571590Srgrimes SS.space = SS.back; 4581590Srgrimes 4591590Srgrimes /* Handle the 'p' flag. */ 4601590Srgrimes if (cp->u.s->p) 461170609Syar OUT(); 4621590Srgrimes 4631590Srgrimes /* Handle the 'w' flag. */ 4641590Srgrimes if (cp->u.s->wfile && !pd) { 4651590Srgrimes if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile, 4661590Srgrimes O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) 46728066Scharnier err(1, "%s", cp->u.s->wfile); 468176126Sdwmalone if (write(cp->u.s->wfd, ps, psl) != (ssize_t)psl || 46998601Stjr write(cp->u.s->wfd, "\n", 1) != 1) 47028066Scharnier err(1, "%s", cp->u.s->wfile); 4711590Srgrimes } 4721590Srgrimes return (1); 4731590Srgrimes} 4741590Srgrimes 4751590Srgrimes/* 476132145Stjr * do_tr -- 477132145Stjr * Perform translation ('y' command) in the pattern space. 478132145Stjr */ 479132145Stjrstatic void 480132145Stjrdo_tr(struct s_tr *y) 481132145Stjr{ 482132145Stjr SPACE tmp; 483132145Stjr char c, *p; 484132145Stjr size_t clen, left; 485132145Stjr int i; 486132145Stjr 487132145Stjr if (MB_CUR_MAX == 1) { 488132145Stjr /* 489132145Stjr * Single-byte encoding: perform in-place translation 490132145Stjr * of the pattern space. 491132145Stjr */ 492132145Stjr for (p = ps; p < &ps[psl]; p++) 493132145Stjr *p = y->bytetab[(u_char)*p]; 494132145Stjr } else { 495132145Stjr /* 496132145Stjr * Multi-byte encoding: perform translation into the 497132145Stjr * translation space, then swap the translation and 498132145Stjr * pattern spaces. 499132145Stjr */ 500132145Stjr /* Clean translation space. */ 501132145Stjr YS.len = 0; 502132145Stjr for (p = ps, left = psl; left > 0; p += clen, left -= clen) { 503132145Stjr if ((c = y->bytetab[(u_char)*p]) != '\0') { 504132145Stjr cspace(&YS, &c, 1, APPEND); 505132145Stjr clen = 1; 506132145Stjr continue; 507132145Stjr } 508132145Stjr for (i = 0; i < y->nmultis; i++) 509132145Stjr if (left >= y->multis[i].fromlen && 510132145Stjr memcmp(p, y->multis[i].from, 511132145Stjr y->multis[i].fromlen) == 0) 512132145Stjr break; 513132145Stjr if (i < y->nmultis) { 514132145Stjr cspace(&YS, y->multis[i].to, 515132145Stjr y->multis[i].tolen, APPEND); 516132145Stjr clen = y->multis[i].fromlen; 517132145Stjr } else { 518132145Stjr cspace(&YS, p, 1, APPEND); 519132145Stjr clen = 1; 520132145Stjr } 521132145Stjr } 522132145Stjr /* Swap the translation space and the pattern space. */ 523132145Stjr tmp = PS; 524132145Stjr PS = YS; 525132145Stjr YS = tmp; 526132145Stjr YS.space = YS.back; 527132145Stjr } 528132145Stjr} 529132145Stjr 530132145Stjr/* 5311590Srgrimes * Flush append requests. Always called before reading a line, 5321590Srgrimes * therefore it also resets the substitution done (sdone) flag. 5331590Srgrimes */ 5341590Srgrimesstatic void 535122044Sdesflush_appends(void) 5361590Srgrimes{ 5371590Srgrimes FILE *f; 538337367Spfg unsigned int count, idx; 5391590Srgrimes char buf[8 * 1024]; 5401590Srgrimes 541337367Spfg for (idx = 0; idx < appendx; idx++) 542337367Spfg switch (appends[idx].type) { 5431590Srgrimes case AP_STRING: 544337367Spfg fwrite(appends[idx].s, sizeof(char), appends[idx].len, 545122049Sdes outfile); 5461590Srgrimes break; 5471590Srgrimes case AP_FILE: 5481590Srgrimes /* 5491590Srgrimes * Read files probably shouldn't be cached. Since 5501590Srgrimes * it's not an error to read a non-existent file, 5511590Srgrimes * it's possible that another program is interacting 55296704Strhodes * with the sed script through the filesystem. It 5531590Srgrimes * would be truly bizarre, but possible. It's probably 5541590Srgrimes * not that big a performance win, anyhow. 5551590Srgrimes */ 556337367Spfg if ((f = fopen(appends[idx].s, "r")) == NULL) 5571590Srgrimes break; 55828066Scharnier while ((count = fread(buf, sizeof(char), sizeof(buf), f))) 559122049Sdes (void)fwrite(buf, sizeof(char), count, outfile); 5601590Srgrimes (void)fclose(f); 5611590Srgrimes break; 5621590Srgrimes } 563122049Sdes if (ferror(outfile)) 564122049Sdes errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO)); 5651590Srgrimes appendx = sdone = 0; 5661590Srgrimes} 5671590Srgrimes 5681590Srgrimesstatic void 569132083Stjrlputs(char *s, size_t len) 5701590Srgrimes{ 571132083Stjr static const char escapes[] = "\\\a\b\f\r\t\v"; 572132083Stjr int c, col, width; 573176126Sdwmalone const char *p; 5741590Srgrimes struct winsize win; 5751590Srgrimes static int termwidth = -1; 576132083Stjr size_t clen, i; 577132083Stjr wchar_t wc; 578132083Stjr mbstate_t mbs; 5791590Srgrimes 580122049Sdes if (outfile != stdout) 581122049Sdes termwidth = 60; 58246081Simp if (termwidth == -1) { 58397801Stjr if ((p = getenv("COLUMNS")) && *p != '\0') 5841590Srgrimes termwidth = atoi(p); 5851590Srgrimes else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 5861590Srgrimes win.ws_col > 0) 5871590Srgrimes termwidth = win.ws_col; 5881590Srgrimes else 5891590Srgrimes termwidth = 60; 59046081Simp } 591176126Sdwmalone if (termwidth <= 0) 592176126Sdwmalone termwidth = 1; 5931590Srgrimes 594132083Stjr memset(&mbs, 0, sizeof(mbs)); 595132083Stjr col = 0; 596132083Stjr while (len != 0) { 597132083Stjr clen = mbrtowc(&wc, s, len, &mbs); 598132083Stjr if (clen == 0) 599132083Stjr clen = 1; 600132083Stjr if (clen == (size_t)-1 || clen == (size_t)-2) { 601132083Stjr wc = (unsigned char)*s; 602132083Stjr clen = 1; 603132083Stjr memset(&mbs, 0, sizeof(mbs)); 6041590Srgrimes } 605132083Stjr if (wc == '\n') { 606132083Stjr if (col + 1 >= termwidth) 607132083Stjr fprintf(outfile, "\\\n"); 608132083Stjr fputc('$', outfile); 609132083Stjr fputc('\n', outfile); 610132083Stjr col = 0; 611132083Stjr } else if (iswprint(wc)) { 612132083Stjr width = wcwidth(wc); 613132083Stjr if (col + width >= termwidth) { 614132083Stjr fprintf(outfile, "\\\n"); 615132083Stjr col = 0; 616132083Stjr } 617132083Stjr fwrite(s, 1, clen, outfile); 618132083Stjr col += width; 619132083Stjr } else if (wc != L'\0' && (c = wctob(wc)) != EOF && 620132083Stjr (p = strchr(escapes, c)) != NULL) { 621132083Stjr if (col + 2 >= termwidth) { 622132083Stjr fprintf(outfile, "\\\n"); 623132083Stjr col = 0; 624132083Stjr } 625132083Stjr fprintf(outfile, "\\%c", "\\abfrtv"[p - escapes]); 626132083Stjr col += 2; 62798603Stjr } else { 628176126Sdwmalone if (col + 4 * clen >= (unsigned)termwidth) { 629132083Stjr fprintf(outfile, "\\\n"); 630132083Stjr col = 0; 6311590Srgrimes } 632132083Stjr for (i = 0; i < clen; i++) 633132083Stjr fprintf(outfile, "\\%03o", 634132083Stjr (int)(unsigned char)s[i]); 635132083Stjr col += 4 * clen; 6361590Srgrimes } 637132083Stjr s += clen; 638132083Stjr len -= clen; 6391590Srgrimes } 640132083Stjr if (col + 1 >= termwidth) 641132083Stjr fprintf(outfile, "\\\n"); 642122049Sdes (void)fputc('$', outfile); 643122049Sdes (void)fputc('\n', outfile); 644122049Sdes if (ferror(outfile)) 645122049Sdes errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO)); 6461590Srgrimes} 6471590Srgrimes 648277538Spfgstatic int 649122044Sdesregexec_e(regex_t *preg, const char *string, int eflags, int nomatch, 650302228Spfg size_t start, size_t stop) 6511590Srgrimes{ 6521590Srgrimes int eval; 6538874Srgrimes 6541590Srgrimes if (preg == NULL) { 6551590Srgrimes if (defpreg == NULL) 65628066Scharnier errx(1, "first RE may not be empty"); 6571590Srgrimes } else 6581590Srgrimes defpreg = preg; 6591590Srgrimes 66098601Stjr /* Set anchors */ 661302228Spfg match[0].rm_so = start; 662302228Spfg match[0].rm_eo = stop; 6638874Srgrimes 6641590Srgrimes eval = regexec(defpreg, string, 6651590Srgrimes nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND); 6661590Srgrimes switch(eval) { 6671590Srgrimes case 0: 6681590Srgrimes return (1); 6691590Srgrimes case REG_NOMATCH: 6701590Srgrimes return (0); 6711590Srgrimes } 67228066Scharnier errx(1, "RE error: %s", strregerror(eval, defpreg)); 6731590Srgrimes /* NOTREACHED */ 6741590Srgrimes} 6751590Srgrimes 6761590Srgrimes/* 6771590Srgrimes * regsub - perform substitutions after a regexp match 6781590Srgrimes * Based on a routine by Henry Spencer 6791590Srgrimes */ 6801590Srgrimesstatic void 681122044Sdesregsub(SPACE *sp, char *string, char *src) 6821590Srgrimes{ 68387766Smarkm int len, no; 68487766Smarkm char c, *dst; 6851590Srgrimes 6861590Srgrimes#define NEEDSP(reqlen) \ 687121915Stjr /* XXX What is the +1 for? */ \ 688121915Stjr if (sp->len + (reqlen) + 1 >= sp->blen) { \ 6891590Srgrimes sp->blen += (reqlen) + 1024; \ 69080286Sobrien if ((sp->space = sp->back = realloc(sp->back, sp->blen)) \ 69180286Sobrien == NULL) \ 69280286Sobrien err(1, "realloc"); \ 6931590Srgrimes dst = sp->space + sp->len; \ 6941590Srgrimes } 6951590Srgrimes 6961590Srgrimes dst = sp->space + sp->len; 6971590Srgrimes while ((c = *src++) != '\0') { 6981590Srgrimes if (c == '&') 6991590Srgrimes no = 0; 70017522Sache else if (c == '\\' && isdigit((unsigned char)*src)) 7011590Srgrimes no = *src++ - '0'; 7021590Srgrimes else 7031590Srgrimes no = -1; 7041590Srgrimes if (no < 0) { /* Ordinary character. */ 705122045Sdes if (c == '\\' && (*src == '\\' || *src == '&')) 706122045Sdes c = *src++; 7071590Srgrimes NEEDSP(1); 708122045Sdes *dst++ = c; 7091590Srgrimes ++sp->len; 710122045Sdes } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) { 7111590Srgrimes len = match[no].rm_eo - match[no].rm_so; 7121590Srgrimes NEEDSP(len); 7131590Srgrimes memmove(dst, string + match[no].rm_so, len); 7141590Srgrimes dst += len; 7151590Srgrimes sp->len += len; 7161590Srgrimes } 7171590Srgrimes } 7181590Srgrimes NEEDSP(1); 7191590Srgrimes *dst = '\0'; 7201590Srgrimes} 7211590Srgrimes 7221590Srgrimes/* 723168120Syar * cspace -- 724168120Syar * Concatenate space: append the source space to the destination space, 725168120Syar * allocating new space as necessary. 7261590Srgrimes */ 7271590Srgrimesvoid 728122044Sdescspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag) 7291590Srgrimes{ 7301590Srgrimes size_t tlen; 7311590Srgrimes 7321590Srgrimes /* Make sure SPACE has enough memory and ramp up quickly. */ 7331590Srgrimes tlen = sp->len + len + 1; 7341590Srgrimes if (tlen > sp->blen) { 7351590Srgrimes sp->blen = tlen + 1024; 73680286Sobrien if ((sp->space = sp->back = realloc(sp->back, sp->blen)) == 73780286Sobrien NULL) 73880286Sobrien err(1, "realloc"); 7391590Srgrimes } 7401590Srgrimes 7411590Srgrimes if (spflag == REPLACE) 7421590Srgrimes sp->len = 0; 7431590Srgrimes 7441590Srgrimes memmove(sp->space + sp->len, p, len); 7451590Srgrimes 7461590Srgrimes sp->space[sp->len += len] = '\0'; 7471590Srgrimes} 7481590Srgrimes 7491590Srgrimes/* 7501590Srgrimes * Close all cached opened files and report any errors 7511590Srgrimes */ 7521590Srgrimesvoid 753122044Sdescfclose(struct s_command *cp, struct s_command *end) 7541590Srgrimes{ 7551590Srgrimes 7561590Srgrimes for (; cp != end; cp = cp->next) 7571590Srgrimes switch(cp->code) { 7581590Srgrimes case 's': 7591590Srgrimes if (cp->u.s->wfd != -1 && close(cp->u.s->wfd)) 76028066Scharnier err(1, "%s", cp->u.s->wfile); 7611590Srgrimes cp->u.s->wfd = -1; 7621590Srgrimes break; 7631590Srgrimes case 'w': 7641590Srgrimes if (cp->u.fd != -1 && close(cp->u.fd)) 76528066Scharnier err(1, "%s", cp->t); 7661590Srgrimes cp->u.fd = -1; 7671590Srgrimes break; 7681590Srgrimes case '{': 7691590Srgrimes cfclose(cp->u.c, cp->next); 7701590Srgrimes break; 7711590Srgrimes } 7721590Srgrimes} 773