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