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