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