1 /***********************************************************************
2 * *
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 *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * *
20 ***********************************************************************/
21 #pragma prototyped
22 /*
23 * David Korn
24 * AT&T Bell Laboratories
25 *
26 * fold
27 */
28
29 static const char usage[] =
30 "[-?\n@(#)$Id: fold (AT&T Research) 2004-11-18 $\n]"
31 USAGE_LICENSE
32 "[+NAME?fold - fold lines]"
33 "[+DESCRIPTION?\bfold\b is a filter that folds lines from its input, "
34 "breaking the lines to have a maximum of \awidth\a column "
35 "positions (or bytes if the \b-b\b option is specified). Lines "
36 "are broken by the insertion of a newline character such that "
37 "each output line is the maximum width possible that does not "
38 "exceed the specified number of column positions, (or bytes). A line "
39 "will not be broken in the middle of a character.] "
40 "[+?Unless the \b-b\b option is specified, the following will be treated "
41 "specially:]{"
42 "[+carriage-return?The current count of line width will be set "
43 "to zero. \bfold\b will not insert a newline immediately "
44 "before or after a carriage-return.]"
45 "[+backspace?If positive, the current count of line width will be "
46 "decremented by one. \bfold\b will not insert a newline "
47 "immediately before or after a backspace.]"
48 "[+tab?Each tab character encountered will advance the column "
49 "position to the next tab stop. Tab stops are at each "
50 "column position \an\a, where \an\a modulo 8 equals 1.]"
51 "}"
52 "[+?If no \afile\a is given, or if the \afile\a is \b-\b, \bfold\b "
53 "reads from standard input. The start of the file is defined "
54 "as the current offset.]"
55
56 "[b:bytes?Count bytes rather than columns so that each carriage-return, "
57 "backspace, and tab counts as 1.]"
58 "[c:continue?Emit \atext\a at line splits.]:[text:='\\n']"
59 "[d:delimiter?Break at \adelim\a boundaries.]:[delim]"
60 "[s:spaces?Break at word boundaries. If the line contains any blanks, "
61 "(spaces or tabs), within the first \awidth\a column positions or "
62 "bytes, the line is broken after the last blank meeting the "
63 "\awidth\a constraint.]"
64 "[w:width]#[width:=80?Use a maximum line length of \awidth\a columns "
65 "instead of the default.]"
66 "\n"
67 "\n[file ...]\n"
68 "\n"
69 "[+EXIT STATUS?]{"
70 "[+0?All files processed successfully.]"
71 "[+>0?An error occurred.]"
72 "}"
73 "[+SEE ALSO?\bpaste\b(1)]"
74 ;
75
76
77 #include <cmd.h>
78
79 #define WIDTH 80
80 #define TABSIZE 8
81
82 #define T_EOF 1
83 #define T_NL 2
84 #define T_BS 3
85 #define T_TAB 4
86 #define T_SP 5
87 #define T_RET 6
88
fold(Sfio_t * in,Sfio_t * out,register int width,const char * cont,size_t contsize,char * cols)89 static void fold(Sfio_t *in, Sfio_t *out, register int width, const char *cont, size_t contsize, char *cols)
90 {
91 register char *cp, *first;
92 register int n, col=0, x=0;
93 register char *last_space=0;
94 cols[0] = 0;
95 for (;;)
96 {
97 if (!(cp = sfgetr(in,'\n',0)))
98 {
99 if (!(cp = sfgetr(in,'\n',-1)) || (n = sfvalue(in)) <= 0)
100 break;
101 x = cp[--n];
102 cp[n] = '\n';
103 }
104 /* special case -b since no column adjustment is needed */
105 if(cols['\b']==0 && (n=sfvalue(in))<=width)
106 {
107 sfwrite(out,cp,n);
108 continue;
109 }
110 first = cp;
111 col = 0;
112 last_space = 0;
113 for(;;)
114 {
115 while((n=cols[*(unsigned char*)cp++])==0);
116 while((cp-first) > (width-col))
117 {
118 if(last_space)
119 col = last_space - first;
120 else
121 col = width-col;
122 sfwrite(out,first,col);
123 first += col;
124 col = 0;
125 last_space = 0;
126 if(cp>first+1 || (n!=T_NL && n!=T_BS))
127 sfwrite(out, cont, contsize);
128 }
129 switch(n)
130 {
131 case T_NL:
132 if(x)
133 *(cp-1) = x;
134 break;
135 case T_RET:
136 col = 0;
137 continue;
138 case T_BS:
139 if((cp+(--col)-first)>0)
140 col--;
141 continue;
142 case T_TAB:
143 n = (TABSIZE-1) - (cp+col-1-first)&(TABSIZE-1);
144 col +=n;
145 if((cp-first) > (width-col))
146 {
147 sfwrite(out,first,(--cp)-first);
148 sfwrite(out, cont, contsize);
149 first = cp;
150 col = TABSIZE-1;
151 last_space = 0;
152 continue;
153 }
154 if(cols[' '])
155 last_space = cp;
156 continue;
157 case T_SP:
158 last_space = cp;
159 continue;
160 default:
161 continue;
162 }
163 break;
164 }
165 sfwrite(out,first,cp-first);
166 }
167 }
168
169 int
b_fold(int argc,char * argv[],void * context)170 b_fold(int argc, char *argv[], void* context)
171 {
172 register int n, width=WIDTH;
173 register Sfio_t *fp;
174 register char *cp;
175 char *cont="\n";
176 size_t contsize = 1;
177 char cols[1<<CHAR_BIT];
178
179 cmdinit(argc, argv, context, ERROR_CATALOG, 0);
180 memset(cols, 0, sizeof(cols));
181 cols['\t'] = T_TAB;
182 cols['\b'] = T_BS;
183 cols['\n'] = T_NL;
184 cols['\r'] = T_RET;
185 for (;;)
186 {
187 switch (optget(argv, usage))
188 {
189 case 0:
190 break;
191 case 'b':
192 cols['\r'] = cols['\b'] = 0;
193 cols['\t'] = cols[' '];
194 continue;
195 case 'c':
196 contsize = stresc(cont = strdup(opt_info.arg));
197 continue;
198 case 'd':
199 if (n = *opt_info.arg)
200 cols[n] = T_SP;
201 continue;
202 case 's':
203 cols[' '] = T_SP;
204 if(cols['\t']==0)
205 cols['\t'] = T_SP;
206 continue;
207 case 'w':
208 if ((width = opt_info.num) <= 0)
209 error(2, "%d: width must be positive", opt_info.num);
210 continue;
211 case ':':
212 error(2, "%s", opt_info.arg);
213 continue;
214 case '?':
215 error(ERROR_usage(2), "%s", opt_info.arg);
216 continue;
217 }
218 break;
219 }
220 argv += opt_info.index;
221 argc -= opt_info.index;
222 if(error_info.errors)
223 error(ERROR_usage(2),"%s", optusage(NiL));
224 if(cp = *argv)
225 argv++;
226 do
227 {
228 if(!cp || streq(cp,"-"))
229 fp = sfstdin;
230 else if(!(fp = sfopen(NiL,cp,"r")))
231 {
232 error(ERROR_system(0),"%s: cannot open",cp);
233 error_info.errors = 1;
234 continue;
235 }
236 fold(fp,sfstdout,width,cont,contsize,cols);
237 if(fp!=sfstdin)
238 sfclose(fp);
239 }
240 while(cp= *argv++);
241 return(error_info.errors);
242 }
243