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