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