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$"); 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 69100359Smarkmstatic __inline int applies(struct s_command *); 70132145Stjrstatic void do_tr(struct s_tr *); 7192922Simpstatic void flush_appends(void); 72132083Stjrstatic void lputs(char *, size_t); 73100359Smarkmstatic __inline int regexec_e(regex_t *, const char *, int, int, size_t); 7492922Simpstatic void regsub(SPACE *, char *, char *); 7592922Simpstatic int substitute(struct s_command *); 761590Srgrimes 771590Srgrimesstruct s_appends *appends; /* Array of pointers to strings to append. */ 781590Srgrimesstatic int appendx; /* Index into appends array. */ 791590Srgrimesint appendnum; /* Size of appends array. */ 801590Srgrimes 811590Srgrimesstatic int lastaddr; /* Set by applies if last address of a range. */ 821590Srgrimesstatic int sdone; /* If any substitutes since last line input. */ 831590Srgrimes /* Iov structure for 'w' commands. */ 841590Srgrimesstatic regex_t *defpreg; 851590Srgrimessize_t maxnsub; 861590Srgrimesregmatch_t *match; 871590Srgrimes 88170609Syar#define OUT() do {fwrite(ps, 1, psl, outfile); fputc('\n', outfile);} while (0) 891590Srgrimes 901590Srgrimesvoid 91122044Sdesprocess(void) 921590Srgrimes{ 931590Srgrimes struct s_command *cp; 941590Srgrimes SPACE tspace; 95144840Sstefanf size_t oldpsl = 0; 9610075Sjkh char *p; 971590Srgrimes 98103715Seric p = NULL; 99103715Seric 1001590Srgrimes for (linenum = 0; mf_fgets(&PS, REPLACE);) { 1011590Srgrimes pd = 0; 10210075Sjkhtop: 1031590Srgrimes cp = prog; 1041590Srgrimesredirect: 1051590Srgrimes while (cp != NULL) { 1061590Srgrimes if (!applies(cp)) { 1071590Srgrimes cp = cp->next; 1081590Srgrimes continue; 1091590Srgrimes } 1101590Srgrimes switch (cp->code) { 1111590Srgrimes case '{': 1121590Srgrimes cp = cp->u.c; 1131590Srgrimes goto redirect; 1141590Srgrimes case 'a': 1151590Srgrimes if (appendx >= appendnum) 11680286Sobrien if ((appends = realloc(appends, 1171590Srgrimes sizeof(struct s_appends) * 11880286Sobrien (appendnum *= 2))) == NULL) 11980286Sobrien err(1, "realloc"); 1201590Srgrimes appends[appendx].type = AP_STRING; 1211590Srgrimes appends[appendx].s = cp->t; 1221590Srgrimes appends[appendx].len = strlen(cp->t); 1231590Srgrimes appendx++; 1241590Srgrimes break; 1251590Srgrimes case 'b': 1261590Srgrimes cp = cp->u.c; 1271590Srgrimes goto redirect; 1281590Srgrimes case 'c': 1291590Srgrimes pd = 1; 1301590Srgrimes psl = 0; 131168211Syar if (cp->a2 == NULL || lastaddr || lastline()) 132122049Sdes (void)fprintf(outfile, "%s", cp->t); 1331590Srgrimes break; 1341590Srgrimes case 'd': 1351590Srgrimes pd = 1; 1361590Srgrimes goto new; 1371590Srgrimes case 'D': 1381590Srgrimes if (pd) 1391590Srgrimes goto new; 14094012Sjmallett if (psl == 0 || 141101668Stjr (p = memchr(ps, '\n', psl)) == NULL) { 1421590Srgrimes pd = 1; 14310075Sjkh goto new; 14410075Sjkh } else { 14510075Sjkh psl -= (p + 1) - ps; 1461590Srgrimes memmove(ps, p + 1, psl); 14710075Sjkh goto top; 1481590Srgrimes } 1491590Srgrimes case 'g': 1501590Srgrimes cspace(&PS, hs, hsl, REPLACE); 1511590Srgrimes break; 1521590Srgrimes case 'G': 153170605Syar cspace(&PS, "\n", 1, APPEND); 154170605Syar cspace(&PS, hs, hsl, APPEND); 1551590Srgrimes break; 1561590Srgrimes case 'h': 1571590Srgrimes cspace(&HS, ps, psl, REPLACE); 1581590Srgrimes break; 1591590Srgrimes case 'H': 160170605Syar cspace(&HS, "\n", 1, APPEND); 161170605Syar cspace(&HS, ps, psl, APPEND); 1621590Srgrimes break; 1631590Srgrimes case 'i': 164122049Sdes (void)fprintf(outfile, "%s", cp->t); 1651590Srgrimes break; 1661590Srgrimes case 'l': 167132083Stjr lputs(ps, psl); 1681590Srgrimes break; 1691590Srgrimes case 'n': 1701590Srgrimes if (!nflag && !pd) 171170609Syar OUT(); 1721590Srgrimes flush_appends(); 1731590Srgrimes if (!mf_fgets(&PS, REPLACE)) 1741590Srgrimes exit(0); 1751590Srgrimes pd = 0; 1761590Srgrimes break; 1771590Srgrimes case 'N': 1781590Srgrimes flush_appends(); 179170605Syar cspace(&PS, "\n", 1, APPEND); 180170605Syar if (!mf_fgets(&PS, APPEND)) 1811590Srgrimes exit(0); 1821590Srgrimes break; 1831590Srgrimes case 'p': 1841590Srgrimes if (pd) 1851590Srgrimes break; 186170609Syar OUT(); 1871590Srgrimes break; 1881590Srgrimes case 'P': 1891590Srgrimes if (pd) 1901590Srgrimes break; 191158989Skrion if ((p = memchr(ps, '\n', psl)) != NULL) { 19210075Sjkh oldpsl = psl; 19398601Stjr psl = p - ps; 1941590Srgrimes } 195170609Syar OUT(); 1961590Srgrimes if (p != NULL) 19710075Sjkh psl = oldpsl; 1981590Srgrimes break; 1991590Srgrimes case 'q': 2001590Srgrimes if (!nflag && !pd) 201170609Syar OUT(); 2021590Srgrimes flush_appends(); 2031590Srgrimes exit(0); 2041590Srgrimes case 'r': 2051590Srgrimes if (appendx >= appendnum) 20680286Sobrien if ((appends = realloc(appends, 2071590Srgrimes sizeof(struct s_appends) * 20880286Sobrien (appendnum *= 2))) == NULL) 20980286Sobrien err(1, "realloc"); 2101590Srgrimes appends[appendx].type = AP_FILE; 2111590Srgrimes appends[appendx].s = cp->t; 2121590Srgrimes appends[appendx].len = strlen(cp->t); 2131590Srgrimes appendx++; 2141590Srgrimes break; 2151590Srgrimes case 's': 2161590Srgrimes sdone |= substitute(cp); 2171590Srgrimes break; 2181590Srgrimes case 't': 2191590Srgrimes if (sdone) { 2201590Srgrimes sdone = 0; 2211590Srgrimes cp = cp->u.c; 2221590Srgrimes goto redirect; 2231590Srgrimes } 2241590Srgrimes break; 2251590Srgrimes case 'w': 2261590Srgrimes if (pd) 2271590Srgrimes break; 2281590Srgrimes if (cp->u.fd == -1 && (cp->u.fd = open(cp->t, 2291590Srgrimes O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, 2301590Srgrimes DEFFILEMODE)) == -1) 23128066Scharnier err(1, "%s", cp->t); 232176126Sdwmalone if (write(cp->u.fd, ps, psl) != (ssize_t)psl || 23398601Stjr write(cp->u.fd, "\n", 1) != 1) 23428066Scharnier err(1, "%s", cp->t); 2351590Srgrimes break; 2361590Srgrimes case 'x': 237170608Syar /* 238170608Syar * If the hold space is null, make it empty 239170608Syar * but not null. Otherwise the pattern space 240170608Syar * will become null after the swap, which is 241170608Syar * an abnormal condition. 242170608Syar */ 24399351Stjr if (hs == NULL) 24499351Stjr cspace(&HS, "", 0, REPLACE); 2451590Srgrimes tspace = PS; 2461590Srgrimes PS = HS; 2471590Srgrimes HS = tspace; 2481590Srgrimes break; 2491590Srgrimes case 'y': 25094012Sjmallett if (pd || psl == 0) 2511590Srgrimes break; 252132145Stjr do_tr(cp->u.y); 2531590Srgrimes break; 2541590Srgrimes case ':': 2551590Srgrimes case '}': 2561590Srgrimes break; 2571590Srgrimes case '=': 258122049Sdes (void)fprintf(outfile, "%lu\n", linenum); 2591590Srgrimes } 2601590Srgrimes cp = cp->next; 2611590Srgrimes } /* for all cp */ 2621590Srgrimes 2631590Srgrimesnew: if (!nflag && !pd) 264170609Syar OUT(); 2651590Srgrimes flush_appends(); 2661590Srgrimes } /* for all lines */ 2671590Srgrimes} 2681590Srgrimes 2691590Srgrimes/* 2701590Srgrimes * TRUE if the address passed matches the current program state 2711590Srgrimes * (lastline, linenumber, ps). 2721590Srgrimes */ 273168255Syar#define MATCH(a) \ 274168255Syar ((a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, psl) : \ 275168255Syar (a)->type == AT_LINE ? linenum == (a)->u.l : lastline()) 2761590Srgrimes 2771590Srgrimes/* 278192732Sbrian * Return TRUE if the command applies to the current line. Sets the start 279192732Sbrian * line for process ranges. Interprets the non-select (``!'') flag. 2801590Srgrimes */ 281100359Smarkmstatic __inline int 282122044Sdesapplies(struct s_command *cp) 2831590Srgrimes{ 2841590Srgrimes int r; 2851590Srgrimes 2861590Srgrimes lastaddr = 0; 2871590Srgrimes if (cp->a1 == NULL && cp->a2 == NULL) 2881590Srgrimes r = 1; 2891590Srgrimes else if (cp->a2) 290192732Sbrian if (cp->startline > 0) { 291269837Sjlh switch (cp->a2->type) { 292269837Sjlh case AT_RELLINE: 293269837Sjlh if (linenum - cp->startline <= cp->a2->u.l) 294269837Sjlh r = 1; 295269837Sjlh else { 296269837Sjlh cp->startline = 0; 297269837Sjlh r = 0; 298269837Sjlh } 299269837Sjlh break; 300269837Sjlh default: 301269837Sjlh if (MATCH(cp->a2)) { 302269837Sjlh cp->startline = 0; 303269837Sjlh lastaddr = 1; 304269837Sjlh r = 1; 305269837Sjlh } else if (cp->a2->type == AT_LINE && 306269837Sjlh linenum > cp->a2->u.l) { 307269837Sjlh /* 308269837Sjlh * We missed the 2nd address due to a 309269837Sjlh * branch, so just close the range and 310269837Sjlh * return false. 311269837Sjlh */ 312269837Sjlh cp->startline = 0; 313269837Sjlh r = 0; 314269837Sjlh } else 315269837Sjlh r = 1; 316269837Sjlh } 3171590Srgrimes } else if (MATCH(cp->a1)) { 3181590Srgrimes /* 3191590Srgrimes * If the second address is a number less than or 3201590Srgrimes * equal to the line number first selected, only 3211590Srgrimes * one line shall be selected. 3221590Srgrimes * -- POSIX 1003.2 323192732Sbrian * Likewise if the relative second line address is zero. 3241590Srgrimes */ 325192732Sbrian if ((cp->a2->type == AT_LINE && 326192732Sbrian linenum >= cp->a2->u.l) || 327192732Sbrian (cp->a2->type == AT_RELLINE && cp->a2->u.l == 0)) 3281590Srgrimes lastaddr = 1; 329192732Sbrian else { 330192732Sbrian cp->startline = linenum; 331192732Sbrian } 3321590Srgrimes r = 1; 3331590Srgrimes } else 3341590Srgrimes r = 0; 3351590Srgrimes else 3361590Srgrimes r = MATCH(cp->a1); 3371590Srgrimes return (cp->nonsel ? ! r : r); 3381590Srgrimes} 3391590Srgrimes 3401590Srgrimes/* 341170608Syar * Reset the sed processor to its initial state. 342168921Syar */ 343168921Syarvoid 344170608Syarresetstate(void) 345168921Syar{ 346168921Syar struct s_command *cp; 347168921Syar 348170608Syar /* 349192732Sbrian * Reset all in-range markers. 350170608Syar */ 351168921Syar for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next) 352168921Syar if (cp->a2) 353192732Sbrian cp->startline = 0; 354170608Syar 355170608Syar /* 356170608Syar * Clear out the hold space. 357170608Syar */ 358170608Syar cspace(&HS, "", 0, REPLACE); 359168921Syar} 360168921Syar 361168921Syar/* 3621590Srgrimes * substitute -- 3631590Srgrimes * Do substitutions in the pattern space. Currently, we build a 3641590Srgrimes * copy of the new pattern space in the substitute space structure 3651590Srgrimes * and then swap them. 3661590Srgrimes */ 3671590Srgrimesstatic int 368122044Sdessubstitute(struct s_command *cp) 3691590Srgrimes{ 3701590Srgrimes SPACE tspace; 3711590Srgrimes regex_t *re; 372115831Sfanf regoff_t re_off, slen; 3731590Srgrimes int lastempty, n; 3741590Srgrimes char *s; 3751590Srgrimes 3761590Srgrimes s = ps; 3771590Srgrimes re = cp->u.s->re; 3781590Srgrimes if (re == NULL) { 3791590Srgrimes if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) { 3801590Srgrimes linenum = cp->u.s->linenum; 381176126Sdwmalone errx(1, "%lu: %s: \\%u not defined in the RE", 38228066Scharnier linenum, fname, cp->u.s->maxbref); 3831590Srgrimes } 3841590Srgrimes } 3851590Srgrimes if (!regexec_e(re, s, 0, 0, psl)) 3861590Srgrimes return (0); 3871590Srgrimes 388122045Sdes SS.len = 0; /* Clean substitute space. */ 389122045Sdes slen = psl; 390122045Sdes n = cp->u.s->n; 3911590Srgrimes lastempty = 1; 3921590Srgrimes 393122045Sdes switch (n) { 394122045Sdes case 0: /* Global */ 395122045Sdes do { 3961590Srgrimes if (lastempty || match[0].rm_so != match[0].rm_eo) { 3971590Srgrimes /* Locate start of replaced string. */ 3981590Srgrimes re_off = match[0].rm_so; 3991590Srgrimes /* Copy leading retained string. */ 4001590Srgrimes cspace(&SS, s, re_off, APPEND); 4011590Srgrimes /* Add in regular expression. */ 4021590Srgrimes regsub(&SS, s, cp->u.s->new); 4031590Srgrimes } 4041590Srgrimes 40510075Sjkh /* Move past this match. */ 4061590Srgrimes if (match[0].rm_so != match[0].rm_eo) { 4071590Srgrimes s += match[0].rm_eo; 4081590Srgrimes slen -= match[0].rm_eo; 4091590Srgrimes lastempty = 0; 4101590Srgrimes } else { 411115871Sfanf if (match[0].rm_so < slen) 41210075Sjkh cspace(&SS, s + match[0].rm_so, 1, 41310075Sjkh APPEND); 4141590Srgrimes s += match[0].rm_so + 1; 4151590Srgrimes slen -= match[0].rm_so + 1; 4161590Srgrimes lastempty = 1; 4171590Srgrimes } 418115831Sfanf } while (slen >= 0 && regexec_e(re, s, REG_NOTBOL, 0, slen)); 4191590Srgrimes /* Copy trailing retained string. */ 4201590Srgrimes if (slen > 0) 4211590Srgrimes cspace(&SS, s, slen, APPEND); 422122045Sdes break; 4231590Srgrimes default: /* Nth occurrence */ 4241590Srgrimes while (--n) { 425115831Sfanf if (match[0].rm_eo == match[0].rm_so) 426115831Sfanf match[0].rm_eo = match[0].rm_so + 1; 4271590Srgrimes s += match[0].rm_eo; 4281590Srgrimes slen -= match[0].rm_eo; 429115831Sfanf if (slen < 0) 430115831Sfanf return (0); 4311590Srgrimes if (!regexec_e(re, s, REG_NOTBOL, 0, slen)) 4321590Srgrimes return (0); 4331590Srgrimes } 4341590Srgrimes /* FALLTHROUGH */ 4351590Srgrimes case 1: /* 1st occurrence */ 4361590Srgrimes /* Locate start of replaced string. */ 4371590Srgrimes re_off = match[0].rm_so + (s - ps); 4381590Srgrimes /* Copy leading retained string. */ 4391590Srgrimes cspace(&SS, ps, re_off, APPEND); 4401590Srgrimes /* Add in regular expression. */ 4411590Srgrimes regsub(&SS, s, cp->u.s->new); 4421590Srgrimes /* Copy trailing retained string. */ 4431590Srgrimes s += match[0].rm_eo; 4441590Srgrimes slen -= match[0].rm_eo; 4451590Srgrimes cspace(&SS, s, slen, APPEND); 4461590Srgrimes break; 4471590Srgrimes } 4481590Srgrimes 4491590Srgrimes /* 4501590Srgrimes * Swap the substitute space and the pattern space, and make sure 4511590Srgrimes * that any leftover pointers into stdio memory get lost. 4521590Srgrimes */ 4531590Srgrimes tspace = PS; 4541590Srgrimes PS = SS; 4551590Srgrimes SS = tspace; 4561590Srgrimes SS.space = SS.back; 4571590Srgrimes 4581590Srgrimes /* Handle the 'p' flag. */ 4591590Srgrimes if (cp->u.s->p) 460170609Syar OUT(); 4611590Srgrimes 4621590Srgrimes /* Handle the 'w' flag. */ 4631590Srgrimes if (cp->u.s->wfile && !pd) { 4641590Srgrimes if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile, 4651590Srgrimes O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) 46628066Scharnier err(1, "%s", cp->u.s->wfile); 467176126Sdwmalone if (write(cp->u.s->wfd, ps, psl) != (ssize_t)psl || 46898601Stjr write(cp->u.s->wfd, "\n", 1) != 1) 46928066Scharnier err(1, "%s", cp->u.s->wfile); 4701590Srgrimes } 4711590Srgrimes return (1); 4721590Srgrimes} 4731590Srgrimes 4741590Srgrimes/* 475132145Stjr * do_tr -- 476132145Stjr * Perform translation ('y' command) in the pattern space. 477132145Stjr */ 478132145Stjrstatic void 479132145Stjrdo_tr(struct s_tr *y) 480132145Stjr{ 481132145Stjr SPACE tmp; 482132145Stjr char c, *p; 483132145Stjr size_t clen, left; 484132145Stjr int i; 485132145Stjr 486132145Stjr if (MB_CUR_MAX == 1) { 487132145Stjr /* 488132145Stjr * Single-byte encoding: perform in-place translation 489132145Stjr * of the pattern space. 490132145Stjr */ 491132145Stjr for (p = ps; p < &ps[psl]; p++) 492132145Stjr *p = y->bytetab[(u_char)*p]; 493132145Stjr } else { 494132145Stjr /* 495132145Stjr * Multi-byte encoding: perform translation into the 496132145Stjr * translation space, then swap the translation and 497132145Stjr * pattern spaces. 498132145Stjr */ 499132145Stjr /* Clean translation space. */ 500132145Stjr YS.len = 0; 501132145Stjr for (p = ps, left = psl; left > 0; p += clen, left -= clen) { 502132145Stjr if ((c = y->bytetab[(u_char)*p]) != '\0') { 503132145Stjr cspace(&YS, &c, 1, APPEND); 504132145Stjr clen = 1; 505132145Stjr continue; 506132145Stjr } 507132145Stjr for (i = 0; i < y->nmultis; i++) 508132145Stjr if (left >= y->multis[i].fromlen && 509132145Stjr memcmp(p, y->multis[i].from, 510132145Stjr y->multis[i].fromlen) == 0) 511132145Stjr break; 512132145Stjr if (i < y->nmultis) { 513132145Stjr cspace(&YS, y->multis[i].to, 514132145Stjr y->multis[i].tolen, APPEND); 515132145Stjr clen = y->multis[i].fromlen; 516132145Stjr } else { 517132145Stjr cspace(&YS, p, 1, APPEND); 518132145Stjr clen = 1; 519132145Stjr } 520132145Stjr } 521132145Stjr /* Swap the translation space and the pattern space. */ 522132145Stjr tmp = PS; 523132145Stjr PS = YS; 524132145Stjr YS = tmp; 525132145Stjr YS.space = YS.back; 526132145Stjr } 527132145Stjr} 528132145Stjr 529132145Stjr/* 5301590Srgrimes * Flush append requests. Always called before reading a line, 5311590Srgrimes * therefore it also resets the substitution done (sdone) flag. 5321590Srgrimes */ 5331590Srgrimesstatic void 534122044Sdesflush_appends(void) 5351590Srgrimes{ 5361590Srgrimes FILE *f; 5371590Srgrimes int count, i; 5381590Srgrimes char buf[8 * 1024]; 5391590Srgrimes 5408874Srgrimes for (i = 0; i < appendx; i++) 5411590Srgrimes switch (appends[i].type) { 5421590Srgrimes case AP_STRING: 5438874Srgrimes fwrite(appends[i].s, sizeof(char), appends[i].len, 544122049Sdes outfile); 5451590Srgrimes break; 5461590Srgrimes case AP_FILE: 5471590Srgrimes /* 5481590Srgrimes * Read files probably shouldn't be cached. Since 5491590Srgrimes * it's not an error to read a non-existent file, 5501590Srgrimes * it's possible that another program is interacting 55196704Strhodes * with the sed script through the filesystem. It 5521590Srgrimes * would be truly bizarre, but possible. It's probably 5531590Srgrimes * not that big a performance win, anyhow. 5541590Srgrimes */ 5551590Srgrimes if ((f = fopen(appends[i].s, "r")) == NULL) 5561590Srgrimes break; 55728066Scharnier while ((count = fread(buf, sizeof(char), sizeof(buf), f))) 558122049Sdes (void)fwrite(buf, sizeof(char), count, outfile); 5591590Srgrimes (void)fclose(f); 5601590Srgrimes break; 5611590Srgrimes } 562122049Sdes if (ferror(outfile)) 563122049Sdes errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO)); 5641590Srgrimes appendx = sdone = 0; 5651590Srgrimes} 5661590Srgrimes 5671590Srgrimesstatic void 568132083Stjrlputs(char *s, size_t len) 5691590Srgrimes{ 570132083Stjr static const char escapes[] = "\\\a\b\f\r\t\v"; 571132083Stjr int c, col, width; 572176126Sdwmalone const char *p; 5731590Srgrimes struct winsize win; 5741590Srgrimes static int termwidth = -1; 575132083Stjr size_t clen, i; 576132083Stjr wchar_t wc; 577132083Stjr mbstate_t mbs; 5781590Srgrimes 579122049Sdes if (outfile != stdout) 580122049Sdes termwidth = 60; 58146081Simp if (termwidth == -1) { 58297801Stjr if ((p = getenv("COLUMNS")) && *p != '\0') 5831590Srgrimes termwidth = atoi(p); 5841590Srgrimes else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 5851590Srgrimes win.ws_col > 0) 5861590Srgrimes termwidth = win.ws_col; 5871590Srgrimes else 5881590Srgrimes termwidth = 60; 58946081Simp } 590176126Sdwmalone if (termwidth <= 0) 591176126Sdwmalone termwidth = 1; 5921590Srgrimes 593132083Stjr memset(&mbs, 0, sizeof(mbs)); 594132083Stjr col = 0; 595132083Stjr while (len != 0) { 596132083Stjr clen = mbrtowc(&wc, s, len, &mbs); 597132083Stjr if (clen == 0) 598132083Stjr clen = 1; 599132083Stjr if (clen == (size_t)-1 || clen == (size_t)-2) { 600132083Stjr wc = (unsigned char)*s; 601132083Stjr clen = 1; 602132083Stjr memset(&mbs, 0, sizeof(mbs)); 6031590Srgrimes } 604132083Stjr if (wc == '\n') { 605132083Stjr if (col + 1 >= termwidth) 606132083Stjr fprintf(outfile, "\\\n"); 607132083Stjr fputc('$', outfile); 608132083Stjr fputc('\n', outfile); 609132083Stjr col = 0; 610132083Stjr } else if (iswprint(wc)) { 611132083Stjr width = wcwidth(wc); 612132083Stjr if (col + width >= termwidth) { 613132083Stjr fprintf(outfile, "\\\n"); 614132083Stjr col = 0; 615132083Stjr } 616132083Stjr fwrite(s, 1, clen, outfile); 617132083Stjr col += width; 618132083Stjr } else if (wc != L'\0' && (c = wctob(wc)) != EOF && 619132083Stjr (p = strchr(escapes, c)) != NULL) { 620132083Stjr if (col + 2 >= termwidth) { 621132083Stjr fprintf(outfile, "\\\n"); 622132083Stjr col = 0; 623132083Stjr } 624132083Stjr fprintf(outfile, "\\%c", "\\abfrtv"[p - escapes]); 625132083Stjr col += 2; 62698603Stjr } else { 627176126Sdwmalone if (col + 4 * clen >= (unsigned)termwidth) { 628132083Stjr fprintf(outfile, "\\\n"); 629132083Stjr col = 0; 6301590Srgrimes } 631132083Stjr for (i = 0; i < clen; i++) 632132083Stjr fprintf(outfile, "\\%03o", 633132083Stjr (int)(unsigned char)s[i]); 634132083Stjr col += 4 * clen; 6351590Srgrimes } 636132083Stjr s += clen; 637132083Stjr len -= clen; 6381590Srgrimes } 639132083Stjr if (col + 1 >= termwidth) 640132083Stjr fprintf(outfile, "\\\n"); 641122049Sdes (void)fputc('$', outfile); 642122049Sdes (void)fputc('\n', outfile); 643122049Sdes if (ferror(outfile)) 644122049Sdes errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO)); 6451590Srgrimes} 6461590Srgrimes 647100359Smarkmstatic __inline int 648122044Sdesregexec_e(regex_t *preg, const char *string, int eflags, int nomatch, 649122044Sdes size_t slen) 6501590Srgrimes{ 6511590Srgrimes int eval; 6528874Srgrimes 6531590Srgrimes if (preg == NULL) { 6541590Srgrimes if (defpreg == NULL) 65528066Scharnier errx(1, "first RE may not be empty"); 6561590Srgrimes } else 6571590Srgrimes defpreg = preg; 6581590Srgrimes 65998601Stjr /* Set anchors */ 6601590Srgrimes match[0].rm_so = 0; 6611590Srgrimes match[0].rm_eo = slen; 6628874Srgrimes 6631590Srgrimes eval = regexec(defpreg, string, 6641590Srgrimes nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND); 6651590Srgrimes switch(eval) { 6661590Srgrimes case 0: 6671590Srgrimes return (1); 6681590Srgrimes case REG_NOMATCH: 6691590Srgrimes return (0); 6701590Srgrimes } 67128066Scharnier errx(1, "RE error: %s", strregerror(eval, defpreg)); 6721590Srgrimes /* NOTREACHED */ 6731590Srgrimes} 6741590Srgrimes 6751590Srgrimes/* 6761590Srgrimes * regsub - perform substitutions after a regexp match 6771590Srgrimes * Based on a routine by Henry Spencer 6781590Srgrimes */ 6791590Srgrimesstatic void 680122044Sdesregsub(SPACE *sp, char *string, char *src) 6811590Srgrimes{ 68287766Smarkm int len, no; 68387766Smarkm char c, *dst; 6841590Srgrimes 6851590Srgrimes#define NEEDSP(reqlen) \ 686121915Stjr /* XXX What is the +1 for? */ \ 687121915Stjr if (sp->len + (reqlen) + 1 >= sp->blen) { \ 6881590Srgrimes sp->blen += (reqlen) + 1024; \ 68980286Sobrien if ((sp->space = sp->back = realloc(sp->back, sp->blen)) \ 69080286Sobrien == NULL) \ 69180286Sobrien err(1, "realloc"); \ 6921590Srgrimes dst = sp->space + sp->len; \ 6931590Srgrimes } 6941590Srgrimes 6951590Srgrimes dst = sp->space + sp->len; 6961590Srgrimes while ((c = *src++) != '\0') { 6971590Srgrimes if (c == '&') 6981590Srgrimes no = 0; 69917522Sache else if (c == '\\' && isdigit((unsigned char)*src)) 7001590Srgrimes no = *src++ - '0'; 7011590Srgrimes else 7021590Srgrimes no = -1; 7031590Srgrimes if (no < 0) { /* Ordinary character. */ 704122045Sdes if (c == '\\' && (*src == '\\' || *src == '&')) 705122045Sdes c = *src++; 7061590Srgrimes NEEDSP(1); 707122045Sdes *dst++ = c; 7081590Srgrimes ++sp->len; 709122045Sdes } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) { 7101590Srgrimes len = match[no].rm_eo - match[no].rm_so; 7111590Srgrimes NEEDSP(len); 7121590Srgrimes memmove(dst, string + match[no].rm_so, len); 7131590Srgrimes dst += len; 7141590Srgrimes sp->len += len; 7151590Srgrimes } 7161590Srgrimes } 7171590Srgrimes NEEDSP(1); 7181590Srgrimes *dst = '\0'; 7191590Srgrimes} 7201590Srgrimes 7211590Srgrimes/* 722168120Syar * cspace -- 723168120Syar * Concatenate space: append the source space to the destination space, 724168120Syar * allocating new space as necessary. 7251590Srgrimes */ 7261590Srgrimesvoid 727122044Sdescspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag) 7281590Srgrimes{ 7291590Srgrimes size_t tlen; 7301590Srgrimes 7311590Srgrimes /* Make sure SPACE has enough memory and ramp up quickly. */ 7321590Srgrimes tlen = sp->len + len + 1; 7331590Srgrimes if (tlen > sp->blen) { 7341590Srgrimes sp->blen = tlen + 1024; 73580286Sobrien if ((sp->space = sp->back = realloc(sp->back, sp->blen)) == 73680286Sobrien NULL) 73780286Sobrien err(1, "realloc"); 7381590Srgrimes } 7391590Srgrimes 7401590Srgrimes if (spflag == REPLACE) 7411590Srgrimes sp->len = 0; 7421590Srgrimes 7431590Srgrimes memmove(sp->space + sp->len, p, len); 7441590Srgrimes 7451590Srgrimes sp->space[sp->len += len] = '\0'; 7461590Srgrimes} 7471590Srgrimes 7481590Srgrimes/* 7491590Srgrimes * Close all cached opened files and report any errors 7501590Srgrimes */ 7511590Srgrimesvoid 752122044Sdescfclose(struct s_command *cp, struct s_command *end) 7531590Srgrimes{ 7541590Srgrimes 7551590Srgrimes for (; cp != end; cp = cp->next) 7561590Srgrimes switch(cp->code) { 7571590Srgrimes case 's': 7581590Srgrimes if (cp->u.s->wfd != -1 && close(cp->u.s->wfd)) 75928066Scharnier err(1, "%s", cp->u.s->wfile); 7601590Srgrimes cp->u.s->wfd = -1; 7611590Srgrimes break; 7621590Srgrimes case 'w': 7631590Srgrimes if (cp->u.fd != -1 && close(cp->u.fd)) 76428066Scharnier err(1, "%s", cp->t); 7651590Srgrimes cp->u.fd = -1; 7661590Srgrimes break; 7671590Srgrimes case '{': 7681590Srgrimes cfclose(cp->u.c, cp->next); 7691590Srgrimes break; 7701590Srgrimes } 7711590Srgrimes} 772