[BACK]Return to nl.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / nl

Annotation of src/usr.bin/nl/nl.c, Revision 1.1

1.1     ! jca         1: /*     $OpenBSD$ */
        !             2: /*     $NetBSD: nl.c,v 1.11 2011/08/16 12:00:46 christos Exp $ */
        !             3:
        !             4: /*-
        !             5:  * Copyright (c) 1999 The NetBSD Foundation, Inc.
        !             6:  * All rights reserved.
        !             7:  *
        !             8:  * This code is derived from software contributed to The NetBSD Foundation
        !             9:  * by Klaus Klein.
        !            10:  *
        !            11:  * Redistribution and use in source and binary forms, with or without
        !            12:  * modification, are permitted provided that the following conditions
        !            13:  * are met:
        !            14:  * 1. Redistributions of source code must retain the above copyright
        !            15:  *    notice, this list of conditions and the following disclaimer.
        !            16:  * 2. Redistributions in binary form must reproduce the above copyright
        !            17:  *    notice, this list of conditions and the following disclaimer in the
        !            18:  *    documentation and/or other materials provided with the distribution.
        !            19:  *
        !            20:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
        !            21:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
        !            22:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        !            23:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
        !            24:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
        !            25:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
        !            26:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
        !            27:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
        !            28:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
        !            29:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        !            30:  * POSSIBILITY OF SUCH DAMAGE.
        !            31:  */
        !            32:
        !            33: #include <sys/cdefs.h>
        !            34:
        !            35: #include <err.h>
        !            36: #include <errno.h>
        !            37: #include <limits.h>
        !            38: #include <locale.h>
        !            39: #include <regex.h>
        !            40: #include <stdio.h>
        !            41: #include <stdlib.h>
        !            42: #include <string.h>
        !            43: #include <wchar.h>
        !            44:
        !            45: typedef enum {
        !            46:        number_all,             /* number all lines */
        !            47:        number_nonempty,        /* number non-empty lines */
        !            48:        number_none,            /* no line numbering */
        !            49:        number_regex            /* number lines matching regular expression */
        !            50: } numbering_type;
        !            51:
        !            52: struct numbering_property {
        !            53:        const char * const      name;           /* for diagnostics */
        !            54:        numbering_type          type;           /* numbering type */
        !            55:        regex_t                 expr;           /* for type == number_regex */
        !            56: };
        !            57:
        !            58: /* line numbering formats */
        !            59: #define FORMAT_LN      "%-*d"  /* left justified, leading zeros suppressed */
        !            60: #define FORMAT_RN      "%*d"   /* right justified, leading zeros suppressed */
        !            61: #define FORMAT_RZ      "%0*d"  /* right justified, leading zeros kept */
        !            62:
        !            63: #define FOOTER         0
        !            64: #define BODY           1
        !            65: #define HEADER         2
        !            66: #define NP_LAST                HEADER
        !            67:
        !            68: static struct numbering_property numbering_properties[NP_LAST + 1] = {
        !            69:        { "footer",     number_none,    { 0, 0, 0, 0 } },
        !            70:        { "body",       number_nonempty, { 0, 0, 0, 0 } },
        !            71:        { "header",     number_none,    { 0, 0, 0, 0 } },
        !            72: };
        !            73:
        !            74: void           filter(void);
        !            75: void           parse_numbering(const char *, int);
        !            76: __dead void    usage(void);
        !            77:
        !            78: /*
        !            79:  * Delimiter characters that indicate the start of a logical page section.
        !            80:  */
        !            81: static char delim[2 * MB_LEN_MAX];
        !            82: static int delimlen;
        !            83:
        !            84: /*
        !            85:  * Configurable parameters.
        !            86:  */
        !            87:
        !            88: /* line numbering format */
        !            89: static const char *format = FORMAT_RN;
        !            90:
        !            91: /* increment value used to number logical page lines */
        !            92: static int incr = 1;
        !            93:
        !            94: /* number of adjacent blank lines to be considered (and numbered) as one */
        !            95: static unsigned int nblank = 1;
        !            96:
        !            97: /* whether to restart numbering at logical page delimiters */
        !            98: static int restart = 1;
        !            99:
        !           100: /* characters used in separating the line number and the corrsp. text line */
        !           101: static const char *sep = "\t";
        !           102:
        !           103: /* initial value used to number logical page lines */
        !           104: static int startnum = 1;
        !           105:
        !           106: /* number of characters to be used for the line number */
        !           107: /* should be unsigned but required signed by `*' precision conversion */
        !           108: static int width = 6;
        !           109:
        !           110:
        !           111: int
        !           112: main(int argc, char *argv[])
        !           113: {
        !           114:        int c;
        !           115:        size_t clen;
        !           116:        char delim1[MB_LEN_MAX] = { '\\' }, delim2[MB_LEN_MAX] = { ':' };
        !           117:        size_t delim1len = 1, delim2len = 1;
        !           118:        const char *errstr;
        !           119:
        !           120:        (void)setlocale(LC_ALL, "");
        !           121:
        !           122:        while ((c = getopt(argc, argv, "pb:d:f:h:i:l:n:s:v:w:")) != -1) {
        !           123:                switch (c) {
        !           124:                case 'p':
        !           125:                        restart = 0;
        !           126:                        break;
        !           127:                case 'b':
        !           128:                        parse_numbering(optarg, BODY);
        !           129:                        break;
        !           130:                case 'd':
        !           131:                        clen = mbrlen(optarg, MB_CUR_MAX, NULL);
        !           132:                        if (clen == (size_t)-1 || clen == (size_t)-2) {
        !           133:                                errno = EILSEQ;
        !           134:                                err(EXIT_FAILURE, NULL);
        !           135:                        }
        !           136:                        if (clen != 0) {
        !           137:                                memcpy(delim1, optarg, delim1len = clen);
        !           138:                                clen = mbrlen(optarg + delim1len,
        !           139:                                    MB_CUR_MAX, NULL);
        !           140:                                if (clen == (size_t)-1 || clen == (size_t)-2) {
        !           141:                                        errno = EILSEQ;
        !           142:                                        err(EXIT_FAILURE, NULL);
        !           143:                                }
        !           144:                                if (clen != 0) {
        !           145:                                        memcpy(delim2, optarg + delim1len,
        !           146:                                            delim2len = clen);
        !           147:                                        if (optarg[delim1len + clen] != '\0') {
        !           148:                                                errx(EXIT_FAILURE,
        !           149:                                                    "invalid delimiter: %s",
        !           150:                                                    optarg);
        !           151:                                        }
        !           152:                                }
        !           153:                        }
        !           154:                        break;
        !           155:                case 'f':
        !           156:                        parse_numbering(optarg, FOOTER);
        !           157:                        break;
        !           158:                case 'h':
        !           159:                        parse_numbering(optarg, HEADER);
        !           160:                        break;
        !           161:                case 'i':
        !           162:                        incr = strtonum(optarg, INT_MIN, INT_MAX, &errstr);
        !           163:                        if (errstr)
        !           164:                                errx(EXIT_FAILURE, "increment value is %s: %s",
        !           165:                                    errstr, optarg);
        !           166:                        break;
        !           167:                case 'l':
        !           168:                        nblank = strtonum(optarg, 0, UINT_MAX, &errstr);
        !           169:                        if (errstr)
        !           170:                                errx(EXIT_FAILURE,
        !           171:                                    "blank line value is %s: %s",
        !           172:                                    errstr, optarg);
        !           173:                        break;
        !           174:                case 'n':
        !           175:                        if (strcmp(optarg, "ln") == 0) {
        !           176:                                format = FORMAT_LN;
        !           177:                        } else if (strcmp(optarg, "rn") == 0) {
        !           178:                                format = FORMAT_RN;
        !           179:                        } else if (strcmp(optarg, "rz") == 0) {
        !           180:                                format = FORMAT_RZ;
        !           181:                        } else
        !           182:                                errx(EXIT_FAILURE,
        !           183:                                    "illegal format -- %s", optarg);
        !           184:                        break;
        !           185:                case 's':
        !           186:                        sep = optarg;
        !           187:                        break;
        !           188:                case 'v':
        !           189:                        startnum = strtonum(optarg, INT_MIN, INT_MAX, &errstr);
        !           190:                        if (errstr)
        !           191:                                errx(EXIT_FAILURE,
        !           192:                                    "initial logical page value is %s: %s",
        !           193:                                    errstr, optarg);
        !           194:                        break;
        !           195:                case 'w':
        !           196:                        width = strtonum(optarg, 1, INT_MAX, &errstr);
        !           197:                        if (errstr)
        !           198:                                errx(EXIT_FAILURE, "width is %s: %s", errstr,
        !           199:                                    optarg);
        !           200:                        break;
        !           201:                case '?':
        !           202:                default:
        !           203:                        usage();
        !           204:                        /* NOTREACHED */
        !           205:                }
        !           206:        }
        !           207:        argc -= optind;
        !           208:        argv += optind;
        !           209:
        !           210:        switch (argc) {
        !           211:        case 0:
        !           212:                break;
        !           213:        case 1:
        !           214:                if (strcmp(argv[0], "-") != 0 &&
        !           215:                    freopen(argv[0], "r", stdin) == NULL)
        !           216:                        err(EXIT_FAILURE, "%s", argv[0]);
        !           217:                break;
        !           218:        default:
        !           219:                usage();
        !           220:                /* NOTREACHED */
        !           221:        }
        !           222:
        !           223:        /* Generate the delimiter sequence */
        !           224:        memcpy(delim, delim1, delim1len);
        !           225:        memcpy(delim + delim1len, delim2, delim2len);
        !           226:        delimlen = delim1len + delim2len;
        !           227:
        !           228:        /* Do the work. */
        !           229:        filter();
        !           230:
        !           231:        exit(EXIT_SUCCESS);
        !           232: }
        !           233:
        !           234: void
        !           235: filter(void)
        !           236: {
        !           237:        char *buffer;
        !           238:        size_t buffersize;
        !           239:        ssize_t linelen;
        !           240:        int line;               /* logical line number */
        !           241:        int section;            /* logical page section */
        !           242:        unsigned int adjblank;  /* adjacent blank lines */
        !           243:        int donumber = 0, idx;
        !           244:
        !           245:        adjblank = 0;
        !           246:        line = startnum;
        !           247:        section = BODY;
        !           248:
        !           249:        buffer = NULL;
        !           250:        buffersize = 0;
        !           251:        while ((linelen = getline(&buffer, &buffersize, stdin)) > 0) {
        !           252:                for (idx = FOOTER; idx <= NP_LAST; idx++) {
        !           253:                        /* Does it look like a delimiter? */
        !           254:                        if (delimlen * (idx + 1) > linelen)
        !           255:                                break;
        !           256:                        if (memcmp(buffer + delimlen * idx, delim,
        !           257:                            delimlen) != 0)
        !           258:                                break;
        !           259:                        /* Was this the whole line? */
        !           260:                        if (buffer[delimlen * (idx + 1)] == '\n') {
        !           261:                                section = idx;
        !           262:                                adjblank = 0;
        !           263:                                if (restart)
        !           264:                                        line = startnum;
        !           265:                                goto nextline;
        !           266:                        }
        !           267:                }
        !           268:
        !           269:                switch (numbering_properties[section].type) {
        !           270:                case number_all:
        !           271:                        /*
        !           272:                         * Doing this for number_all only is disputable, but
        !           273:                         * the standard expresses an explicit dependency on
        !           274:                         * `-b a' etc.
        !           275:                         */
        !           276:                        if (buffer[0] == '\n' && ++adjblank < nblank)
        !           277:                                donumber = 0;
        !           278:                        else
        !           279:                                donumber = 1, adjblank = 0;
        !           280:                        break;
        !           281:                case number_nonempty:
        !           282:                        donumber = (buffer[0] != '\n');
        !           283:                        break;
        !           284:                case number_none:
        !           285:                        donumber = 0;
        !           286:                        break;
        !           287:                case number_regex:
        !           288:                        donumber =
        !           289:                            (regexec(&numbering_properties[section].expr,
        !           290:                            buffer, 0, NULL, 0) == 0);
        !           291:                        break;
        !           292:                }
        !           293:
        !           294:                if (donumber) {
        !           295:                        (void)printf(format, width, line);
        !           296:                        line += incr;
        !           297:                        (void)fputs(sep, stdout);
        !           298:                } else {
        !           299:                        (void)printf("%*s", width, "");
        !           300:                }
        !           301:                (void)fwrite(buffer, linelen, 1, stdout);
        !           302:
        !           303:                if (ferror(stdout))
        !           304:                        err(EXIT_FAILURE, "output error");
        !           305: nextline:
        !           306:                ;
        !           307:        }
        !           308:
        !           309:        if (ferror(stdin))
        !           310:                err(EXIT_FAILURE, "input error");
        !           311:
        !           312:        free(buffer);
        !           313: }
        !           314:
        !           315: /*
        !           316:  * Various support functions.
        !           317:  */
        !           318:
        !           319: void
        !           320: parse_numbering(const char *argstr, int section)
        !           321: {
        !           322:        int error;
        !           323:        char errorbuf[NL_TEXTMAX];
        !           324:
        !           325:        switch (argstr[0]) {
        !           326:        case 'a':
        !           327:                numbering_properties[section].type = number_all;
        !           328:                break;
        !           329:        case 'n':
        !           330:                numbering_properties[section].type = number_none;
        !           331:                break;
        !           332:        case 't':
        !           333:                numbering_properties[section].type = number_nonempty;
        !           334:                break;
        !           335:        case 'p':
        !           336:                /* If there was a previous expression, throw it away. */
        !           337:                if (numbering_properties[section].type == number_regex)
        !           338:                        regfree(&numbering_properties[section].expr);
        !           339:                else
        !           340:                        numbering_properties[section].type = number_regex;
        !           341:
        !           342:                /* Compile/validate the supplied regular expression. */
        !           343:                if ((error = regcomp(&numbering_properties[section].expr,
        !           344:                    &argstr[1], REG_NEWLINE|REG_NOSUB)) != 0) {
        !           345:                        (void)regerror(error,
        !           346:                            &numbering_properties[section].expr,
        !           347:                            errorbuf, sizeof(errorbuf));
        !           348:                        errx(EXIT_FAILURE,
        !           349:                            "%s expr: %s -- %s",
        !           350:                            numbering_properties[section].name, errorbuf,
        !           351:                            &argstr[1]);
        !           352:                }
        !           353:                break;
        !           354:        default:
        !           355:                errx(EXIT_FAILURE,
        !           356:                    "illegal %s line numbering type -- %s",
        !           357:                    numbering_properties[section].name, argstr);
        !           358:        }
        !           359: }
        !           360:
        !           361: __dead void
        !           362: usage(void)
        !           363: {
        !           364:        (void)fprintf(stderr, "usage: %s [-p] [-b type] [-d delim] [-f type] "
        !           365:            "[-h type] [-i incr] [-l num]\n\t[-n format] [-s sep] "
        !           366:            "[-v startnum] [-w width] [file]\n", getprogname());
        !           367:        exit(EXIT_FAILURE);
        !           368: }