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: }