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