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