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

Annotation of src/usr.bin/fmt/fmt.c, Revision 1.1.1.1

1.1       deraadt     1: /*     $NetBSD: fmt.c,v 1.4 1995/09/01 01:29:41 jtc Exp $      */
                      2:
                      3: /*
                      4:  * Copyright (c) 1980, 1993
                      5:  *     The Regents of the University of California.  All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
                     15:  * 3. All advertising materials mentioning features or use of this software
                     16:  *    must display the following acknowledgement:
                     17:  *     This product includes software developed by the University of
                     18:  *     California, Berkeley and its contributors.
                     19:  * 4. 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:
                     36: #ifndef lint
                     37: static char copyright[] =
                     38: "@(#) Copyright (c) 1980, 1993\n\
                     39:        The Regents of the University of California.  All rights reserved.\n";
                     40: #endif /* not lint */
                     41:
                     42: #ifndef lint
                     43: #if 0
                     44: static char sccsid[] = "@(#)fmt.c      8.1 (Berkeley) 7/20/93";
                     45: #endif
                     46: static char rcsid[] = "$NetBSD: fmt.c,v 1.4 1995/09/01 01:29:41 jtc Exp $";
                     47: #endif /* not lint */
                     48:
                     49: #include <stdio.h>
                     50: #include <stdlib.h>
                     51: #include <string.h>
                     52: #include <ctype.h>
                     53:
                     54: /*
                     55:  * fmt -- format the concatenation of input files or standard input
                     56:  * onto standard output.  Designed for use with Mail ~|
                     57:  *
                     58:  * Syntax : fmt [ goal [ max ] ] [ name ... ]
                     59:  * Authors: Kurt Shoens (UCB) 12/7/78;
                     60:  *          Liz Allen (UMCP) 2/24/83 [Addition of goal length concept].
                     61:  */
                     62:
                     63: /* LIZ@UOM 6/18/85 -- Don't need LENGTH any more.
                     64:  * #define     LENGTH  72              Max line length in output
                     65:  */
                     66: #define        NOSTR   ((char *) 0)    /* Null string pointer for lint */
                     67:
                     68: /* LIZ@UOM 6/18/85 --New variables goal_length and max_length */
                     69: #define GOAL_LENGTH 65
                     70: #define MAX_LENGTH 75
                     71: int    goal_length;            /* Target or goal line length in output */
                     72: int    max_length;             /* Max line length in output */
                     73: int    pfx;                    /* Current leading blank count */
                     74: int    lineno;                 /* Current input line */
                     75: int    mark;                   /* Last place we saw a head line */
                     76:
                     77: char   *headnames[] = {"To", "Subject", "Cc", 0};
                     78:
                     79: /*
                     80:  * Drive the whole formatter by managing input files.  Also,
                     81:  * cause initialization of the output stuff and flush it out
                     82:  * at the end.
                     83:  */
                     84:
                     85: main(argc, argv)
                     86:        int argc;
                     87:        char **argv;
                     88: {
                     89:        register FILE *fi;
                     90:        register int errs = 0;
                     91:        int number;             /* LIZ@UOM 6/18/85 */
                     92:
                     93:        goal_length = GOAL_LENGTH;
                     94:        max_length = MAX_LENGTH;
                     95:        setout();
                     96:        lineno = 1;
                     97:        mark = -10;
                     98:        /*
                     99:         * LIZ@UOM 6/18/85 -- Check for goal and max length arguments
                    100:         */
                    101:        if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) {
                    102:                argv++;
                    103:                argc--;
                    104:                goal_length = number;
                    105:                if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) {
                    106:                        argv++;
                    107:                        argc--;
                    108:                        max_length = number;
                    109:                }
                    110:        }
                    111:        if (max_length <= goal_length) {
                    112:                fprintf(stderr, "Max length must be greater than %s\n",
                    113:                        "goal length");
                    114:                exit(1);
                    115:        }
                    116:        if (argc < 2) {
                    117:                fmt(stdin);
                    118:                oflush();
                    119:                exit(0);
                    120:        }
                    121:        while (--argc) {
                    122:                if ((fi = fopen(*++argv, "r")) == NULL) {
                    123:                        perror(*argv);
                    124:                        errs++;
                    125:                        continue;
                    126:                }
                    127:                fmt(fi);
                    128:                fclose(fi);
                    129:        }
                    130:        oflush();
                    131:        exit(errs);
                    132: }
                    133:
                    134: /*
                    135:  * Read up characters from the passed input file, forming lines,
                    136:  * doing ^H processing, expanding tabs, stripping trailing blanks,
                    137:  * and sending each line down for analysis.
                    138:  */
                    139: fmt(fi)
                    140:        FILE *fi;
                    141: {
                    142:        char linebuf[BUFSIZ], canonb[BUFSIZ];
                    143:        register char *cp, *cp2;
                    144:        register int c, col;
                    145:
                    146:        c = getc(fi);
                    147:        while (c != EOF) {
                    148:                /*
                    149:                 * Collect a line, doing ^H processing.
                    150:                 * Leave tabs for now.
                    151:                 */
                    152:                cp = linebuf;
                    153:                while (c != '\n' && c != EOF && cp-linebuf < BUFSIZ-1) {
                    154:                        if (c == '\b') {
                    155:                                if (cp > linebuf)
                    156:                                        cp--;
                    157:                                c = getc(fi);
                    158:                                continue;
                    159:                        }
                    160:                        if ((c < ' ' || c >= 0177) && c != '\t') {
                    161:                                c = getc(fi);
                    162:                                continue;
                    163:                        }
                    164:                        *cp++ = c;
                    165:                        c = getc(fi);
                    166:                }
                    167:                *cp = '\0';
                    168:
                    169:                /*
                    170:                 * Toss anything remaining on the input line.
                    171:                 */
                    172:                while (c != '\n' && c != EOF)
                    173:                        c = getc(fi);
                    174:
                    175:                /*
                    176:                 * Expand tabs on the way to canonb.
                    177:                 */
                    178:                col = 0;
                    179:                cp = linebuf;
                    180:                cp2 = canonb;
                    181:                while (c = *cp++) {
                    182:                        if (c != '\t') {
                    183:                                col++;
                    184:                                if (cp2-canonb < BUFSIZ-1)
                    185:                                        *cp2++ = c;
                    186:                                continue;
                    187:                        }
                    188:                        do {
                    189:                                if (cp2-canonb < BUFSIZ-1)
                    190:                                        *cp2++ = ' ';
                    191:                                col++;
                    192:                        } while ((col & 07) != 0);
                    193:                }
                    194:
                    195:                /*
                    196:                 * Swipe trailing blanks from the line.
                    197:                 */
                    198:                for (cp2--; cp2 >= canonb && *cp2 == ' '; cp2--)
                    199:                        ;
                    200:                *++cp2 = '\0';
                    201:                prefix(canonb);
                    202:                if (c != EOF)
                    203:                        c = getc(fi);
                    204:        }
                    205: }
                    206:
                    207: /*
                    208:  * Take a line devoid of tabs and other garbage and determine its
                    209:  * blank prefix.  If the indent changes, call for a linebreak.
                    210:  * If the input line is blank, echo the blank line on the output.
                    211:  * Finally, if the line minus the prefix is a mail header, try to keep
                    212:  * it on a line by itself.
                    213:  */
                    214: prefix(line)
                    215:        char line[];
                    216: {
                    217:        register char *cp, **hp;
                    218:        register int np, h;
                    219:
                    220:        if (strlen(line) == 0) {
                    221:                oflush();
                    222:                putchar('\n');
                    223:                return;
                    224:        }
                    225:        for (cp = line; *cp == ' '; cp++)
                    226:                ;
                    227:        np = cp - line;
                    228:
                    229:        /*
                    230:         * The following horrible expression attempts to avoid linebreaks
                    231:         * when the indent changes due to a paragraph.
                    232:         */
                    233:        if (np != pfx && (np > pfx || abs(pfx-np) > 8))
                    234:                oflush();
                    235:        if (h = ishead(cp))
                    236:                oflush(), mark = lineno;
                    237:        if (lineno - mark < 3 && lineno - mark > 0)
                    238:                for (hp = &headnames[0]; *hp != (char *) 0; hp++)
                    239:                        if (ispref(*hp, cp)) {
                    240:                                h = 1;
                    241:                                oflush();
                    242:                                break;
                    243:                        }
                    244:        if (!h && (h = (*cp == '.')))
                    245:                oflush();
                    246:        pfx = np;
                    247:        if (h)
                    248:                pack(cp, strlen(cp));
                    249:        else    split(cp);
                    250:        if (h)
                    251:                oflush();
                    252:        lineno++;
                    253: }
                    254:
                    255: /*
                    256:  * Split up the passed line into output "words" which are
                    257:  * maximal strings of non-blanks with the blank separation
                    258:  * attached at the end.  Pass these words along to the output
                    259:  * line packer.
                    260:  */
                    261: split(line)
                    262:        char line[];
                    263: {
                    264:        register char *cp, *cp2;
                    265:        char word[BUFSIZ];
                    266:        int wordl;              /* LIZ@UOM 6/18/85 */
                    267:
                    268:        cp = line;
                    269:        while (*cp) {
                    270:                cp2 = word;
                    271:                wordl = 0;      /* LIZ@UOM 6/18/85 */
                    272:
                    273:                /*
                    274:                 * Collect a 'word,' allowing it to contain escaped white
                    275:                 * space.
                    276:                 */
                    277:                while (*cp && *cp != ' ') {
                    278:                        if (*cp == '\\' && isspace(cp[1]))
                    279:                                *cp2++ = *cp++;
                    280:                        *cp2++ = *cp++;
                    281:                        wordl++;/* LIZ@UOM 6/18/85 */
                    282:                }
                    283:
                    284:                /*
                    285:                 * Guarantee a space at end of line. Two spaces after end of
                    286:                 * sentence punctuation.
                    287:                 */
                    288:                if (*cp == '\0') {
                    289:                        *cp2++ = ' ';
                    290:                        if (index(".:!", cp[-1]))
                    291:                                *cp2++ = ' ';
                    292:                }
                    293:                while (*cp == ' ')
                    294:                        *cp2++ = *cp++;
                    295:                *cp2 = '\0';
                    296:                /*
                    297:                 * LIZ@UOM 6/18/85 pack(word);
                    298:                 */
                    299:                pack(word, wordl);
                    300:        }
                    301: }
                    302:
                    303: /*
                    304:  * Output section.
                    305:  * Build up line images from the words passed in.  Prefix
                    306:  * each line with correct number of blanks.  The buffer "outbuf"
                    307:  * contains the current partial line image, including prefixed blanks.
                    308:  * "outp" points to the next available space therein.  When outp is NOSTR,
                    309:  * there ain't nothing in there yet.  At the bottom of this whole mess,
                    310:  * leading tabs are reinserted.
                    311:  */
                    312: char   outbuf[BUFSIZ];                 /* Sandbagged output line image */
                    313: char   *outp;                          /* Pointer in above */
                    314:
                    315: /*
                    316:  * Initialize the output section.
                    317:  */
                    318: setout()
                    319: {
                    320:        outp = NOSTR;
                    321: }
                    322:
                    323: /*
                    324:  * Pack a word onto the output line.  If this is the beginning of
                    325:  * the line, push on the appropriately-sized string of blanks first.
                    326:  * If the word won't fit on the current line, flush and begin a new
                    327:  * line.  If the word is too long to fit all by itself on a line,
                    328:  * just give it its own and hope for the best.
                    329:  *
                    330:  * LIZ@UOM 6/18/85 -- If the new word will fit in at less than the
                    331:  *     goal length, take it.  If not, then check to see if the line
                    332:  *     will be over the max length; if so put the word on the next
                    333:  *     line.  If not, check to see if the line will be closer to the
                    334:  *     goal length with or without the word and take it or put it on
                    335:  *     the next line accordingly.
                    336:  */
                    337:
                    338: /*
                    339:  * LIZ@UOM 6/18/85 -- pass in the length of the word as well
                    340:  * pack(word)
                    341:  *     char word[];
                    342:  */
                    343: pack(word,wl)
                    344:        char word[];
                    345:        int wl;
                    346: {
                    347:        register char *cp;
                    348:        register int s, t;
                    349:
                    350:        if (outp == NOSTR)
                    351:                leadin();
                    352:        /*
                    353:         * LIZ@UOM 6/18/85 -- change condition to check goal_length; s is the
                    354:         * length of the line before the word is added; t is now the length
                    355:         * of the line after the word is added
                    356:         *      t = strlen(word);
                    357:         *      if (t+s <= LENGTH)
                    358:         */
                    359:        s = outp - outbuf;
                    360:        t = wl + s;
                    361:        if ((t <= goal_length) ||
                    362:            ((t <= max_length) && (t - goal_length <= goal_length - s))) {
                    363:                /*
                    364:                 * In like flint!
                    365:                 */
                    366:                for (cp = word; *cp; *outp++ = *cp++);
                    367:                return;
                    368:        }
                    369:        if (s > pfx) {
                    370:                oflush();
                    371:                leadin();
                    372:        }
                    373:        for (cp = word; *cp; *outp++ = *cp++);
                    374: }
                    375:
                    376: /*
                    377:  * If there is anything on the current output line, send it on
                    378:  * its way.  Set outp to NOSTR to indicate the absence of the current
                    379:  * line prefix.
                    380:  */
                    381: oflush()
                    382: {
                    383:        if (outp == NOSTR)
                    384:                return;
                    385:        *outp = '\0';
                    386:        tabulate(outbuf);
                    387:        outp = NOSTR;
                    388: }
                    389:
                    390: /*
                    391:  * Take the passed line buffer, insert leading tabs where possible, and
                    392:  * output on standard output (finally).
                    393:  */
                    394: tabulate(line)
                    395:        char line[];
                    396: {
                    397:        register char *cp;
                    398:        register int b, t;
                    399:
                    400:        /*
                    401:         * Toss trailing blanks in the output line.
                    402:         */
                    403:        cp = line + strlen(line) - 1;
                    404:        while (cp >= line && *cp == ' ')
                    405:                cp--;
                    406:        *++cp = '\0';
                    407:
                    408:        /*
                    409:         * Count the leading blank space and tabulate.
                    410:         */
                    411:        for (cp = line; *cp == ' '; cp++)
                    412:                ;
                    413:        b = cp-line;
                    414:        t = b >> 3;
                    415:        b &= 07;
                    416:        if (t > 0)
                    417:                do
                    418:                        putc('\t', stdout);
                    419:                while (--t);
                    420:        if (b > 0)
                    421:                do
                    422:                        putc(' ', stdout);
                    423:                while (--b);
                    424:        while (*cp)
                    425:                putc(*cp++, stdout);
                    426:        putc('\n', stdout);
                    427: }
                    428:
                    429: /*
                    430:  * Initialize the output line with the appropriate number of
                    431:  * leading blanks.
                    432:  */
                    433: leadin()
                    434: {
                    435:        register int b;
                    436:        register char *cp;
                    437:
                    438:        for (b = 0, cp = outbuf; b < pfx; b++)
                    439:                *cp++ = ' ';
                    440:        outp = cp;
                    441: }
                    442:
                    443: /*
                    444:  * Save a string in dynamic space.
                    445:  * This little goodie is needed for
                    446:  * a headline detector in head.c
                    447:  */
                    448: char *
                    449: savestr(str)
                    450:        char str[];
                    451: {
                    452:        register char *top;
                    453:
                    454:        top = malloc(strlen(str) + 1);
                    455:        if (top == NOSTR) {
                    456:                fprintf(stderr, "fmt:  Ran out of memory\n");
                    457:                exit(1);
                    458:        }
                    459:        strcpy(top, str);
                    460:        return (top);
                    461: }
                    462:
                    463: /*
                    464:  * Is s1 a prefix of s2??
                    465:  */
                    466: ispref(s1, s2)
                    467:        register char *s1, *s2;
                    468: {
                    469:
                    470:        while (*s1++ == *s2)
                    471:                ;
                    472:        return (*s1 == '\0');
                    473: }