removed some functions in the C library
[unix-history] / usr / src / usr.bin / fmt / fmt.c
CommitLineData
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 19char 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 25static 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
48int goal_length; /* Target or goal line length in output */
49int max_length; /* Max line length in output */
79e55e87
KS
50int pfx; /* Current leading blank count */
51int lineno; /* Current input line */
52int mark; /* Last place we saw a head line */
53
828615a1 54char *malloc(); /* for lint . . . */
79e55e87
KS
55char *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
63main(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
117fmt(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
192prefix(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
237split(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
288char outbuf[BUFSIZ]; /* Sandbagged output line image */
289char *outp; /* Pointer in above */
290
291/*
292 * Initialize the output section.
293 */
79e55e87
KS
294setout()
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 */
319pack(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
357oflush()
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
370tabulate(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
409leadin()
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
424char *
425savestr(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
442ispref(s1, s2)
443 register char *s1, *s2;
444{
445
446 while (*s1++ == *s2)
447 ;
fadf875b 448 return (*s1 == '\0');
79e55e87 449}