xref: /illumos-gate/usr/src/contrib/ast/src/lib/libcmd/fmt.c (revision b30d1939)
1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*b30d1939SAndy Fiddaman *          Copyright (c) 1992-2012 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6*b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10*b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11*b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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,Shbltin_t * context)565*b30d1939SAndy Fiddaman b_fmt(int argc, char** argv, Shbltin_t* 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);
585*b30d1939SAndy Fiddaman 	for (;;)
586*b30d1939SAndy Fiddaman 	{
587*b30d1939SAndy Fiddaman 		switch (n = optget(argv, usage))
588da2e3ebdSchin 		{
589da2e3ebdSchin 		case 'c':
590da2e3ebdSchin 		case 'o':
591da2e3ebdSchin 		case 's':
592da2e3ebdSchin 		case 'u':
593da2e3ebdSchin 			setoption(&fmt, n);
594*b30d1939SAndy Fiddaman 			continue;
595da2e3ebdSchin 		case 'w':
596da2e3ebdSchin 			if (opt_info.num < TABSZ || opt_info.num>= sizeof(outbuf))
597da2e3ebdSchin 				error(2, "width out of range");
598da2e3ebdSchin 			fmt.endbuf = &outbuf[opt_info.num];
599*b30d1939SAndy Fiddaman 			continue;
600da2e3ebdSchin 		case ':':
601da2e3ebdSchin 			error(2, "%s", opt_info.arg);
602da2e3ebdSchin 			break;
603da2e3ebdSchin 		case '?':
604da2e3ebdSchin 			error(ERROR_usage(2), "%s", opt_info.arg);
605da2e3ebdSchin 			break;
606da2e3ebdSchin 		}
607*b30d1939SAndy Fiddaman 		break;
608*b30d1939SAndy Fiddaman 	}
609da2e3ebdSchin 	argv += opt_info.index;
610da2e3ebdSchin 	if (error_info.errors)
611da2e3ebdSchin 		error(ERROR_usage(2), "%s", optusage(NiL));
612da2e3ebdSchin 	if (isoption(&fmt, 'o'))
613da2e3ebdSchin 		setoption(&fmt, 'c');
614da2e3ebdSchin 	if (isoption(&fmt, 's'))
615da2e3ebdSchin 		clroption(&fmt, 'u');
616da2e3ebdSchin 	if (cp = *argv)
617da2e3ebdSchin 		argv++;
618da2e3ebdSchin 	do {
619da2e3ebdSchin 		if (!cp || streq(cp, "-"))
620da2e3ebdSchin 			fmt.in = sfstdin;
621da2e3ebdSchin 		else if (!(fmt.in = sfopen(NiL, cp, "r")))
622da2e3ebdSchin 		{
623da2e3ebdSchin 			error(ERROR_system(0), "%s: cannot open", cp);
624da2e3ebdSchin 			error_info.errors = 1;
625da2e3ebdSchin 			continue;
626da2e3ebdSchin 		}
627da2e3ebdSchin 		dofmt(&fmt);
628da2e3ebdSchin 		if (fmt.in != sfstdin)
629da2e3ebdSchin 			sfclose(fmt.in);
630da2e3ebdSchin 	} while (cp = *argv++);
631da2e3ebdSchin 	outline(&fmt);
632da2e3ebdSchin 	if (sfsync(sfstdout))
633da2e3ebdSchin 		error(ERROR_system(0), "write error");
634da2e3ebdSchin 	return error_info.errors != 0;
635da2e3ebdSchin }
636