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 <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
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
donl(Nl_t * pp,Sfio_t * in,Sfio_t * out)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
b_nl(int argc,char ** argv,Shbltin_t * context)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