[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.5

1.5     ! millert     1: /*     $OpenBSD: fmt.c,v 1.4 1997/01/26 03:56:52 millert Exp $ */
1.1       deraadt     2: /*     $NetBSD: fmt.c,v 1.4 1995/09/01 01:29:41 jtc Exp $      */
                      3:
                      4: /*
                      5:  * Copyright (c) 1980, 1993
                      6:  *     The Regents of the University of California.  All rights reserved.
                      7:  *
                      8:  * Redistribution and use in source and binary forms, with or without
                      9:  * modification, are permitted provided that the following conditions
                     10:  * are met:
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  * 2. Redistributions in binary form must reproduce the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer in the
                     15:  *    documentation and/or other materials provided with the distribution.
                     16:  * 3. All advertising materials mentioning features or use of this software
                     17:  *    must display the following acknowledgement:
                     18:  *     This product includes software developed by the University of
                     19:  *     California, Berkeley and its contributors.
                     20:  * 4. Neither the name of the University nor the names of its contributors
                     21:  *    may be used to endorse or promote products derived from this software
                     22:  *    without specific prior written permission.
                     23:  *
                     24:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     25:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     26:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     27:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     28:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     29:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     30:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     31:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     32:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     33:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     34:  * SUCH DAMAGE.
                     35:  */
                     36:
                     37: #ifndef lint
                     38: static char copyright[] =
                     39: "@(#) Copyright (c) 1980, 1993\n\
                     40:        The Regents of the University of California.  All rights reserved.\n";
                     41: #endif /* not lint */
                     42:
                     43: #ifndef lint
                     44: #if 0
                     45: static char sccsid[] = "@(#)fmt.c      8.1 (Berkeley) 7/20/93";
1.4       millert    46: #else
1.5     ! millert    47: static char rcsid[] = "$OpenBSD: fmt.c,v 1.4 1997/01/26 03:56:52 millert Exp $";
1.1       deraadt    48: #endif
                     49: #endif /* not lint */
                     50:
1.4       millert    51: #include <ctype.h>
                     52: #include <err.h>
                     53: #include <locale.h>
1.1       deraadt    54: #include <stdio.h>
                     55: #include <stdlib.h>
                     56: #include <string.h>
                     57:
1.5     ! millert    58: #ifdef __GNUC__
        !            59: #define inline __inline
        !            60: #else  /* !__GNUC__ */
        !            61: #define inline
        !            62: #endif /* !__GNUC__ */
        !            63:
1.1       deraadt    64: /*
                     65:  * fmt -- format the concatenation of input files or standard input
                     66:  * onto standard output.  Designed for use with Mail ~|
                     67:  *
                     68:  * Syntax : fmt [ goal [ max ] ] [ name ... ]
                     69:  * Authors: Kurt Shoens (UCB) 12/7/78;
                     70:  *          Liz Allen (UMCP) 2/24/83 [Addition of goal length concept].
                     71:  */
                     72:
                     73: /* LIZ@UOM 6/18/85 -- Don't need LENGTH any more.
                     74:  * #define     LENGTH  72              Max line length in output
                     75:  */
                     76: #define        NOSTR   ((char *) 0)    /* Null string pointer for lint */
                     77:
                     78: /* LIZ@UOM 6/18/85 --New variables goal_length and max_length */
                     79: #define GOAL_LENGTH 65
                     80: #define MAX_LENGTH 75
                     81: int    goal_length;            /* Target or goal line length in output */
                     82: int    max_length;             /* Max line length in output */
                     83: int    pfx;                    /* Current leading blank count */
                     84: int    lineno;                 /* Current input line */
                     85: int    mark;                   /* Last place we saw a head line */
1.4       millert    86: int    center;                 /* Did they ask to center lines? */
1.1       deraadt    87:
                     88: char   *headnames[] = {"To", "Subject", "Cc", 0};
                     89:
1.4       millert    90: void fmt __P((FILE *));
                     91: void setout __P((void));
                     92: void prefix __P((char *));
                     93: void split __P((char *));
                     94: void pack __P((char *, int));
                     95: void oflush __P((void));
                     96: void tabulate __P((char *));
                     97: void leadin __P((void));
                     98: char *savestr __P((char *));
1.5     ! millert    99: char *extstr __P((char *, int *, int));
1.4       millert   100: int  ispref __P((char *, char *));
                    101: int  ishead __P((char *));
                    102:
1.1       deraadt   103: /*
                    104:  * Drive the whole formatter by managing input files.  Also,
                    105:  * cause initialization of the output stuff and flush it out
                    106:  * at the end.
                    107:  */
                    108:
1.4       millert   109: int
1.1       deraadt   110: main(argc, argv)
                    111:        int argc;
                    112:        char **argv;
                    113: {
                    114:        register FILE *fi;
                    115:        register int errs = 0;
                    116:        int number;             /* LIZ@UOM 6/18/85 */
                    117:
1.4       millert   118:        (void) setlocale(LC_CTYPE, "");
                    119:
1.1       deraadt   120:        goal_length = GOAL_LENGTH;
                    121:        max_length = MAX_LENGTH;
                    122:        setout();
                    123:        lineno = 1;
                    124:        mark = -10;
                    125:        /*
1.4       millert   126:         * LIZ@UOM 6/18/85 -- Check for goal and max length arguments
1.1       deraadt   127:         */
1.4       millert   128:        if (argc > 1 && !strcmp(argv[1], "-c")) {
                    129:                center++;
                    130:                argc--;
                    131:                argv++;
                    132:        }
1.1       deraadt   133:        if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) {
                    134:                argv++;
                    135:                argc--;
                    136:                goal_length = number;
                    137:                if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) {
                    138:                        argv++;
                    139:                        argc--;
                    140:                        max_length = number;
                    141:                }
                    142:        }
1.4       millert   143:        if (max_length <= goal_length)
                    144:                errx(1, "Max length (%d) must be greater than goal length: %d",
                    145:                     max_length, goal_length);
1.1       deraadt   146:        if (argc < 2) {
                    147:                fmt(stdin);
                    148:                oflush();
                    149:                exit(0);
                    150:        }
                    151:        while (--argc) {
                    152:                if ((fi = fopen(*++argv, "r")) == NULL) {
                    153:                        perror(*argv);
                    154:                        errs++;
                    155:                        continue;
                    156:                }
                    157:                fmt(fi);
                    158:                fclose(fi);
                    159:        }
                    160:        oflush();
                    161:        exit(errs);
                    162: }
                    163:
                    164: /*
                    165:  * Read up characters from the passed input file, forming lines,
                    166:  * doing ^H processing, expanding tabs, stripping trailing blanks,
                    167:  * and sending each line down for analysis.
                    168:  */
1.4       millert   169: void
1.1       deraadt   170: fmt(fi)
                    171:        FILE *fi;
                    172: {
1.5     ! millert   173:        static char *linebuf, *canonb;
        !           174:        static int lbufsize, cbufsize;
1.4       millert   175:        register char *cp, *cp2, cc;
1.1       deraadt   176:        register int c, col;
1.4       millert   177: #define CHUNKSIZE 1024
1.1       deraadt   178:
1.4       millert   179:        if (center) {
1.5     ! millert   180:                register int len;
        !           181:
        !           182:                linebuf = extstr(linebuf, &lbufsize, NULL);
1.4       millert   183:                for (;;) {
1.5     ! millert   184:                        len = 0;
        !           185:                        for (;;) {
        !           186:                                if (!fgets(linebuf + len, lbufsize - len, fi))
        !           187:                                        break;
        !           188:                                len = strlen(linebuf);
        !           189:                                if (linebuf[len-1] == '\n' || feof(fi))
        !           190:                                        break;
        !           191:                                linebuf = extstr(linebuf, &lbufsize, NULL);
        !           192:                        }
        !           193:                        if (len == 0)
1.4       millert   194:                                return;
1.5     ! millert   195:                        cp = linebuf;
1.4       millert   196:                        while (*cp && isspace(*cp))
                    197:                                cp++;
                    198:                        cp2 = cp + strlen(cp) - 1;
                    199:                        while (cp2 > cp && isspace(*cp2))
                    200:                                cp2--;
                    201:                        if (cp == cp2)
                    202:                                putchar('\n');
                    203:                        col = cp2 - cp;
                    204:                        for (c = 0; c < (goal_length-col)/2; c++)
                    205:                                putchar(' ');
                    206:                        while (cp <= cp2)
                    207:                                putchar(*cp++);
                    208:                        putchar('\n');
                    209:                }
                    210:        }
1.1       deraadt   211:        c = getc(fi);
                    212:        while (c != EOF) {
                    213:                /*
                    214:                 * Collect a line, doing ^H processing.
                    215:                 * Leave tabs for now.
                    216:                 */
                    217:                cp = linebuf;
1.4       millert   218:                while (c != '\n' && c != EOF) {
                    219:                        if (cp - linebuf >= lbufsize) {
                    220:                                int offset = cp - linebuf;
1.5     ! millert   221:                                linebuf = extstr(linebuf, &lbufsize, NULL);
1.4       millert   222:                                cp = linebuf + offset;
                    223:                        }
1.1       deraadt   224:                        if (c == '\b') {
                    225:                                if (cp > linebuf)
                    226:                                        cp--;
                    227:                                c = getc(fi);
                    228:                                continue;
                    229:                        }
1.4       millert   230:                        if (!isprint(c) && c != '\t') {
1.1       deraadt   231:                                c = getc(fi);
                    232:                                continue;
                    233:                        }
                    234:                        *cp++ = c;
                    235:                        c = getc(fi);
                    236:                }
                    237:
                    238:                /*
                    239:                 * Toss anything remaining on the input line.
                    240:                 */
                    241:                while (c != '\n' && c != EOF)
                    242:                        c = getc(fi);
1.4       millert   243:
                    244:                if (cp != NULL) {
                    245:                        *cp = '\0';
                    246:                } else {
                    247:                        putchar('\n');
                    248:                        c = getc(fi);
                    249:                        continue;
                    250:                }
                    251:
1.1       deraadt   252:                /*
                    253:                 * Expand tabs on the way to canonb.
                    254:                 */
                    255:                col = 0;
                    256:                cp = linebuf;
                    257:                cp2 = canonb;
1.4       millert   258:                while ((cc = *cp++)) {
                    259:                        if (cc != '\t') {
1.1       deraadt   260:                                col++;
1.4       millert   261:                                if (cp2 - canonb >= cbufsize) {
                    262:                                        int offset = cp2 - canonb;
1.5     ! millert   263:                                        canonb = extstr(canonb, &cbufsize, NULL);
1.4       millert   264:                                        cp2 = canonb + offset;
                    265:                                }
                    266:                                *cp2++ = cc;
1.1       deraadt   267:                                continue;
                    268:                        }
                    269:                        do {
1.4       millert   270:                                if (cp2 - canonb >= cbufsize) {
                    271:                                        int offset = cp2 - canonb;
1.5     ! millert   272:                                        canonb = extstr(canonb, &cbufsize, NULL);
1.4       millert   273:                                        cp2 = canonb + offset;
                    274:                                }
                    275:                                *cp2++ = ' ';
1.1       deraadt   276:                                col++;
                    277:                        } while ((col & 07) != 0);
                    278:                }
                    279:
                    280:                /*
                    281:                 * Swipe trailing blanks from the line.
                    282:                 */
                    283:                for (cp2--; cp2 >= canonb && *cp2 == ' '; cp2--)
                    284:                        ;
                    285:                *++cp2 = '\0';
                    286:                prefix(canonb);
                    287:                if (c != EOF)
                    288:                        c = getc(fi);
                    289:        }
                    290: }
                    291:
                    292: /*
                    293:  * Take a line devoid of tabs and other garbage and determine its
                    294:  * blank prefix.  If the indent changes, call for a linebreak.
                    295:  * If the input line is blank, echo the blank line on the output.
                    296:  * Finally, if the line minus the prefix is a mail header, try to keep
                    297:  * it on a line by itself.
                    298:  */
1.4       millert   299: void
1.1       deraadt   300: prefix(line)
                    301:        char line[];
                    302: {
                    303:        register char *cp, **hp;
                    304:        register int np, h;
                    305:
1.4       millert   306:        if (*line == '\0') {
1.1       deraadt   307:                oflush();
                    308:                putchar('\n');
                    309:                return;
                    310:        }
                    311:        for (cp = line; *cp == ' '; cp++)
                    312:                ;
                    313:        np = cp - line;
                    314:
                    315:        /*
                    316:         * The following horrible expression attempts to avoid linebreaks
                    317:         * when the indent changes due to a paragraph.
                    318:         */
                    319:        if (np != pfx && (np > pfx || abs(pfx-np) > 8))
                    320:                oflush();
1.4       millert   321:        if ((h = ishead(cp)))
1.1       deraadt   322:                oflush(), mark = lineno;
                    323:        if (lineno - mark < 3 && lineno - mark > 0)
1.5     ! millert   324:                for (hp = &headnames[0]; *hp != NULL; hp++)
1.1       deraadt   325:                        if (ispref(*hp, cp)) {
                    326:                                h = 1;
                    327:                                oflush();
                    328:                                break;
                    329:                        }
                    330:        if (!h && (h = (*cp == '.')))
                    331:                oflush();
                    332:        pfx = np;
                    333:        if (h)
                    334:                pack(cp, strlen(cp));
1.5     ! millert   335:        else
        !           336:                split(cp);
1.1       deraadt   337:        if (h)
                    338:                oflush();
                    339:        lineno++;
                    340: }
                    341:
                    342: /*
                    343:  * Split up the passed line into output "words" which are
                    344:  * maximal strings of non-blanks with the blank separation
                    345:  * attached at the end.  Pass these words along to the output
                    346:  * line packer.
                    347:  */
1.4       millert   348: void
1.1       deraadt   349: split(line)
                    350:        char line[];
                    351: {
                    352:        register char *cp, *cp2;
1.5     ! millert   353:        static char *word;
        !           354:        static int wordsize;
1.1       deraadt   355:        int wordl;              /* LIZ@UOM 6/18/85 */
                    356:
1.5     ! millert   357:        if (strlen(line) >= wordsize)
        !           358:                word = extstr(word, &wordsize, strlen(line) + 1);
        !           359:
1.1       deraadt   360:        cp = line;
                    361:        while (*cp) {
                    362:                cp2 = word;
                    363:                wordl = 0;      /* LIZ@UOM 6/18/85 */
                    364:
                    365:                /*
                    366:                 * Collect a 'word,' allowing it to contain escaped white
1.4       millert   367:                 * space.
1.1       deraadt   368:                 */
                    369:                while (*cp && *cp != ' ') {
                    370:                        if (*cp == '\\' && isspace(cp[1]))
                    371:                                *cp2++ = *cp++;
                    372:                        *cp2++ = *cp++;
                    373:                        wordl++;/* LIZ@UOM 6/18/85 */
                    374:                }
                    375:
                    376:                /*
                    377:                 * Guarantee a space at end of line. Two spaces after end of
1.4       millert   378:                 * sentence punctuation.
1.1       deraadt   379:                 */
                    380:                if (*cp == '\0') {
                    381:                        *cp2++ = ' ';
1.3       millert   382:                        if (strchr(".:!", cp[-1]))
1.1       deraadt   383:                                *cp2++ = ' ';
                    384:                }
                    385:                while (*cp == ' ')
                    386:                        *cp2++ = *cp++;
                    387:                *cp2 = '\0';
                    388:                /*
1.4       millert   389:                 * LIZ@UOM 6/18/85 pack(word);
1.1       deraadt   390:                 */
                    391:                pack(word, wordl);
                    392:        }
                    393: }
                    394:
                    395: /*
                    396:  * Output section.
                    397:  * Build up line images from the words passed in.  Prefix
                    398:  * each line with correct number of blanks.  The buffer "outbuf"
                    399:  * contains the current partial line image, including prefixed blanks.
                    400:  * "outp" points to the next available space therein.  When outp is NOSTR,
                    401:  * there ain't nothing in there yet.  At the bottom of this whole mess,
                    402:  * leading tabs are reinserted.
                    403:  */
1.5     ! millert   404: static char *outbuf;                   /* Sandbagged output line image */
        !           405: static int   obufsize;                 /* Size of outbuf */
        !           406: static char *outp;                     /* Pointer in above */
1.1       deraadt   407:
                    408: /*
                    409:  * Initialize the output section.
                    410:  */
1.4       millert   411: void
1.1       deraadt   412: setout()
                    413: {
                    414:        outp = NOSTR;
                    415: }
                    416:
                    417: /*
                    418:  * Pack a word onto the output line.  If this is the beginning of
                    419:  * the line, push on the appropriately-sized string of blanks first.
                    420:  * If the word won't fit on the current line, flush and begin a new
                    421:  * line.  If the word is too long to fit all by itself on a line,
                    422:  * just give it its own and hope for the best.
                    423:  *
                    424:  * LIZ@UOM 6/18/85 -- If the new word will fit in at less than the
                    425:  *     goal length, take it.  If not, then check to see if the line
                    426:  *     will be over the max length; if so put the word on the next
                    427:  *     line.  If not, check to see if the line will be closer to the
                    428:  *     goal length with or without the word and take it or put it on
                    429:  *     the next line accordingly.
                    430:  */
                    431:
                    432: /*
                    433:  * LIZ@UOM 6/18/85 -- pass in the length of the word as well
                    434:  * pack(word)
                    435:  *     char word[];
                    436:  */
1.4       millert   437: void
                    438: pack(word, wl)
1.1       deraadt   439:        char word[];
                    440:        int wl;
                    441: {
                    442:        register char *cp;
                    443:        register int s, t;
                    444:
                    445:        if (outp == NOSTR)
                    446:                leadin();
                    447:        /*
                    448:         * LIZ@UOM 6/18/85 -- change condition to check goal_length; s is the
                    449:         * length of the line before the word is added; t is now the length
                    450:         * of the line after the word is added
                    451:         *      t = strlen(word);
1.4       millert   452:         *      if (t+s <= LENGTH)
1.1       deraadt   453:         */
                    454:        s = outp - outbuf;
                    455:        t = wl + s;
1.5     ! millert   456:        if (t + 1 > obufsize) {
        !           457:                outbuf = extstr(outbuf, &obufsize, t + 1);
        !           458:                outp = outbuf + s;
        !           459:        }
1.1       deraadt   460:        if ((t <= goal_length) ||
                    461:            ((t <= max_length) && (t - goal_length <= goal_length - s))) {
                    462:                /*
1.4       millert   463:                 * In like flint!
1.1       deraadt   464:                 */
1.5     ! millert   465:                for (cp = word; *cp; *outp++ = *cp++)
        !           466:                        ;
1.1       deraadt   467:                return;
                    468:        }
                    469:        if (s > pfx) {
                    470:                oflush();
                    471:                leadin();
                    472:        }
1.5     ! millert   473:        for (cp = word; *cp; *outp++ = *cp++)
        !           474:                ;
1.1       deraadt   475: }
                    476:
                    477: /*
                    478:  * If there is anything on the current output line, send it on
                    479:  * its way.  Set outp to NOSTR to indicate the absence of the current
                    480:  * line prefix.
                    481:  */
1.4       millert   482: void
1.1       deraadt   483: oflush()
                    484: {
                    485:        if (outp == NOSTR)
                    486:                return;
                    487:        *outp = '\0';
                    488:        tabulate(outbuf);
                    489:        outp = NOSTR;
                    490: }
                    491:
                    492: /*
                    493:  * Take the passed line buffer, insert leading tabs where possible, and
                    494:  * output on standard output (finally).
                    495:  */
1.4       millert   496: void
1.1       deraadt   497: tabulate(line)
                    498:        char line[];
                    499: {
                    500:        register char *cp;
                    501:        register int b, t;
                    502:
                    503:        /*
                    504:         * Toss trailing blanks in the output line.
                    505:         */
                    506:        cp = line + strlen(line) - 1;
                    507:        while (cp >= line && *cp == ' ')
                    508:                cp--;
                    509:        *++cp = '\0';
1.4       millert   510:
1.1       deraadt   511:        /*
                    512:         * Count the leading blank space and tabulate.
                    513:         */
                    514:        for (cp = line; *cp == ' '; cp++)
                    515:                ;
                    516:        b = cp-line;
                    517:        t = b >> 3;
                    518:        b &= 07;
                    519:        if (t > 0)
                    520:                do
                    521:                        putc('\t', stdout);
                    522:                while (--t);
                    523:        if (b > 0)
                    524:                do
                    525:                        putc(' ', stdout);
                    526:                while (--b);
                    527:        while (*cp)
                    528:                putc(*cp++, stdout);
                    529:        putc('\n', stdout);
                    530: }
                    531:
                    532: /*
                    533:  * Initialize the output line with the appropriate number of
                    534:  * leading blanks.
                    535:  */
1.4       millert   536: void
1.1       deraadt   537: leadin()
                    538: {
                    539:        register int b;
                    540:        register char *cp;
                    541:
1.5     ! millert   542:        if (obufsize == 0 || (outp != NULL && outp - outbuf <= pfx))
        !           543:                outbuf = extstr(outbuf, &obufsize, pfx);
1.1       deraadt   544:        for (b = 0, cp = outbuf; b < pfx; b++)
                    545:                *cp++ = ' ';
                    546:        outp = cp;
                    547: }
                    548:
                    549: /*
                    550:  * Save a string in dynamic space.
                    551:  * This little goodie is needed for
                    552:  * a headline detector in head.c
                    553:  */
                    554: char *
                    555: savestr(str)
                    556:        char str[];
                    557: {
1.4       millert   558:        char *top;
1.1       deraadt   559:
1.4       millert   560:        top = strdup(str);
                    561:        if (top == NOSTR)
                    562:                errx(1, "Ran out of memory");
1.1       deraadt   563:        return (top);
                    564: }
                    565:
                    566: /*
                    567:  * Is s1 a prefix of s2??
                    568:  */
1.4       millert   569: int
1.1       deraadt   570: ispref(s1, s2)
                    571:        register char *s1, *s2;
                    572: {
                    573:
                    574:        while (*s1++ == *s2)
                    575:                ;
                    576:        return (*s1 == '\0');
1.5     ! millert   577: }
        !           578:
        !           579: inline char *
        !           580: extstr(str, size, gsize)
        !           581:        char *str;
        !           582:        int *size;
        !           583:        int gsize;
        !           584: {
        !           585:        do {
        !           586:                *size += CHUNKSIZE;
        !           587:        } while (gsize && *size < gsize);
        !           588:
        !           589:        if ((str = realloc(str, *size)) == NULL)
        !           590:                errx(1, "Ran out of memory");
        !           591:
        !           592:        return(str);
1.1       deraadt   593: }