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