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