Added gitignore entries needed to ignore derived objects generated from full build...
[bash.git] / examples / loadables / cut.c
blob47d7d3ef4af39fd59b64ebbbcc78b16880236a90
1 /*
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Adam S. Moskowitz of Menlo Consulting and Marciano Pitargue.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
37 #ifndef lint
38 static const char copyright[] =
39 "@(#) Copyright (c) 1989, 1993\n\
40 The Regents of the University of California. All rights reserved.\n";
41 #endif /* not lint */
43 #ifndef lint
44 static const char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95";
45 #endif /* not lint */
47 #include <config.h>
49 #include <ctype.h>
50 #include <stdio.h>
51 #include <errno.h>
53 #include "bashansi.h"
55 #ifdef HAVE_LIMITS_H
56 # include <limits.h>
57 #endif
59 #ifdef HAVE_UNISTD_H
60 # include <unistd.h>
61 #endif
63 #include "builtins.h"
64 #include "shell.h"
65 #include "bashgetopt.h"
66 #include "common.h"
68 #if !defined (errno)
69 extern int errno;
70 #endif
72 #if !defined (_POSIX2_LINE_MAX)
73 # define _POSIX2_LINE_MAX 2048
74 #endif
76 static int cflag;
77 static char dchar;
78 static int dflag;
79 static int fflag;
80 static int sflag;
82 static int autostart, autostop, maxval;
83 static char positions[_POSIX2_LINE_MAX + 1];
85 static int c_cut __P((FILE *, char *));
86 static int f_cut __P((FILE *, char *));
87 static int get_list __P((char *));
88 static char *_cut_strsep __P((char **, const char *));
90 int
91 cut_builtin(list)
92 WORD_LIST *list;
94 FILE *fp;
95 int (*fcn) __P((FILE *, char *)) = NULL;
96 int ch;
98 fcn = NULL;
99 dchar = '\t'; /* default delimiter is \t */
101 /* Since we don't support multi-byte characters, the -c and -b
102 options are equivalent, and the -n option is meaningless. */
103 reset_internal_getopt ();
104 while ((ch = internal_getopt (list, "b:c:d:f:sn")) != -1)
105 switch(ch) {
106 case 'b':
107 case 'c':
108 fcn = c_cut;
109 if (get_list(list_optarg) < 0)
110 return (EXECUTION_FAILURE);
111 cflag = 1;
112 break;
113 case 'd':
114 dchar = *list_optarg;
115 dflag = 1;
116 break;
117 case 'f':
118 fcn = f_cut;
119 if (get_list(list_optarg) < 0)
120 return (EXECUTION_FAILURE);
121 fflag = 1;
122 break;
123 case 's':
124 sflag = 1;
125 break;
126 case 'n':
127 break;
128 case '?':
129 default:
130 builtin_usage();
131 return (EX_USAGE);
134 list = loptend;
136 if (fflag) {
137 if (cflag) {
138 builtin_usage();
139 return (EX_USAGE);
141 } else if (!cflag || dflag || sflag) {
142 builtin_usage();
143 return (EX_USAGE);
146 if (list) {
147 while (list) {
148 fp = fopen(list->word->word, "r");
149 if (fp == 0) {
150 builtin_error("%s", list->word->word);
151 return (EXECUTION_FAILURE);
153 ch = (*fcn)(fp, list->word->word);
154 (void)fclose(fp);
155 if (ch < 0)
156 return (EXECUTION_FAILURE);
157 list = list->next;
159 } else {
160 ch = (*fcn)(stdin, "stdin");
161 if (ch < 0)
162 return (EXECUTION_FAILURE);
165 return (EXECUTION_SUCCESS);
168 static int
169 get_list(list)
170 char *list;
172 int setautostart, start, stop;
173 char *pos;
174 char *p;
177 * set a byte in the positions array to indicate if a field or
178 * column is to be selected; use +1, it's 1-based, not 0-based.
179 * This parser is less restrictive than the Draft 9 POSIX spec.
180 * POSIX doesn't allow lists that aren't in increasing order or
181 * overlapping lists. We also handle "-3-5" although there's no
182 * real reason too.
184 for (; (p = _cut_strsep(&list, ", \t")) != NULL;) {
185 setautostart = start = stop = 0;
186 if (*p == '-') {
187 ++p;
188 setautostart = 1;
190 if (isdigit((unsigned char)*p)) {
191 start = stop = strtol(p, &p, 10);
192 if (setautostart && start > autostart)
193 autostart = start;
195 if (*p == '-') {
196 if (isdigit((unsigned char)p[1]))
197 stop = strtol(p + 1, &p, 10);
198 if (*p == '-') {
199 ++p;
200 if (!autostop || autostop > stop)
201 autostop = stop;
204 if (*p) {
205 builtin_error("[-cf] list: illegal list value");
206 return -1;
208 if (!stop || !start) {
209 builtin_error("[-cf] list: values may not include zero");
210 return -1;
212 if (stop > _POSIX2_LINE_MAX) {
213 builtin_error("[-cf] list: %d too large (max %d)",
214 stop, _POSIX2_LINE_MAX);
215 return -1;
217 if (maxval < stop)
218 maxval = stop;
219 for (pos = positions + start; start++ <= stop; *pos++ = 1);
222 /* overlapping ranges */
223 if (autostop && maxval > autostop)
224 maxval = autostop;
226 /* set autostart */
227 if (autostart)
228 memset(positions + 1, '1', autostart);
230 return 0;
233 /* ARGSUSED */
234 static int
235 c_cut(fp, fname)
236 FILE *fp;
237 char *fname;
239 int ch, col;
240 char *pos;
242 ch = 0;
243 for (;;) {
244 pos = positions + 1;
245 for (col = maxval; col; --col) {
246 if ((ch = getc(fp)) == EOF)
247 return;
248 if (ch == '\n')
249 break;
250 if (*pos++)
251 (void)putchar(ch);
253 if (ch != '\n') {
254 if (autostop)
255 while ((ch = getc(fp)) != EOF && ch != '\n')
256 (void)putchar(ch);
257 else
258 while ((ch = getc(fp)) != EOF && ch != '\n');
260 (void)putchar('\n');
262 return (0);
265 static int
266 f_cut(fp, fname)
267 FILE *fp;
268 char *fname;
270 int ch, field, isdelim;
271 char *pos, *p, sep;
272 int output;
273 char lbuf[_POSIX2_LINE_MAX + 1];
275 for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) {
276 output = 0;
277 for (isdelim = 0, p = lbuf;; ++p) {
278 if (!(ch = *p)) {
279 builtin_error("%s: line too long.", fname);
280 return -1;
282 /* this should work if newline is delimiter */
283 if (ch == sep)
284 isdelim = 1;
285 if (ch == '\n') {
286 if (!isdelim && !sflag)
287 (void)printf("%s", lbuf);
288 break;
291 if (!isdelim)
292 continue;
294 pos = positions + 1;
295 for (field = maxval, p = lbuf; field; --field, ++pos) {
296 if (*pos) {
297 if (output++)
298 (void)putchar(sep);
299 while ((ch = *p++) != '\n' && ch != sep)
300 (void)putchar(ch);
301 } else {
302 while ((ch = *p++) != '\n' && ch != sep)
303 continue;
305 if (ch == '\n')
306 break;
308 if (ch != '\n') {
309 if (autostop) {
310 if (output)
311 (void)putchar(sep);
312 for (; (ch = *p) != '\n'; ++p)
313 (void)putchar(ch);
314 } else
315 for (; (ch = *p) != '\n'; ++p);
317 (void)putchar('\n');
319 return (0);
323 * Get next token from string *stringp, where tokens are possibly-empty
324 * strings separated by characters from delim.
326 * Writes NULs into the string at *stringp to end tokens.
327 * delim need not remain constant from call to call.
328 * On return, *stringp points past the last NUL written (if there might
329 * be further tokens), or is NULL (if there are definitely no more tokens).
331 * If *stringp is NULL, strsep returns NULL.
333 static char *
334 _cut_strsep(stringp, delim)
335 register char **stringp;
336 register const char *delim;
338 register char *s;
339 register const char *spanp;
340 register int c, sc;
341 char *tok;
343 if ((s = *stringp) == NULL)
344 return (NULL);
345 for (tok = s;;) {
346 c = *s++;
347 spanp = delim;
348 do {
349 if ((sc = *spanp++) == c) {
350 if (c == 0)
351 s = NULL;
352 else
353 s[-1] = 0;
354 *stringp = s;
355 return (tok);
357 } while (sc != 0);
359 /* NOTREACHED */
362 static char *cut_doc[] = {
363 "Select portions of lines.",
365 "Select portions of each line (as specified by LIST) from each FILE",
366 "(by default, the standard input), and write them to the standard output.",
367 "Items specified by LIST are either column positions or fields delimited",
368 "by a special character. Column numbering starts at 1.",
369 (char *)0
372 struct builtin cut_struct = {
373 "cut",
374 cut_builtin,
375 BUILTIN_ENABLED,
376 cut_doc,
377 "cut -b list [-n] [file ...] OR cut -c list [file ...] OR cut -f list [-s] [-d delim] [file ...]",