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

1.7     ! deraadt     1: /*     $OpenBSD: fmt.c,v 1.6 1997/02/08 10:23:39 deraadt 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.7     ! deraadt    47: static char rcsid[] = "$OpenBSD: fmt.c,v 1.6 1997/02/08 10:23:39 deraadt 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.6       deraadt    99: inline 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.7     ! deraadt   178:
        !           179:        canonb = malloc(CHUNKSIZE);
        !           180:        if (canonb == 0)
        !           181:                errx(1, "Ran out of memory");
1.1       deraadt   182:
1.4       millert   183:        if (center) {
1.5       millert   184:                register int len;
                    185:
                    186:                linebuf = extstr(linebuf, &lbufsize, NULL);
1.4       millert   187:                for (;;) {
1.5       millert   188:                        len = 0;
                    189:                        for (;;) {
                    190:                                if (!fgets(linebuf + len, lbufsize - len, fi))
                    191:                                        break;
                    192:                                len = strlen(linebuf);
                    193:                                if (linebuf[len-1] == '\n' || feof(fi))
                    194:                                        break;
                    195:                                linebuf = extstr(linebuf, &lbufsize, NULL);
                    196:                        }
                    197:                        if (len == 0)
1.4       millert   198:                                return;
1.5       millert   199:                        cp = linebuf;
1.4       millert   200:                        while (*cp && isspace(*cp))
                    201:                                cp++;
                    202:                        cp2 = cp + strlen(cp) - 1;
                    203:                        while (cp2 > cp && isspace(*cp2))
                    204:                                cp2--;
                    205:                        if (cp == cp2)
                    206:                                putchar('\n');
                    207:                        col = cp2 - cp;
                    208:                        for (c = 0; c < (goal_length-col)/2; c++)
                    209:                                putchar(' ');
                    210:                        while (cp <= cp2)
                    211:                                putchar(*cp++);
                    212:                        putchar('\n');
                    213:                }
                    214:        }
1.1       deraadt   215:        c = getc(fi);
                    216:        while (c != EOF) {
                    217:                /*
                    218:                 * Collect a line, doing ^H processing.
                    219:                 * Leave tabs for now.
                    220:                 */
                    221:                cp = linebuf;
1.4       millert   222:                while (c != '\n' && c != EOF) {
                    223:                        if (cp - linebuf >= lbufsize) {
                    224:                                int offset = cp - linebuf;
1.5       millert   225:                                linebuf = extstr(linebuf, &lbufsize, NULL);
1.4       millert   226:                                cp = linebuf + offset;
                    227:                        }
1.1       deraadt   228:                        if (c == '\b') {
                    229:                                if (cp > linebuf)
                    230:                                        cp--;
                    231:                                c = getc(fi);
                    232:                                continue;
                    233:                        }
1.4       millert   234:                        if (!isprint(c) && c != '\t') {
1.1       deraadt   235:                                c = getc(fi);
                    236:                                continue;
                    237:                        }
                    238:                        *cp++ = c;
                    239:                        c = getc(fi);
                    240:                }
                    241:
                    242:                /*
                    243:                 * Toss anything remaining on the input line.
                    244:                 */
                    245:                while (c != '\n' && c != EOF)
                    246:                        c = getc(fi);
1.4       millert   247:
                    248:                if (cp != NULL) {
                    249:                        *cp = '\0';
                    250:                } else {
                    251:                        putchar('\n');
                    252:                        c = getc(fi);
                    253:                        continue;
                    254:                }
                    255:
1.1       deraadt   256:                /*
                    257:                 * Expand tabs on the way to canonb.
                    258:                 */
                    259:                col = 0;
                    260:                cp = linebuf;
                    261:                cp2 = canonb;
1.4       millert   262:                while ((cc = *cp++)) {
                    263:                        if (cc != '\t') {
1.1       deraadt   264:                                col++;
1.4       millert   265:                                if (cp2 - canonb >= cbufsize) {
                    266:                                        int offset = cp2 - canonb;
1.5       millert   267:                                        canonb = extstr(canonb, &cbufsize, NULL);
1.4       millert   268:                                        cp2 = canonb + offset;
                    269:                                }
                    270:                                *cp2++ = cc;
1.1       deraadt   271:                                continue;
                    272:                        }
                    273:                        do {
1.4       millert   274:                                if (cp2 - canonb >= cbufsize) {
                    275:                                        int offset = cp2 - canonb;
1.5       millert   276:                                        canonb = extstr(canonb, &cbufsize, NULL);
1.4       millert   277:                                        cp2 = canonb + offset;
                    278:                                }
                    279:                                *cp2++ = ' ';
1.1       deraadt   280:                                col++;
                    281:                        } while ((col & 07) != 0);
                    282:                }
                    283:
                    284:                /*
                    285:                 * Swipe trailing blanks from the line.
                    286:                 */
                    287:                for (cp2--; cp2 >= canonb && *cp2 == ' '; cp2--)
                    288:                        ;
                    289:                *++cp2 = '\0';
                    290:                prefix(canonb);
                    291:                if (c != EOF)
                    292:                        c = getc(fi);
                    293:        }
                    294: }
                    295:
                    296: /*
                    297:  * Take a line devoid of tabs and other garbage and determine its
                    298:  * blank prefix.  If the indent changes, call for a linebreak.
                    299:  * If the input line is blank, echo the blank line on the output.
                    300:  * Finally, if the line minus the prefix is a mail header, try to keep
                    301:  * it on a line by itself.
                    302:  */
1.4       millert   303: void
1.1       deraadt   304: prefix(line)
                    305:        char line[];
                    306: {
                    307:        register char *cp, **hp;
                    308:        register int np, h;
                    309:
1.4       millert   310:        if (*line == '\0') {
1.1       deraadt   311:                oflush();
                    312:                putchar('\n');
                    313:                return;
                    314:        }
                    315:        for (cp = line; *cp == ' '; cp++)
                    316:                ;
                    317:        np = cp - line;
                    318:
                    319:        /*
                    320:         * The following horrible expression attempts to avoid linebreaks
                    321:         * when the indent changes due to a paragraph.
                    322:         */
                    323:        if (np != pfx && (np > pfx || abs(pfx-np) > 8))
                    324:                oflush();
1.4       millert   325:        if ((h = ishead(cp)))
1.1       deraadt   326:                oflush(), mark = lineno;
                    327:        if (lineno - mark < 3 && lineno - mark > 0)
1.5       millert   328:                for (hp = &headnames[0]; *hp != NULL; hp++)
1.1       deraadt   329:                        if (ispref(*hp, cp)) {
                    330:                                h = 1;
                    331:                                oflush();
                    332:                                break;
                    333:                        }
                    334:        if (!h && (h = (*cp == '.')))
                    335:                oflush();
                    336:        pfx = np;
                    337:        if (h)
                    338:                pack(cp, strlen(cp));
1.5       millert   339:        else
                    340:                split(cp);
1.1       deraadt   341:        if (h)
                    342:                oflush();
                    343:        lineno++;
                    344: }
                    345:
                    346: /*
                    347:  * Split up the passed line into output "words" which are
                    348:  * maximal strings of non-blanks with the blank separation
                    349:  * attached at the end.  Pass these words along to the output
                    350:  * line packer.
                    351:  */
1.4       millert   352: void
1.1       deraadt   353: split(line)
                    354:        char line[];
                    355: {
                    356:        register char *cp, *cp2;
1.5       millert   357:        static char *word;
                    358:        static int wordsize;
1.1       deraadt   359:        int wordl;              /* LIZ@UOM 6/18/85 */
                    360:
1.5       millert   361:        if (strlen(line) >= wordsize)
                    362:                word = extstr(word, &wordsize, strlen(line) + 1);
                    363:
1.1       deraadt   364:        cp = line;
                    365:        while (*cp) {
                    366:                cp2 = word;
                    367:                wordl = 0;      /* LIZ@UOM 6/18/85 */
                    368:
                    369:                /*
                    370:                 * Collect a 'word,' allowing it to contain escaped white
1.4       millert   371:                 * space.
1.1       deraadt   372:                 */
                    373:                while (*cp && *cp != ' ') {
                    374:                        if (*cp == '\\' && isspace(cp[1]))
                    375:                                *cp2++ = *cp++;
                    376:                        *cp2++ = *cp++;
                    377:                        wordl++;/* LIZ@UOM 6/18/85 */
                    378:                }
                    379:
                    380:                /*
                    381:                 * Guarantee a space at end of line. Two spaces after end of
1.4       millert   382:                 * sentence punctuation.
1.1       deraadt   383:                 */
                    384:                if (*cp == '\0') {
                    385:                        *cp2++ = ' ';
1.3       millert   386:                        if (strchr(".:!", cp[-1]))
1.1       deraadt   387:                                *cp2++ = ' ';
                    388:                }
                    389:                while (*cp == ' ')
                    390:                        *cp2++ = *cp++;
                    391:                *cp2 = '\0';
                    392:                /*
1.4       millert   393:                 * LIZ@UOM 6/18/85 pack(word);
1.1       deraadt   394:                 */
                    395:                pack(word, wordl);
                    396:        }
                    397: }
                    398:
                    399: /*
                    400:  * Output section.
                    401:  * Build up line images from the words passed in.  Prefix
                    402:  * each line with correct number of blanks.  The buffer "outbuf"
                    403:  * contains the current partial line image, including prefixed blanks.
                    404:  * "outp" points to the next available space therein.  When outp is NOSTR,
                    405:  * there ain't nothing in there yet.  At the bottom of this whole mess,
                    406:  * leading tabs are reinserted.
                    407:  */
1.5       millert   408: static char *outbuf;                   /* Sandbagged output line image */
                    409: static int   obufsize;                 /* Size of outbuf */
                    410: static char *outp;                     /* Pointer in above */
1.1       deraadt   411:
                    412: /*
                    413:  * Initialize the output section.
                    414:  */
1.4       millert   415: void
1.1       deraadt   416: setout()
                    417: {
                    418:        outp = NOSTR;
                    419: }
                    420:
                    421: /*
                    422:  * Pack a word onto the output line.  If this is the beginning of
                    423:  * the line, push on the appropriately-sized string of blanks first.
                    424:  * If the word won't fit on the current line, flush and begin a new
                    425:  * line.  If the word is too long to fit all by itself on a line,
                    426:  * just give it its own and hope for the best.
                    427:  *
                    428:  * LIZ@UOM 6/18/85 -- If the new word will fit in at less than the
                    429:  *     goal length, take it.  If not, then check to see if the line
                    430:  *     will be over the max length; if so put the word on the next
                    431:  *     line.  If not, check to see if the line will be closer to the
                    432:  *     goal length with or without the word and take it or put it on
                    433:  *     the next line accordingly.
                    434:  */
                    435:
                    436: /*
                    437:  * LIZ@UOM 6/18/85 -- pass in the length of the word as well
                    438:  * pack(word)
                    439:  *     char word[];
                    440:  */
1.4       millert   441: void
                    442: pack(word, wl)
1.1       deraadt   443:        char word[];
                    444:        int wl;
                    445: {
                    446:        register char *cp;
                    447:        register int s, t;
                    448:
                    449:        if (outp == NOSTR)
                    450:                leadin();
                    451:        /*
                    452:         * LIZ@UOM 6/18/85 -- change condition to check goal_length; s is the
                    453:         * length of the line before the word is added; t is now the length
                    454:         * of the line after the word is added
                    455:         *      t = strlen(word);
1.4       millert   456:         *      if (t+s <= LENGTH)
1.1       deraadt   457:         */
                    458:        s = outp - outbuf;
                    459:        t = wl + s;
1.5       millert   460:        if (t + 1 > obufsize) {
                    461:                outbuf = extstr(outbuf, &obufsize, t + 1);
                    462:                outp = outbuf + s;
                    463:        }
1.1       deraadt   464:        if ((t <= goal_length) ||
                    465:            ((t <= max_length) && (t - goal_length <= goal_length - s))) {
                    466:                /*
1.4       millert   467:                 * In like flint!
1.1       deraadt   468:                 */
1.5       millert   469:                for (cp = word; *cp; *outp++ = *cp++)
                    470:                        ;
1.1       deraadt   471:                return;
                    472:        }
                    473:        if (s > pfx) {
                    474:                oflush();
                    475:                leadin();
                    476:        }
1.5       millert   477:        for (cp = word; *cp; *outp++ = *cp++)
                    478:                ;
1.1       deraadt   479: }
                    480:
                    481: /*
                    482:  * If there is anything on the current output line, send it on
                    483:  * its way.  Set outp to NOSTR to indicate the absence of the current
                    484:  * line prefix.
                    485:  */
1.4       millert   486: void
1.1       deraadt   487: oflush()
                    488: {
                    489:        if (outp == NOSTR)
                    490:                return;
                    491:        *outp = '\0';
                    492:        tabulate(outbuf);
                    493:        outp = NOSTR;
                    494: }
                    495:
                    496: /*
                    497:  * Take the passed line buffer, insert leading tabs where possible, and
                    498:  * output on standard output (finally).
                    499:  */
1.4       millert   500: void
1.1       deraadt   501: tabulate(line)
                    502:        char line[];
                    503: {
                    504:        register char *cp;
                    505:        register int b, t;
                    506:
                    507:        /*
                    508:         * Toss trailing blanks in the output line.
                    509:         */
                    510:        cp = line + strlen(line) - 1;
                    511:        while (cp >= line && *cp == ' ')
                    512:                cp--;
                    513:        *++cp = '\0';
1.4       millert   514:
1.1       deraadt   515:        /*
                    516:         * Count the leading blank space and tabulate.
                    517:         */
                    518:        for (cp = line; *cp == ' '; cp++)
                    519:                ;
                    520:        b = cp-line;
                    521:        t = b >> 3;
                    522:        b &= 07;
                    523:        if (t > 0)
                    524:                do
                    525:                        putc('\t', stdout);
                    526:                while (--t);
                    527:        if (b > 0)
                    528:                do
                    529:                        putc(' ', stdout);
                    530:                while (--b);
                    531:        while (*cp)
                    532:                putc(*cp++, stdout);
                    533:        putc('\n', stdout);
                    534: }
                    535:
                    536: /*
                    537:  * Initialize the output line with the appropriate number of
                    538:  * leading blanks.
                    539:  */
1.4       millert   540: void
1.1       deraadt   541: leadin()
                    542: {
                    543:        register int b;
                    544:        register char *cp;
                    545:
1.5       millert   546:        if (obufsize == 0 || (outp != NULL && outp - outbuf <= pfx))
                    547:                outbuf = extstr(outbuf, &obufsize, pfx);
1.1       deraadt   548:        for (b = 0, cp = outbuf; b < pfx; b++)
                    549:                *cp++ = ' ';
                    550:        outp = cp;
                    551: }
                    552:
                    553: /*
                    554:  * Save a string in dynamic space.
                    555:  * This little goodie is needed for
                    556:  * a headline detector in head.c
                    557:  */
                    558: char *
                    559: savestr(str)
                    560:        char str[];
                    561: {
1.4       millert   562:        char *top;
1.1       deraadt   563:
1.4       millert   564:        top = strdup(str);
                    565:        if (top == NOSTR)
                    566:                errx(1, "Ran out of memory");
1.1       deraadt   567:        return (top);
                    568: }
                    569:
                    570: /*
                    571:  * Is s1 a prefix of s2??
                    572:  */
1.4       millert   573: int
1.1       deraadt   574: ispref(s1, s2)
                    575:        register char *s1, *s2;
                    576: {
                    577:
                    578:        while (*s1++ == *s2)
                    579:                ;
                    580:        return (*s1 == '\0');
1.5       millert   581: }
                    582:
                    583: inline char *
                    584: extstr(str, size, gsize)
                    585:        char *str;
                    586:        int *size;
                    587:        int gsize;
                    588: {
                    589:        do {
                    590:                *size += CHUNKSIZE;
                    591:        } while (gsize && *size < gsize);
                    592:
                    593:        if ((str = realloc(str, *size)) == NULL)
                    594:                errx(1, "Ran out of memory");
                    595:
                    596:        return(str);
1.1       deraadt   597: }