Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/std/unexpand.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1989-2011 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* expand.c and unexpand.c
23
* They can be linked together
24
* Written by David Korn
25
* Sat Oct 8 13:47:13 EDT 1994
26
*/
27
28
static const char expand_usage[] =
29
"[-?@(#)$Id: expand (AT&T Research) 1999-06-17 $\n]"
30
USAGE_LICENSE
31
"[+NAME?expand - convert tabs to spaces]"
32
"[+DESCRIPTION?\bexpand\b writes the contents of each given file "
33
"to standard output with tab characters replaced with one or "
34
"more space characters needed to pad to the next tab stop. Each "
35
"backspace character copied to standard output causes the column "
36
"position to be decremented by 1 unless already in the first column.]" "[+?If no \afile\a is given, or if the \afile\a is \b-\b, \bexpand\b "
37
"copies from standard input. The start of the file is defined "
38
"as the current offset.]"
39
"[i:initial? Only convert initial tabs (those that precede all non "
40
"space or tab characters) on each line to spaces.]"
41
"[t:tabs]:[tablist:=8?\atablist\a is a comma or space separated list "
42
"of positive integers that specifies the tab stops. If only one "
43
"tab stop is specified, then tabs will be set at that many "
44
"column positions apart. Otherwise, the value in \atablist\a "
45
"must be in ascending order and the tab stops will be set to "
46
"these positions. In the event of \bexpand\b having to process "
47
"tab characters beyond the last specified tab stop, each tab "
48
"character is replaced by a single tab.]"
49
"\n"
50
"\n[file ...]\n"
51
"\n"
52
"[+EXIT STATUS]{"
53
"[+0?All files expanded successfully.]"
54
"[+>0?One or more files failed to open or could not be read.]"
55
"}"
56
"[+SEE ALSO?\bunexpand\b(1), \bpr\b(1)]"
57
;
58
59
static const char unexpand_usage[] =
60
"[-?@(#)$Id: unexpand (AT&T Research) 1999-06-07 $\n]"
61
USAGE_LICENSE
62
"[+NAME?unexpand - convert spaces to tabs]"
63
"[+DESCRIPTION?\bunexpand\b writes the contents of each given file "
64
"to standard output with strings of two or more space and "
65
"tab characters at the beginning of each line converted "
66
"to as many tabs as possible followed by as many spaces needed "
67
"to fill the same number of column positions. By default, "
68
"tabs are set at every 8th column. Each backspace character copied "
69
"to standard output causes the column position to be decremented by 1 "
70
"unless already in the first column.]"
71
"[+?If no \afile\a is given, or if the \afile\a is \b-\b, \bunexpand\b "
72
"copies from standard input. The start of the file is defined "
73
"as the current offset.]"
74
"[a:all?Convert all strings of two or more spaces or tabs, not just "
75
"initial ones.]"
76
"[t:tabs]:[tablist:=8?\atablist\a is a comma or space separated list "
77
"of positive integers that specifies the tab stops. If only one "
78
"tab stop is specified, then tabs will be set at that many "
79
"column positions apart. Otherwise, the value in \atablist\a "
80
"must be in ascending order and the tab stops will be set to "
81
"these positions. This option implies the \b-a\b option.]"
82
"\n"
83
"\n[file ...]\n"
84
"\n"
85
"[+EXIT STATUS?]{"
86
"[+0?All files unexpanded successfully.]"
87
"[+>0?One or more files failed to open or could not be read.]"
88
"}"
89
"[+SEE ALSO?\bexpand\b(1), \bpr\b(1)]"
90
;
91
92
93
#include <cmd.h>
94
95
#define S_BS 1
96
#define S_TAB 2
97
#define S_NL 3
98
#define S_EOF 4
99
#define S_SPACE 5
100
101
static int *gettabs(const char *arg, int *ntab)
102
{
103
register const char *cp=arg;
104
register int c,n=1,old= -1;
105
int *tablist;
106
while(c= *cp++)
107
{
108
if(c==' ' || c=='\t' || c==',')
109
n++;
110
}
111
tablist = newof(NiL,int,n,0);
112
n=0;
113
while(1)
114
{
115
cp=arg;
116
while((c= *cp) && c==' ' || c=='\t' || c==',')
117
cp++;
118
if(c==0)
119
break;
120
tablist[n] = strtol(cp,(char**)&arg,10)-1;
121
if(cp==arg)
122
error(ERROR_exit(1),"%c - invalid character in tablist",*cp);
123
if(tablist[n] <= old)
124
error(ERROR_exit(1),"tab stops must be increasing");
125
old = tablist[n++];
126
}
127
*ntab=n;
128
return(tablist);
129
}
130
131
static int expand(Sfio_t *in, Sfio_t *out, int tablist[], int tabmax, int type,int initial)
132
{
133
static char state[256];
134
register int n=0;
135
register char *cp, *first, *buff;
136
register int savec;
137
register char *cpend;
138
state[0] = S_EOF;
139
state['\b'] = S_BS;
140
state['\n'] = S_NL;
141
if(type)
142
state['\t'] = S_TAB;
143
else
144
state[' '] = S_SPACE;
145
errno=0;
146
while(buff = cp = sfreserve(in,SF_UNBOUND,0))
147
{
148
first = cp-n;
149
cpend = cp + sfvalue(in);
150
if(state[n= *(unsigned char*)(cpend-1)]==0 || (n==' '&&type==0))
151
cpend[-1] = 0; /* put in sentinal */
152
savec = n;
153
while(1)
154
{
155
while((n=state[*(unsigned char*)cp++])==0);
156
switch(n)
157
{
158
case S_SPACE:
159
if(tabmax==0 || cp==first+1)
160
{
161
register int i,tabspace = tablist[0];
162
n = 1;
163
cp -= 1;
164
if(tabmax==0)
165
tabspace -= (cp-first)%tabspace;
166
while(cp[n]==' ')
167
n++;
168
i = cp-buff;
169
/* check for end of buffer */
170
if(cp[n]==0 && cp+n+1==cpend && savec==' ')
171
{
172
/* keep grabbing spaces */
173
register int c;
174
if(i)
175
sfwrite(out,buff, i);
176
i=0;
177
n++;
178
while((c=sfgetc(in))==' ')
179
n++;
180
if(c!=EOF)
181
sfungetc(in,c);
182
buff = cp+n;
183
}
184
cp += n;
185
if(n >= tabspace || cp+n>cpend)
186
{
187
if(i)
188
{
189
sfwrite(out,buff, i);
190
i=0;
191
}
192
buff = cp;
193
while(n >= tabspace)
194
{
195
sfputc(out,'\t');
196
n -= tabspace;
197
if(++i < tabmax)
198
tabspace = tablist[i]-tablist[i-1];
199
else if(tabmax<=1)
200
tabspace = tablist[0];
201
else
202
break;
203
204
}
205
if(n>0)
206
sfnputc(out,' ',n);
207
}
208
}
209
if(tabmax)
210
state[' '] = 0;
211
break;
212
case S_TAB:
213
sfwrite(out,buff, (cp-1)-buff);
214
n = (cp-1)-first;
215
if(tabmax==1)
216
n = tablist[0]*((n+tablist[0])/tablist[0])-n;
217
else
218
{
219
register int i=0;
220
while(1)
221
{
222
if(i>=tabmax)
223
n=1;
224
else if(tablist[i++]<=n)
225
continue;
226
else
227
n = tablist[i-1]-n;
228
break;
229
}
230
}
231
if(n<=1)
232
sfputc(out,' ');
233
else
234
{
235
sfnputc(out,' ',n);
236
first -= (n-1);
237
}
238
buff = cp;
239
if(initial)
240
state[' '] = 0;
241
break;
242
case S_BS:
243
if((first+=2) > cp)
244
first = cp;
245
break;
246
case S_EOF:
247
if(cp==cpend)
248
{
249
/* end of buffer */
250
cp[-1] = savec;
251
}
252
break;
253
case S_NL:
254
first = cp;
255
if(type==0)
256
state[' '] = S_SPACE;
257
else
258
state['\t'] = S_TAB;
259
break;
260
}
261
if(cp >= cpend)
262
{
263
if((n=cp-buff)>0)
264
sfwrite(out, buff, n);
265
n = cp-first;
266
break;
267
}
268
}
269
}
270
return(errno);
271
}
272
273
int
274
main(int argc, char** argv)
275
{
276
register int n,type;
277
register char *cp;
278
register Sfio_t *fp;
279
int ntabs= -1;
280
int tabstop=8;
281
int initial=0;
282
int *tablist=&tabstop;
283
const char *usage;
284
285
if (cp = strrchr(argv[0], '/')) cp++;
286
else cp = argv[0];
287
error_info.id = cp;
288
type = *cp == 'e';
289
if(type)
290
usage = expand_usage;
291
else
292
usage = unexpand_usage;
293
while (n = optget(argv, usage)) switch (n)
294
{
295
case 't':
296
tablist = gettabs(opt_info.arg, &ntabs);
297
break;
298
case 'i':
299
initial = 1;
300
break;
301
case 'a':
302
if(ntabs < 0)
303
ntabs = 0;
304
break;
305
case ':':
306
cp = argv[opt_info.index]+1;
307
if(*cp>='0' && *cp<='9')
308
tablist = gettabs(cp, &ntabs);
309
else
310
error(2, "%s", opt_info.arg);
311
break;
312
case '?':
313
error(ERROR_usage(2), "%s", opt_info.arg);
314
break;
315
}
316
argv += opt_info.index;
317
if(ntabs<0)
318
ntabs=1;
319
else if(ntabs==1)
320
tablist[0] += 1;
321
if(error_info.errors)
322
error(ERROR_usage(2), "%s", optusage((char*)0));
323
if(cp= *argv)
324
argv++;
325
do
326
{
327
if (!cp || streq(cp,"-"))
328
fp = sfstdin;
329
else if (!(fp = sfopen(NiL, cp, "r")))
330
{
331
error(ERROR_system(0), "%s: cannot open", cp);
332
error_info.errors = 1;
333
continue;
334
}
335
if(expand(fp,sfstdout,tablist,ntabs,type,initial)<0)
336
error(ERROR_system(1), "failed");
337
if (fp != sfstdin)
338
sfclose(fp);
339
} while (cp = *argv++);
340
return(error_info.errors);
341
}
342
343