1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1992-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12da2e3ebdSchin * *
13da2e3ebdSchin * Information and Software Systems Research *
14da2e3ebdSchin * AT&T Research *
15da2e3ebdSchin * Florham Park NJ *
16da2e3ebdSchin * *
17da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> *
18da2e3ebdSchin * David Korn <dgk@research.att.com> *
19da2e3ebdSchin * *
20da2e3ebdSchin ***********************************************************************/
21da2e3ebdSchin #pragma prototyped
22da2e3ebdSchin
23da2e3ebdSchin static const char usage[] =
24da2e3ebdSchin "[-?\n@(#)$Id: fmt (AT&T Research) 2007-01-02 $\n]"
25da2e3ebdSchin USAGE_LICENSE
26da2e3ebdSchin "[+NAME?fmt - simple text formatter]"
27da2e3ebdSchin "[+DESCRIPTION?\bfmt\b reads the input files and left justifies space "
28da2e3ebdSchin "separated words into lines \awidth\a characters or less in length and "
29da2e3ebdSchin "writes the lines to the standard output. The standard input is read if "
30da2e3ebdSchin "\b-\b or no files are specified. Blank lines and interword spacing are "
31da2e3ebdSchin "preserved in the output. Indentation is preserved, and lines with "
32da2e3ebdSchin "identical indentation are joined and justified.]"
33da2e3ebdSchin "[+?\bfmt\b is meant to format mail messages prior to sending, but may "
34da2e3ebdSchin "also be useful for other simple tasks. For example, in \bvi\b(1) the "
35da2e3ebdSchin "command \b:!}fmt\b will justify the lines in the current paragraph.]"
36da2e3ebdSchin "[c:crown-margin?Preserve the indentation of the first two lines within "
37da2e3ebdSchin "a paragraph, and align the left margin of each subsequent line with "
38da2e3ebdSchin "that of the second line.]"
39da2e3ebdSchin "[o:optget?Format concatenated \boptget\b(3) usage strings.]"
40da2e3ebdSchin "[s:split-only?Split lines only; do not join short lines to form longer "
41da2e3ebdSchin "ones.]"
42da2e3ebdSchin "[u:uniform-spacing?One space between words, two after sentences.]"
43da2e3ebdSchin "[w:width?Set the output line width to \acolumns\a.]#[columns:=72]"
44da2e3ebdSchin "\n\n"
45da2e3ebdSchin "[ file ... ]"
46da2e3ebdSchin "\n\n"
47da2e3ebdSchin "[+SEE ALSO?\bmailx\b(1), \bnroff\b(1), \btroff\b(1), \bvi\b(1), "
48da2e3ebdSchin "\boptget\b(3)]"
49da2e3ebdSchin ;
50da2e3ebdSchin
51da2e3ebdSchin #include <cmd.h>
52da2e3ebdSchin #include <ctype.h>
53da2e3ebdSchin
54da2e3ebdSchin typedef struct Fmt_s
55da2e3ebdSchin {
56da2e3ebdSchin long flags;
57da2e3ebdSchin char* outp;
58da2e3ebdSchin char* outbuf;
59da2e3ebdSchin char* endbuf;
60da2e3ebdSchin Sfio_t* in;
61da2e3ebdSchin Sfio_t* out;
62da2e3ebdSchin int indent;
63da2e3ebdSchin int nextdent;
64da2e3ebdSchin int nwords;
65da2e3ebdSchin int prefix;
66da2e3ebdSchin int quote;
67da2e3ebdSchin int retain;
68da2e3ebdSchin int section;
69da2e3ebdSchin } Fmt_t;
70da2e3ebdSchin
71da2e3ebdSchin #define INDENT 4
72da2e3ebdSchin #define TABSZ 8
73da2e3ebdSchin
74da2e3ebdSchin #define isoption(fp,c) ((fp)->flags&(1L<<((c)-'a')))
75da2e3ebdSchin #define setoption(fp,c) ((fp)->flags|=(1L<<((c)-'a')))
76da2e3ebdSchin #define clroption(fp,c) ((fp)->flags&=~(1L<<((c)-'a')))
77da2e3ebdSchin
78da2e3ebdSchin static void
outline(Fmt_t * fp)79da2e3ebdSchin outline(Fmt_t* fp)
80da2e3ebdSchin {
81da2e3ebdSchin register char* cp = fp->outbuf;
82da2e3ebdSchin int n = 0;
83da2e3ebdSchin int c;
84da2e3ebdSchin int d;
85da2e3ebdSchin
86da2e3ebdSchin if (!fp->outp)
87da2e3ebdSchin return;
88da2e3ebdSchin while (fp->outp[-1] == ' ')
89da2e3ebdSchin fp->outp--;
90da2e3ebdSchin *fp->outp = 0;
91da2e3ebdSchin while (*cp++ == ' ')
92da2e3ebdSchin n++;
93da2e3ebdSchin if (n >= TABSZ)
94da2e3ebdSchin {
95da2e3ebdSchin n /= TABSZ;
96da2e3ebdSchin cp = &fp->outbuf[TABSZ*n];
97da2e3ebdSchin while (n--)
98da2e3ebdSchin *--cp = '\t';
99da2e3ebdSchin }
100da2e3ebdSchin else
101da2e3ebdSchin cp = fp->outbuf;
102da2e3ebdSchin fp->nwords = 0;
103da2e3ebdSchin if (!isoption(fp, 'o'))
104da2e3ebdSchin sfputr(fp->out, cp, '\n');
105da2e3ebdSchin else if (*cp)
106da2e3ebdSchin {
107da2e3ebdSchin n = fp->indent;
108da2e3ebdSchin if (*cp != '[')
109da2e3ebdSchin {
110da2e3ebdSchin if (*cp == ' ')
111da2e3ebdSchin cp++;
112da2e3ebdSchin n += INDENT;
113da2e3ebdSchin }
114da2e3ebdSchin while (n--)
115da2e3ebdSchin sfputc(fp->out, ' ');
116da2e3ebdSchin if (fp->quote)
117da2e3ebdSchin {
118da2e3ebdSchin if ((d = (fp->outp - cp)) <= 0)
119da2e3ebdSchin c = 0;
120da2e3ebdSchin else if ((c = fp->outp[-1]) == 'n' && d > 1 && fp->outp[-2] == '\\')
121da2e3ebdSchin c = '}';
122da2e3ebdSchin sfprintf(fp->out, "\"%s%s\"\n", cp, c == ']' || c == '{' || c == '}' ? "" : " ");
123da2e3ebdSchin }
124da2e3ebdSchin else
125da2e3ebdSchin sfputr(fp->out, cp, '\n');
126da2e3ebdSchin if (fp->nextdent)
127da2e3ebdSchin {
128da2e3ebdSchin fp->indent += fp->nextdent;
129da2e3ebdSchin fp->endbuf -= fp->nextdent;
130da2e3ebdSchin fp->nextdent = 0;
131da2e3ebdSchin }
132da2e3ebdSchin }
133da2e3ebdSchin fp->outp = 0;
134da2e3ebdSchin }
135da2e3ebdSchin
136da2e3ebdSchin static void
split(Fmt_t * fp,char * buf,int splice)137da2e3ebdSchin split(Fmt_t* fp, char* buf, int splice)
138da2e3ebdSchin {
139da2e3ebdSchin register char* cp;
140da2e3ebdSchin register char* ep;
141da2e3ebdSchin register char* qp;
142da2e3ebdSchin register int c = 1;
143da2e3ebdSchin register int q = 0;
144da2e3ebdSchin register int n;
145da2e3ebdSchin int prefix;
146da2e3ebdSchin
147da2e3ebdSchin for (ep = buf; *ep == ' '; ep++);
148da2e3ebdSchin prefix = ep - buf;
149da2e3ebdSchin
150da2e3ebdSchin /*
151da2e3ebdSchin * preserve blank lines
152da2e3ebdSchin */
153da2e3ebdSchin
154da2e3ebdSchin if ((*ep == 0 || *buf == '.') && !isoption(fp, 'o'))
155da2e3ebdSchin {
156da2e3ebdSchin if (*ep)
157da2e3ebdSchin prefix = strlen(buf);
158da2e3ebdSchin outline(fp);
159da2e3ebdSchin strcpy(fp->outbuf, buf);
160da2e3ebdSchin fp->outp = fp->outbuf+prefix;
161da2e3ebdSchin outline(fp);
162da2e3ebdSchin return;
163da2e3ebdSchin }
164da2e3ebdSchin if (fp->prefix < prefix && !isoption(fp, 'c'))
165da2e3ebdSchin outline(fp);
166da2e3ebdSchin if (!fp->outp || prefix < fp->prefix)
167da2e3ebdSchin fp->prefix = prefix;
168da2e3ebdSchin while (c)
169da2e3ebdSchin {
170da2e3ebdSchin cp = ep;
171da2e3ebdSchin while (*ep == ' ')
172da2e3ebdSchin ep++;
173da2e3ebdSchin if (cp != ep && isoption(fp, 'u'))
174da2e3ebdSchin cp = ep-1;
175da2e3ebdSchin while (c = *ep)
176da2e3ebdSchin {
177da2e3ebdSchin if (c == ' ')
178da2e3ebdSchin break;
179da2e3ebdSchin ep++;
180da2e3ebdSchin
181da2e3ebdSchin /*
182da2e3ebdSchin * skip over \space
183da2e3ebdSchin */
184da2e3ebdSchin
185da2e3ebdSchin if (c == '\\' && *ep)
186da2e3ebdSchin ep++;
187da2e3ebdSchin }
188da2e3ebdSchin n = (ep-cp);
189da2e3ebdSchin if (n && isoption(fp, 'o'))
190da2e3ebdSchin {
191da2e3ebdSchin for (qp = cp; qp < ep; qp++)
192da2e3ebdSchin if (*qp == '\\')
193da2e3ebdSchin qp++;
194da2e3ebdSchin else if (*qp == '"')
195da2e3ebdSchin q = !q;
196da2e3ebdSchin if (*(ep-1) == '"')
197da2e3ebdSchin goto skip;
198da2e3ebdSchin }
199da2e3ebdSchin if (fp->nwords > 0 && &fp->outp[n] >= fp->endbuf && !fp->retain && !q)
200da2e3ebdSchin outline(fp);
201da2e3ebdSchin skip:
202da2e3ebdSchin if (fp->nwords == 0)
203da2e3ebdSchin {
204da2e3ebdSchin if (fp->prefix)
205da2e3ebdSchin memset(fp->outbuf, ' ', fp->prefix);
206da2e3ebdSchin fp->outp = &fp->outbuf[fp->prefix];
207da2e3ebdSchin while (*cp == ' ')
208da2e3ebdSchin cp++;
209da2e3ebdSchin n = (ep-cp);
210da2e3ebdSchin }
211da2e3ebdSchin memcpy(fp->outp, cp, n);
212da2e3ebdSchin fp->outp += n;
213da2e3ebdSchin fp->nwords++;
214da2e3ebdSchin }
215da2e3ebdSchin if (isoption(fp, 's') || *buf == 0)
216da2e3ebdSchin outline(fp);
217da2e3ebdSchin else if (fp->outp)
218da2e3ebdSchin {
219da2e3ebdSchin /*
220da2e3ebdSchin * two spaces at ends of sentences
221da2e3ebdSchin */
222da2e3ebdSchin
223da2e3ebdSchin if (!isoption(fp, 'o') && strchr(".:!?", fp->outp[-1]))
224da2e3ebdSchin *fp->outp++ = ' ';
225da2e3ebdSchin if (!splice && !fp->retain && (!fp->quote || (fp->outp - fp->outbuf) < 2 || fp->outp[-2] != '\\' || fp->outp[-1] != 'n' && fp->outp[-1] != 't' && fp->outp[-1] != ' '))
226da2e3ebdSchin *fp->outp++ = ' ';
227da2e3ebdSchin }
228da2e3ebdSchin }
229da2e3ebdSchin
230da2e3ebdSchin static int
dofmt(Fmt_t * fp)231da2e3ebdSchin dofmt(Fmt_t* fp)
232da2e3ebdSchin {
233da2e3ebdSchin register int c;
234da2e3ebdSchin int b;
235da2e3ebdSchin int x;
236da2e3ebdSchin int splice;
237da2e3ebdSchin char* cp;
238da2e3ebdSchin char* dp;
239da2e3ebdSchin char* ep;
240da2e3ebdSchin char* lp;
241da2e3ebdSchin char* tp;
242da2e3ebdSchin char buf[8192];
243da2e3ebdSchin
244da2e3ebdSchin cp = 0;
245da2e3ebdSchin while (cp || (cp = sfgetr(fp->in, '\n', 0)) && !(splice = 0) && (lp = cp + sfvalue(fp->in) - 1) || (cp = sfgetr(fp->in, '\n', SF_LASTR)) && (splice = 1) && (lp = cp + sfvalue(fp->in)))
246da2e3ebdSchin {
247da2e3ebdSchin if (isoption(fp, 'o'))
248da2e3ebdSchin {
249da2e3ebdSchin if (!isoption(fp, 'i'))
250da2e3ebdSchin {
251da2e3ebdSchin setoption(fp, 'i');
252da2e3ebdSchin b = 0;
253da2e3ebdSchin while (cp < lp)
254da2e3ebdSchin {
255da2e3ebdSchin if (*cp == ' ')
256da2e3ebdSchin b += 1;
257da2e3ebdSchin else if (*cp == '\t')
258da2e3ebdSchin b += INDENT;
259da2e3ebdSchin else
260da2e3ebdSchin break;
261da2e3ebdSchin cp++;
262da2e3ebdSchin }
263da2e3ebdSchin fp->indent = roundof(b, INDENT);
264da2e3ebdSchin }
265da2e3ebdSchin else
266da2e3ebdSchin while (cp < lp && (*cp == ' ' || *cp == '\t'))
267da2e3ebdSchin cp++;
268da2e3ebdSchin if (!isoption(fp, 'q') && cp < lp)
269da2e3ebdSchin {
270da2e3ebdSchin setoption(fp, 'q');
271da2e3ebdSchin if (*cp == '"')
272da2e3ebdSchin {
273da2e3ebdSchin ep = lp;
274da2e3ebdSchin while (--ep > cp)
275da2e3ebdSchin if (*ep == '"')
276da2e3ebdSchin {
277da2e3ebdSchin fp->quote = 1;
278da2e3ebdSchin break;
279da2e3ebdSchin }
280da2e3ebdSchin else if (*ep != ' ' && *ep != '\t')
281da2e3ebdSchin break;
282da2e3ebdSchin }
283da2e3ebdSchin }
284da2e3ebdSchin }
285da2e3ebdSchin again:
286da2e3ebdSchin dp = buf;
287da2e3ebdSchin ep = 0;
288da2e3ebdSchin for (b = 1;; b = 0)
289da2e3ebdSchin {
290da2e3ebdSchin if (cp >= lp)
291da2e3ebdSchin {
292da2e3ebdSchin cp = 0;
293da2e3ebdSchin break;
294da2e3ebdSchin }
295da2e3ebdSchin c = *cp++;
296da2e3ebdSchin if (isoption(fp, 'o'))
297da2e3ebdSchin {
298da2e3ebdSchin if (c == '\\')
299da2e3ebdSchin {
300da2e3ebdSchin x = 0;
301da2e3ebdSchin c = ' ';
302da2e3ebdSchin cp--;
303da2e3ebdSchin while (cp < lp)
304da2e3ebdSchin {
305da2e3ebdSchin if (*cp == '\\')
306da2e3ebdSchin {
307da2e3ebdSchin cp++;
308da2e3ebdSchin if ((lp - cp) < 1)
309da2e3ebdSchin {
310da2e3ebdSchin c = '\\';
311da2e3ebdSchin break;
312da2e3ebdSchin }
313da2e3ebdSchin if (*cp == 'n')
314da2e3ebdSchin {
315da2e3ebdSchin cp++;
316da2e3ebdSchin c = '\n';
317da2e3ebdSchin if ((lp - cp) > 2)
318da2e3ebdSchin {
319da2e3ebdSchin if (*cp == ']' || *cp == '@' && *(cp + 1) == '(')
320da2e3ebdSchin {
321da2e3ebdSchin *dp++ = '\\';
322da2e3ebdSchin *dp++ = 'n';
323da2e3ebdSchin c = *cp++;
324da2e3ebdSchin break;
325da2e3ebdSchin }
326da2e3ebdSchin if (*cp == '\\' && *(cp + 1) == 'n')
327da2e3ebdSchin {
328da2e3ebdSchin cp += 2;
329da2e3ebdSchin *dp++ = '\n';
330da2e3ebdSchin break;
331da2e3ebdSchin }
332da2e3ebdSchin }
333da2e3ebdSchin }
334da2e3ebdSchin else if (*cp == 't' || *cp == ' ')
335da2e3ebdSchin {
336da2e3ebdSchin cp++;
337da2e3ebdSchin x = 1;
338da2e3ebdSchin c = ' ';
339da2e3ebdSchin }
340da2e3ebdSchin else
341da2e3ebdSchin {
342da2e3ebdSchin if (x && dp != buf && *(dp - 1) != ' ')
343da2e3ebdSchin *dp++ = ' ';
344da2e3ebdSchin *dp++ = '\\';
345da2e3ebdSchin c = *cp++;
346da2e3ebdSchin break;
347da2e3ebdSchin }
348da2e3ebdSchin }
349da2e3ebdSchin else if (*cp == ' ' || *cp == '\t')
350da2e3ebdSchin {
351da2e3ebdSchin cp++;
352da2e3ebdSchin c = ' ';
353da2e3ebdSchin x = 1;
354da2e3ebdSchin }
355da2e3ebdSchin else
356da2e3ebdSchin {
357da2e3ebdSchin if (x && c != '\n' && dp != buf && *(dp - 1) != ' ')
358da2e3ebdSchin *dp++ = ' ';
359da2e3ebdSchin break;
360da2e3ebdSchin }
361da2e3ebdSchin }
362da2e3ebdSchin if (c == '\n')
363da2e3ebdSchin {
364da2e3ebdSchin c = 0;
365da2e3ebdSchin goto flush;
366da2e3ebdSchin }
367da2e3ebdSchin if (c == ' ' && (dp == buf || *(dp - 1) == ' '))
368da2e3ebdSchin continue;
369da2e3ebdSchin }
370da2e3ebdSchin else if (c == '"')
371da2e3ebdSchin {
372da2e3ebdSchin if (b || cp >= lp)
373da2e3ebdSchin {
374da2e3ebdSchin if (fp->quote)
375da2e3ebdSchin continue;
376da2e3ebdSchin fp->section = 0;
377da2e3ebdSchin }
378da2e3ebdSchin }
379da2e3ebdSchin else if (c == '\a')
380da2e3ebdSchin {
381da2e3ebdSchin *dp++ = '\\';
382da2e3ebdSchin c = 'a';
383da2e3ebdSchin }
384da2e3ebdSchin else if (c == '\b')
385da2e3ebdSchin {
386da2e3ebdSchin *dp++ = '\\';
387da2e3ebdSchin c = 'b';
388da2e3ebdSchin }
389da2e3ebdSchin else if (c == '\f')
390da2e3ebdSchin {
391da2e3ebdSchin *dp++ = '\\';
392da2e3ebdSchin c = 'f';
393da2e3ebdSchin }
394da2e3ebdSchin else if (c == '\v')
395da2e3ebdSchin {
396da2e3ebdSchin *dp++ = '\\';
397da2e3ebdSchin c = 'v';
398da2e3ebdSchin }
399da2e3ebdSchin else if (c == ']' && (cp >= lp || *cp != ':' && *cp != '#' && *cp != '!'))
400da2e3ebdSchin {
401da2e3ebdSchin if (cp < lp && *cp == ']')
402da2e3ebdSchin {
403da2e3ebdSchin cp++;
404da2e3ebdSchin *dp++ = c;
405da2e3ebdSchin }
406da2e3ebdSchin else
407da2e3ebdSchin {
408da2e3ebdSchin fp->section = 1;
409da2e3ebdSchin fp->retain = 0;
410da2e3ebdSchin flush:
411da2e3ebdSchin *dp++ = c;
412da2e3ebdSchin *dp = 0;
413da2e3ebdSchin split(fp, buf, 0);
414da2e3ebdSchin outline(fp);
415da2e3ebdSchin goto again;
416da2e3ebdSchin }
417da2e3ebdSchin }
418da2e3ebdSchin else if (fp->section)
419da2e3ebdSchin {
420da2e3ebdSchin if (c == '[')
421da2e3ebdSchin {
422da2e3ebdSchin if (b)
423da2e3ebdSchin fp->retain = 1;
424da2e3ebdSchin else
425da2e3ebdSchin {
426da2e3ebdSchin cp--;
427da2e3ebdSchin c = 0;
428da2e3ebdSchin goto flush;
429da2e3ebdSchin }
430da2e3ebdSchin fp->section = 0;
431da2e3ebdSchin }
432da2e3ebdSchin else if (c == '{')
433da2e3ebdSchin {
434da2e3ebdSchin x = 1;
435da2e3ebdSchin for (tp = cp; tp < lp; tp++)
436da2e3ebdSchin {
437da2e3ebdSchin if (*tp == '[' || *tp == '\n')
438da2e3ebdSchin break;
439da2e3ebdSchin if (*tp == ' ' || *tp == '\t' || *tp == '"')
440da2e3ebdSchin continue;
441da2e3ebdSchin if (*tp == '\\' && (lp - tp) > 1)
442da2e3ebdSchin {
443da2e3ebdSchin if (*++tp == 'n')
444da2e3ebdSchin break;
445da2e3ebdSchin if (*tp == 't' || *tp == '\n')
446da2e3ebdSchin continue;
447da2e3ebdSchin }
448da2e3ebdSchin x = 0;
449da2e3ebdSchin break;
450da2e3ebdSchin }
451da2e3ebdSchin if (x)
452da2e3ebdSchin {
453da2e3ebdSchin if (fp->endbuf > (fp->outbuf + fp->indent + 2*INDENT))
454da2e3ebdSchin fp->nextdent = 2*INDENT;
455da2e3ebdSchin goto flush;
456da2e3ebdSchin }
457da2e3ebdSchin else
458da2e3ebdSchin fp->section = 0;
459da2e3ebdSchin }
460da2e3ebdSchin else if (c == '}')
461da2e3ebdSchin {
462da2e3ebdSchin if (fp->indent && (b || *(cp - 2) != 'f'))
463da2e3ebdSchin {
464da2e3ebdSchin if (b)
465da2e3ebdSchin {
466da2e3ebdSchin fp->indent -= 2*INDENT;
467da2e3ebdSchin fp->endbuf += 2*INDENT;
468da2e3ebdSchin }
469da2e3ebdSchin else
470da2e3ebdSchin {
471da2e3ebdSchin cp--;
472da2e3ebdSchin c = 0;
473da2e3ebdSchin }
474da2e3ebdSchin goto flush;
475da2e3ebdSchin }
476da2e3ebdSchin else
477da2e3ebdSchin fp->section = 0;
478da2e3ebdSchin }
479da2e3ebdSchin else if (c == ' ' || c == '\t')
480da2e3ebdSchin continue;
481da2e3ebdSchin else
482da2e3ebdSchin fp->section = 0;
483da2e3ebdSchin }
484da2e3ebdSchin else if (c == '?' && (cp >= lp || *cp != '?'))
485da2e3ebdSchin {
486da2e3ebdSchin if (fp->retain)
487da2e3ebdSchin {
488da2e3ebdSchin cp--;
489da2e3ebdSchin while (cp < lp && *cp != ' ' && *cp != '\t' && *cp != ']' && dp < &buf[sizeof(buf)-3])
490da2e3ebdSchin *dp++ = *cp++;
491da2e3ebdSchin if (cp < lp && (*cp == ' ' || *cp == '\t'))
492da2e3ebdSchin *dp++ = *cp++;
493da2e3ebdSchin *dp = 0;
494da2e3ebdSchin split(fp, buf, 0);
495da2e3ebdSchin dp = buf;
496da2e3ebdSchin ep = 0;
497da2e3ebdSchin fp->retain = 0;
498da2e3ebdSchin if (fp->outp >= fp->endbuf)
499da2e3ebdSchin outline(fp);
500da2e3ebdSchin continue;
501da2e3ebdSchin }
502da2e3ebdSchin }
503da2e3ebdSchin else if (c == ' ' || c == '\t')
504da2e3ebdSchin for (c = ' '; *cp == ' ' || *cp == '\t'; cp++);
505da2e3ebdSchin }
506da2e3ebdSchin else if (c == '\b')
507da2e3ebdSchin {
508da2e3ebdSchin if (dp > buf)
509da2e3ebdSchin {
510da2e3ebdSchin dp--;
511da2e3ebdSchin if (ep)
512da2e3ebdSchin ep--;
513da2e3ebdSchin }
514da2e3ebdSchin continue;
515da2e3ebdSchin }
516da2e3ebdSchin else if (c == '\t')
517da2e3ebdSchin {
518da2e3ebdSchin /*
519da2e3ebdSchin * expand tabs
520da2e3ebdSchin */
521da2e3ebdSchin
522da2e3ebdSchin if (!ep)
523da2e3ebdSchin ep = dp;
524da2e3ebdSchin c = isoption(fp, 'o') ? 1 : TABSZ - (dp - buf) % TABSZ;
525da2e3ebdSchin if (dp >= &buf[sizeof(buf) - c - 3])
526da2e3ebdSchin {
527da2e3ebdSchin cp--;
528da2e3ebdSchin break;
529da2e3ebdSchin }
530da2e3ebdSchin while (c-- > 0)
531da2e3ebdSchin *dp++ = ' ';
532da2e3ebdSchin continue;
533da2e3ebdSchin }
534da2e3ebdSchin else if (!isprint(c))
535da2e3ebdSchin continue;
536da2e3ebdSchin if (dp >= &buf[sizeof(buf) - 3])
537da2e3ebdSchin {
538da2e3ebdSchin tp = dp;
539da2e3ebdSchin while (--tp > buf)
540da2e3ebdSchin if (isspace(*tp))
541da2e3ebdSchin {
542da2e3ebdSchin cp -= dp - tp;
543da2e3ebdSchin dp = tp;
544da2e3ebdSchin break;
545da2e3ebdSchin }
546da2e3ebdSchin ep = 0;
547da2e3ebdSchin break;
548da2e3ebdSchin }
549da2e3ebdSchin if (c != ' ')
550da2e3ebdSchin ep = 0;
551da2e3ebdSchin else if (!ep)
552da2e3ebdSchin ep = dp;
553da2e3ebdSchin *dp++ = c;
554da2e3ebdSchin }
555da2e3ebdSchin if (ep)
556da2e3ebdSchin *ep = 0;
557da2e3ebdSchin else
558da2e3ebdSchin *dp = 0;
559da2e3ebdSchin split(fp, buf, splice);
560da2e3ebdSchin }
561da2e3ebdSchin return 0;
562da2e3ebdSchin }
563da2e3ebdSchin
564da2e3ebdSchin int
b_fmt(int argc,char ** argv,void * context)565da2e3ebdSchin b_fmt(int argc, char** argv, void *context)
566da2e3ebdSchin {
567da2e3ebdSchin register int n;
568da2e3ebdSchin char* cp;
569da2e3ebdSchin Fmt_t fmt;
570da2e3ebdSchin char outbuf[8 * 1024];
571da2e3ebdSchin
572da2e3ebdSchin fmt.flags = 0;
573da2e3ebdSchin fmt.out = sfstdout;
574da2e3ebdSchin fmt.outbuf = outbuf;
575da2e3ebdSchin fmt.outp = 0;
576da2e3ebdSchin fmt.endbuf = &outbuf[72];
577da2e3ebdSchin fmt.indent = 0;
578da2e3ebdSchin fmt.nextdent = 0;
579da2e3ebdSchin fmt.nwords = 0;
580da2e3ebdSchin fmt.prefix = 0;
581da2e3ebdSchin fmt.quote = 0;
582da2e3ebdSchin fmt.retain = 0;
583da2e3ebdSchin fmt.section = 1;
584da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, 0);
585da2e3ebdSchin while (n = optget(argv, usage))
586da2e3ebdSchin switch (n)
587da2e3ebdSchin {
588da2e3ebdSchin case 'c':
589da2e3ebdSchin case 'o':
590da2e3ebdSchin case 's':
591da2e3ebdSchin case 'u':
592da2e3ebdSchin setoption(&fmt, n);
593da2e3ebdSchin break;
594da2e3ebdSchin case 'w':
595da2e3ebdSchin if (opt_info.num < TABSZ || opt_info.num>= sizeof(outbuf))
596da2e3ebdSchin error(2, "width out of range");
597da2e3ebdSchin fmt.endbuf = &outbuf[opt_info.num];
598da2e3ebdSchin break;
599da2e3ebdSchin case ':':
600da2e3ebdSchin error(2, "%s", opt_info.arg);
601da2e3ebdSchin break;
602da2e3ebdSchin case '?':
603da2e3ebdSchin error(ERROR_usage(2), "%s", opt_info.arg);
604da2e3ebdSchin break;
605da2e3ebdSchin }
606da2e3ebdSchin argv += opt_info.index;
607da2e3ebdSchin if (error_info.errors)
608da2e3ebdSchin error(ERROR_usage(2), "%s", optusage(NiL));
609da2e3ebdSchin if (isoption(&fmt, 'o'))
610da2e3ebdSchin setoption(&fmt, 'c');
611da2e3ebdSchin if (isoption(&fmt, 's'))
612da2e3ebdSchin clroption(&fmt, 'u');
613da2e3ebdSchin if (cp = *argv)
614da2e3ebdSchin argv++;
615da2e3ebdSchin do {
616da2e3ebdSchin if (!cp || streq(cp, "-"))
617da2e3ebdSchin fmt.in = sfstdin;
618da2e3ebdSchin else if (!(fmt.in = sfopen(NiL, cp, "r")))
619da2e3ebdSchin {
620da2e3ebdSchin error(ERROR_system(0), "%s: cannot open", cp);
621da2e3ebdSchin error_info.errors = 1;
622da2e3ebdSchin continue;
623da2e3ebdSchin }
624da2e3ebdSchin dofmt(&fmt);
625da2e3ebdSchin if (fmt.in != sfstdin)
626da2e3ebdSchin sfclose(fmt.in);
627da2e3ebdSchin } while (cp = *argv++);
628da2e3ebdSchin outline(&fmt);
629da2e3ebdSchin if (sfsync(sfstdout))
630da2e3ebdSchin error(ERROR_system(0), "write error");
631da2e3ebdSchin return error_info.errors != 0;
632da2e3ebdSchin }
633