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