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