[BACK]Return to process.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.bin / sed

Annotation of src/usr.bin/sed/process.c, Revision 1.1.1.2

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

CVSweb <webmaster@jp.NetBSD.org>