1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1992-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
20 ***********************************************************************/
23 static const char usage
[] =
24 "[-?\n@(#)$Id: fmt (AT&T Research) 2007-01-02 $\n]"
26 "[+NAME?fmt - simple text formatter]"
27 "[+DESCRIPTION?\bfmt\b reads the input files and left justifies space "
28 "separated words into lines \awidth\a characters or less in length and "
29 "writes the lines to the standard output. The standard input is read if "
30 "\b-\b or no files are specified. Blank lines and interword spacing are "
31 "preserved in the output. Indentation is preserved, and lines with "
32 "identical indentation are joined and justified.]"
33 "[+?\bfmt\b is meant to format mail messages prior to sending, but may "
34 "also be useful for other simple tasks. For example, in \bvi\b(1) the "
35 "command \b:!}fmt\b will justify the lines in the current paragraph.]"
36 "[c:crown-margin?Preserve the indentation of the first two lines within "
37 "a paragraph, and align the left margin of each subsequent line with "
38 "that of the second line.]"
39 "[o:optget?Format concatenated \boptget\b(3) usage strings.]"
40 "[s:split-only?Split lines only; do not join short lines to form longer "
42 "[u:uniform-spacing?One space between words, two after sentences.]"
43 "[w:width?Set the output line width to \acolumns\a.]#[columns:=72]"
47 "[+SEE ALSO?\bmailx\b(1), \bnroff\b(1), \btroff\b(1), \bvi\b(1), "
74 #define isoption(fp,c) ((fp)->flags&(1L<<((c)-'a')))
75 #define setoption(fp,c) ((fp)->flags|=(1L<<((c)-'a')))
76 #define clroption(fp,c) ((fp)->flags&=~(1L<<((c)-'a')))
81 register char* cp
= fp
->outbuf
;
88 while (fp
->outp
[-1] == ' ')
96 cp
= &fp
->outbuf
[TABSZ
*n
];
103 if (!isoption(fp
, 'o'))
104 sfputr(fp
->out
, cp
, '\n');
115 sfputc(fp
->out
, ' ');
118 if ((d
= (fp
->outp
- cp
)) <= 0)
120 else if ((c
= fp
->outp
[-1]) == 'n' && d
> 1 && fp
->outp
[-2] == '\\')
122 sfprintf(fp
->out
, "\"%s%s\"\n", cp
, c
== ']' || c
== '{' || c
== '}' ? "" : " ");
125 sfputr(fp
->out
, cp
, '\n');
128 fp
->indent
+= fp
->nextdent
;
129 fp
->endbuf
-= fp
->nextdent
;
137 split(Fmt_t
* fp
, char* buf
, int splice
)
147 for (ep
= buf
; *ep
== ' '; ep
++);
151 * preserve blank lines
154 if ((*ep
== 0 || *buf
== '.') && !isoption(fp
, 'o'))
157 prefix
= strlen(buf
);
159 strcpy(fp
->outbuf
, buf
);
160 fp
->outp
= fp
->outbuf
+prefix
;
164 if (fp
->prefix
< prefix
&& !isoption(fp
, 'c'))
166 if (!fp
->outp
|| prefix
< fp
->prefix
)
173 if (cp
!= ep
&& isoption(fp
, 'u'))
185 if (c
== '\\' && *ep
)
189 if (n
&& isoption(fp
, 'o'))
191 for (qp
= cp
; qp
< ep
; qp
++)
199 if (fp
->nwords
> 0 && &fp
->outp
[n
] >= fp
->endbuf
&& !fp
->retain
&& !q
)
205 memset(fp
->outbuf
, ' ', fp
->prefix
);
206 fp
->outp
= &fp
->outbuf
[fp
->prefix
];
211 memcpy(fp
->outp
, cp
, n
);
215 if (isoption(fp
, 's') || *buf
== 0)
220 * two spaces at ends of sentences
223 if (!isoption(fp
, 'o') && strchr(".:!?", fp
->outp
[-1]))
225 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] != ' '))
245 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
)))
247 if (isoption(fp
, 'o'))
249 if (!isoption(fp
, 'i'))
257 else if (*cp
== '\t')
263 fp
->indent
= roundof(b
, INDENT
);
266 while (cp
< lp
&& (*cp
== ' ' || *cp
== '\t'))
268 if (!isoption(fp
, 'q') && cp
< lp
)
280 else if (*ep
!= ' ' && *ep
!= '\t')
296 if (isoption(fp
, 'o'))
319 if (*cp
== ']' || *cp
== '@' && *(cp
+ 1) == '(')
326 if (*cp
== '\\' && *(cp
+ 1) == 'n')
334 else if (*cp
== 't' || *cp
== ' ')
342 if (x
&& dp
!= buf
&& *(dp
- 1) != ' ')
349 else if (*cp
== ' ' || *cp
== '\t')
357 if (x
&& c
!= '\n' && dp
!= buf
&& *(dp
- 1) != ' ')
367 if (c
== ' ' && (dp
== buf
|| *(dp
- 1) == ' '))
399 else if (c
== ']' && (cp
>= lp
|| *cp
!= ':' && *cp
!= '#' && *cp
!= '!'))
401 if (cp
< lp
&& *cp
== ']')
418 else if (fp
->section
)
435 for (tp
= cp
; tp
< lp
; tp
++)
437 if (*tp
== '[' || *tp
== '\n')
439 if (*tp
== ' ' || *tp
== '\t' || *tp
== '"')
441 if (*tp
== '\\' && (lp
- tp
) > 1)
445 if (*tp
== 't' || *tp
== '\n')
453 if (fp
->endbuf
> (fp
->outbuf
+ fp
->indent
+ 2*INDENT
))
454 fp
->nextdent
= 2*INDENT
;
462 if (fp
->indent
&& (b
|| *(cp
- 2) != 'f'))
466 fp
->indent
-= 2*INDENT
;
467 fp
->endbuf
+= 2*INDENT
;
479 else if (c
== ' ' || c
== '\t')
484 else if (c
== '?' && (cp
>= lp
|| *cp
!= '?'))
489 while (cp
< lp
&& *cp
!= ' ' && *cp
!= '\t' && *cp
!= ']' && dp
< &buf
[sizeof(buf
)-3])
491 if (cp
< lp
&& (*cp
== ' ' || *cp
== '\t'))
498 if (fp
->outp
>= fp
->endbuf
)
503 else if (c
== ' ' || c
== '\t')
504 for (c
= ' '; *cp
== ' ' || *cp
== '\t'; cp
++);
524 c
= isoption(fp
, 'o') ? 1 : TABSZ
- (dp
- buf
) % TABSZ
;
525 if (dp
>= &buf
[sizeof(buf
) - c
- 3])
534 else if (!isprint(c
))
536 if (dp
>= &buf
[sizeof(buf
) - 3])
559 split(fp
, buf
, splice
);
565 b_fmt(int argc
, char** argv
, void *context
)
570 char outbuf
[8 * 1024];
576 fmt
.endbuf
= &outbuf
[72];
584 cmdinit(argc
, argv
, context
, ERROR_CATALOG
, 0);
585 while (n
= optget(argv
, usage
))
595 if (opt_info
.num
< TABSZ
|| opt_info
.num
>= sizeof(outbuf
))
596 error(2, "width out of range");
597 fmt
.endbuf
= &outbuf
[opt_info
.num
];
600 error(2, "%s", opt_info
.arg
);
603 error(ERROR_usage(2), "%s", opt_info
.arg
);
606 argv
+= opt_info
.index
;
607 if (error_info
.errors
)
608 error(ERROR_usage(2), "%s", optusage(NiL
));
609 if (isoption(&fmt
, 'o'))
610 setoption(&fmt
, 'c');
611 if (isoption(&fmt
, 's'))
612 clroption(&fmt
, 'u');
616 if (!cp
|| streq(cp
, "-"))
618 else if (!(fmt
.in
= sfopen(NiL
, cp
, "r")))
620 error(ERROR_system(0), "%s: cannot open", cp
);
621 error_info
.errors
= 1;
625 if (fmt
.in
!= sfstdin
)
627 } while (cp
= *argv
++);
629 if (sfsync(sfstdout
))
630 error(ERROR_system(0), "write error");
631 return error_info
.errors
!= 0;