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