Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/builtin/nl.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1992-2012 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
* David Korn <[email protected]> *
19
* *
20
***********************************************************************/
21
#pragma prototyped
22
/*
23
* nl.c
24
* Written by David Korn
25
* AT&T Labs
26
* Mon Mar 24 10:10:10 EST 2003
27
*/
28
29
30
static const char usage[] =
31
"[-?\n@(#)$Id: nl (AT&T Research) 2003-03-24 $\n]"
32
USAGE_LICENSE
33
"[+NAME? nl - line numbering filter ]"
34
"[+DESCRIPTION?\bnl\b reads lines from the file \afile\a or from standard "
35
"input if \afile\a is omitted or is \b-\b, and writes the lines to "
36
"standard output with possible line numbering preceding each line.]"
37
"[+?The \bnl\b utility treats its input in terms of logical pages and resets "
38
"numbering on each logical page. A logical page consists of a header, "
39
"a body, and a footer any of which can be empty, and each can use "
40
"their own line numbering options.]"
41
"[+?The start of logical page sections consist of input lines containing only "
42
"the two delimiter characters whose defaults are \b\\\b and \b:\b as "
43
"follows:]{"
44
"[d1d2d1d2d1d2?Header.]"
45
"[d1d2d1d2?Body.]"
46
"[d1d2?Footer.]"
47
"}"
48
"[+?\bnl\b assumes that the first section is a logical body until it encounters "
49
"one of the section delimiters.]"
50
""
51
"[b:body-numbering]:[type:=t?\atype\a can be one of the following:]{"
52
"[+a?Number all lines.]"
53
"[+t?Number only non-empty lines.]"
54
"[+n?No line numbering.]"
55
"[+p\astring\a?Number only lines that contain the basic "
56
"regular expression \astring\a.]"
57
"}"
58
"[d:section-delimiter]:[delim:=\\::?\adelim\a specifies the two delimiter "
59
"characters that indicate start of a section. If only one character "
60
"is specified, the second character remains unchanged.]"
61
"[f:footer-numbering]:[type:=n?\atype\a is the same as for the \bb\b option.]"
62
"[h:header-numbering]:[type:=n?\atype\a is the same as for the \bb\b option.]"
63
"[i:page-increment]#[incr:=1?\aincr\a is the increment used to number logical "
64
"page lines.]"
65
"[l:join-blank-lines]#[num:=1?\anum\a is the number of blank lines to be "
66
"treated as one]"
67
"[n:number-format]:[format?\aformat\a specifies the line numbering format. "
68
"It must be one of the following:]{"
69
"[101:ln?left justified, leading zeros supressed.]"
70
"[102:rn?right justified, leading zeros supressed.]"
71
"[103:rz?right justified, leading zeros kept.]"
72
"}"
73
"[p:no-renumber?Start renumbering at logical page delimiters.]"
74
"[s:number-separator]:[sep:=\\t?\asep\a is the string that is used to separate the line number "
75
"and the corresponding text line.]"
76
"[v:starting-line-number]#[startnum:=1?\astartnum\a is the initial value to number "
77
"logical pages.]"
78
"[w:number-width]#[width:=6?\awidth\a is the number of characters to be used "
79
"for line numbering.]"
80
81
"\n"
82
"\n[file]\n"
83
"\n"
84
"[+EXIT STATUS?]{"
85
"[+0?Success.]"
86
"[+>0?An error occurred.]"
87
"}"
88
"[+SEE ALSO?\bpr\b(1), \bregex\b(3)]"
89
;
90
91
#include <cmd.h>
92
#include <regex.h>
93
94
typedef struct _nl_
95
{
96
void *section[3];
97
char *sep;
98
int delim1;
99
int delim2;
100
int format;
101
int startnum;
102
int incr;
103
int width;
104
int blines;
105
int pflag;
106
} Nl_t;
107
108
static const char letter_a, letter_t, letter_n;
109
#define TYPE_ALL ((void*)(&letter_a))
110
#define TYPE_NONE ((void*)(&letter_n))
111
#define TYPE_TEXT ((void*)(&letter_t))
112
113
114
#define SECTION_HEAD 0
115
#define SECTION_BODY 1
116
#define SECTION_FOOT 2
117
118
/* These numbers need to be the same as with -n option in option string */
119
#define FORMAT_LN -101
120
#define FORMAT_RN -102
121
#define FORMAT_RZ -103
122
123
static int donl(Nl_t *pp, Sfio_t *in, Sfio_t *out)
124
{
125
register char *cp;
126
register int n,line=pp->startnum, outline, sectnum=SECTION_BODY;
127
int blank=0, width=pp->width+strlen(pp->sep);
128
char format[20];
129
if(pp->format==FORMAT_LN)
130
sfsprintf(format,sizeof(format),"%%-%dd%%s",pp->width);
131
else if(pp->format==FORMAT_RN)
132
sfsprintf(format,sizeof(format),"%%%dd%%s",pp->width);
133
else
134
sfsprintf(format,sizeof(format),"%%0%dd%%s",pp->width);
135
while(cp=sfgetr(in, '\n', 0))
136
{
137
outline = 0;
138
if(*cp!='\n')
139
blank = 0;
140
else
141
blank++;
142
n = sfvalue(in);
143
if(*cp==pp->delim1 && cp[1]==pp->delim2)
144
{
145
if(cp[2]=='\n')
146
{
147
sectnum = SECTION_FOOT;
148
sfputc(out,'\n');
149
continue;
150
}
151
else if(cp[2]==pp->delim1 && cp[3]==pp->delim2)
152
{
153
if(cp[4]=='\n')
154
{
155
sectnum = SECTION_BODY;
156
sfputc(out,'\n');
157
continue;
158
}
159
if(cp[4]==pp->delim1 && cp[5]==pp->delim2 && cp[6]=='\n')
160
{
161
sectnum = SECTION_HEAD;
162
if(!pp->pflag)
163
line = pp->startnum;
164
sfputc(out,'\n');
165
continue;
166
}
167
}
168
}
169
if(pp->section[sectnum]==TYPE_NONE)
170
;
171
#if 0
172
{
173
sfwrite(out, cp, n);
174
continue;
175
}
176
#endif
177
else if(pp->section[sectnum]==TYPE_ALL)
178
{
179
if(!blank || blank==pp->blines)
180
{
181
outline = 1;
182
blank = 0;
183
}
184
}
185
else if(pp->section[sectnum]!=TYPE_TEXT)
186
outline = !regnexec((regex_t*)pp->section[sectnum], cp, n, (size_t)0, NULL, 0);
187
else if(*cp!='\n')
188
outline = 1;
189
if(outline)
190
{
191
blank = 0;
192
sfprintf(out, format, line, pp->sep);
193
line += pp->incr;
194
}
195
else
196
sfnputc(out,' ',width);
197
sfwrite(out, cp, n);
198
}
199
return(0);
200
}
201
202
int
203
b_nl(int argc, char** argv, Shbltin_t* context)
204
{
205
register int n,m;
206
Sfio_t *in=sfstdin;
207
Nl_t nl;
208
regex_t re[3];
209
210
cmdinit(argc, argv, context, (const char*)0, 0);
211
nl.width = 6;
212
nl.startnum = 1;
213
nl.blines = 1;
214
nl.incr = 1;
215
nl.delim1 = '\\';
216
nl.delim2 = ':';
217
nl.section[SECTION_BODY] = TYPE_TEXT;
218
nl.section[SECTION_HEAD] = TYPE_NONE;
219
nl.section[SECTION_FOOT] = TYPE_NONE;
220
nl.format = FORMAT_RN;
221
nl.sep = "\t";
222
nl.pflag = 0;
223
224
while (n = optget(argv, usage)) switch (n)
225
{
226
case 'p':
227
nl.pflag |= 1;
228
break;
229
case 'd':
230
nl.delim1 = *opt_info.arg;
231
if(opt_info.arg[1])
232
nl.delim2 = opt_info.arg[1];
233
break;
234
case 'f': case 'b': case 'h':
235
if(n=='h')
236
m = SECTION_HEAD;
237
else if(n=='b')
238
m = SECTION_BODY;
239
else
240
m = SECTION_FOOT;
241
switch(*opt_info.arg)
242
{
243
case 'a':
244
nl.section[m] = TYPE_ALL;
245
break;
246
case 't':
247
nl.section[m] = TYPE_TEXT;
248
break;
249
case 'n':
250
nl.section[m] = TYPE_NONE;
251
break;
252
case 'p':
253
nl.section[m] = &re[m];
254
if(regcomp(&re[m], opt_info.arg+1, REG_NOSUB))
255
error(2, "-%c: invalid basic regular expression %s", n, opt_info.arg+1);
256
break;
257
}
258
break;
259
case 'i':
260
if((nl.incr=opt_info.num) < 0)
261
error(2, "-%c: option requires non-negative number", n);
262
break;
263
case 'l':
264
if((nl.blines=opt_info.num) < 0)
265
error(2, "-%c: option requires non-negative number", n);
266
break;
267
case 'n':
268
nl.format = opt_info.num;
269
break;
270
case 's':
271
nl.sep = opt_info.arg;
272
break;
273
case 'v':
274
nl.startnum = opt_info.num;
275
break;
276
case 'w':
277
if((nl.width=opt_info.num) < 0)
278
error(2, "-%c: option requires non-negative number", n);
279
break;
280
case ':':
281
error(2, "%s", opt_info.arg);
282
break;
283
case '?':
284
error(ERROR_usage(2), "%s", opt_info.arg);
285
break;
286
}
287
argv += opt_info.index;
288
argc -= opt_info.index;
289
if(argc>1 || error_info.errors)
290
error(ERROR_usage(2),"%s",optusage((char*)0));
291
if(*argv && !(in = sfopen((Sfio_t*)0, *argv, "r")))
292
error(ERROR_system(1),"%s: cannot open for reading",*argv);
293
n = donl(&nl,in,sfstdout);
294
for(m=0; m < 3; m++)
295
{
296
if(nl.section[m] == (void*)&re[m])
297
regfree(&re[m]);
298
}
299
return(n?1:0);
300
}
301
302