1 /*
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Adam S. Moskowitz of Menlo Consulting and Marciano Pitargue.
7 *
8 * %sccs.include.redist.c%
9 */
10
11 #ifndef lint
12 static char copyright[] =
13 "@(#) Copyright (c) 1989, 1993\n\
14 The Regents of the University of California. All rights reserved.\n";
15 #endif /* not lint */
16
17 #ifndef lint
18 static char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 05/04/95";
19 #endif /* not lint */
20
21 #include <ctype.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 int cflag;
30 char dchar;
31 int dflag;
32 int fflag;
33 int sflag;
34
35 void c_cut __P((FILE *, char *));
36 void err __P((const char *, ...));
37 void f_cut __P((FILE *, char *));
38 void get_list __P((char *));
39 void usage __P((void));
40
41 int
main(argc,argv)42 main(argc, argv)
43 int argc;
44 char *argv[];
45 {
46 FILE *fp;
47 void (*fcn) __P((FILE *, char *));
48 int ch;
49
50 dchar = '\t'; /* default delimiter is \t */
51
52 while ((ch = getopt(argc, argv, "c:d:f:s")) != EOF)
53 switch(ch) {
54 case 'c':
55 fcn = c_cut;
56 get_list(optarg);
57 cflag = 1;
58 break;
59 case 'd':
60 dchar = *optarg;
61 dflag = 1;
62 break;
63 case 'f':
64 get_list(optarg);
65 fcn = f_cut;
66 fflag = 1;
67 break;
68 case 's':
69 sflag = 1;
70 break;
71 case '?':
72 default:
73 usage();
74 }
75 argc -= optind;
76 argv += optind;
77
78 if (fflag) {
79 if (cflag)
80 usage();
81 } else if (!cflag || dflag || sflag)
82 usage();
83
84 if (*argv)
85 for (; *argv; ++argv) {
86 if (!(fp = fopen(*argv, "r")))
87 err("%s: %s\n", *argv, strerror(errno));
88 fcn(fp, *argv);
89 (void)fclose(fp);
90 }
91 else
92 fcn(stdin, "stdin");
93 exit(0);
94 }
95
96 int autostart, autostop, maxval;
97
98 char positions[_POSIX2_LINE_MAX + 1];
99
100 void
get_list(list)101 get_list(list)
102 char *list;
103 {
104 register int setautostart, start, stop;
105 register char *pos;
106 char *p;
107
108 /*
109 * set a byte in the positions array to indicate if a field or
110 * column is to be selected; use +1, it's 1-based, not 0-based.
111 * This parser is less restrictive than the Draft 9 POSIX spec.
112 * POSIX doesn't allow lists that aren't in increasing order or
113 * overlapping lists. We also handle "-3-5" although there's no
114 * real reason too.
115 */
116 for (; p = strtok(list, ", \t"); list = NULL) {
117 setautostart = start = stop = 0;
118 if (*p == '-') {
119 ++p;
120 setautostart = 1;
121 }
122 if (isdigit(*p)) {
123 start = stop = strtol(p, &p, 10);
124 if (setautostart && start > autostart)
125 autostart = start;
126 }
127 if (*p == '-') {
128 if (isdigit(p[1]))
129 stop = strtol(p + 1, &p, 10);
130 if (*p == '-') {
131 ++p;
132 if (!autostop || autostop > stop)
133 autostop = stop;
134 }
135 }
136 if (*p)
137 err("[-cf] list: illegal list value\n");
138 if (!stop || !start)
139 err("[-cf] list: values may not include zero\n");
140 if (stop > _POSIX2_LINE_MAX)
141 err("[-cf] list: %d too large (max %d)\n",
142 stop, _POSIX2_LINE_MAX);
143 if (maxval < stop)
144 maxval = stop;
145 for (pos = positions + start; start++ <= stop; *pos++ = 1);
146 }
147
148 /* overlapping ranges */
149 if (autostop && maxval > autostop)
150 maxval = autostop;
151
152 /* set autostart */
153 if (autostart)
154 memset(positions + 1, '1', autostart);
155 }
156
157 /* ARGSUSED */
158 void
c_cut(fp,fname)159 c_cut(fp, fname)
160 FILE *fp;
161 char *fname;
162 {
163 register int ch, col;
164 register char *pos;
165
166 for (;;) {
167 pos = positions + 1;
168 for (col = maxval; col; --col) {
169 if ((ch = getc(fp)) == EOF)
170 return;
171 if (ch == '\n')
172 break;
173 if (*pos++)
174 (void)putchar(ch);
175 }
176 if (ch != '\n')
177 if (autostop)
178 while ((ch = getc(fp)) != EOF && ch != '\n')
179 (void)putchar(ch);
180 else
181 while ((ch = getc(fp)) != EOF && ch != '\n');
182 (void)putchar('\n');
183 }
184 }
185
186 void
f_cut(fp,fname)187 f_cut(fp, fname)
188 FILE *fp;
189 char *fname;
190 {
191 register int ch, field, isdelim;
192 register char *pos, *p, sep;
193 int output;
194 char lbuf[_POSIX2_LINE_MAX + 1];
195
196 for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) {
197 output = 0;
198 for (isdelim = 0, p = lbuf;; ++p) {
199 if (!(ch = *p))
200 err("%s: line too long.\n", fname);
201 /* this should work if newline is delimiter */
202 if (ch == sep)
203 isdelim = 1;
204 if (ch == '\n') {
205 if (!isdelim && !sflag)
206 (void)printf("%s", lbuf);
207 break;
208 }
209 }
210 if (!isdelim)
211 continue;
212
213 pos = positions + 1;
214 for (field = maxval, p = lbuf; field; --field, ++pos) {
215 if (*pos) {
216 if (output++)
217 (void)putchar(sep);
218 while ((ch = *p++) != '\n' && ch != sep)
219 (void)putchar(ch);
220 } else
221 while ((ch = *p++) != '\n' && ch != sep);
222 if (ch == '\n')
223 break;
224 }
225 if (ch != '\n')
226 if (autostop) {
227 if (output)
228 (void)putchar(sep);
229 for (; (ch = *p) != '\n'; ++p)
230 (void)putchar(ch);
231 } else
232 for (; (ch = *p) != '\n'; ++p);
233 (void)putchar('\n');
234 }
235 }
236
237 void
usage()238 usage()
239 {
240 (void)fprintf(stderr,
241 "usage:\tcut -c list [file1 ...]\n\tcut -f list [-s] [-d delim] [file ...]\n");
242 exit(1);
243 }
244
245 #if __STDC__
246 #include <stdarg.h>
247 #else
248 #include <varargs.h>
249 #endif
250
251 void
252 #if __STDC__
err(const char * fmt,...)253 err(const char *fmt, ...)
254 #else
255 err(fmt, va_alist)
256 char *fmt;
257 va_dcl
258 #endif
259 {
260 va_list ap;
261 #if __STDC__
262 va_start(ap, fmt);
263 #else
264 va_start(ap);
265 #endif
266 (void)fprintf(stderr, "cut: ");
267 (void)vfprintf(stderr, fmt, ap);
268 va_end(ap);
269 (void)fprintf(stderr, "\n");
270 exit(1);
271 /* NOTREACHED */
272 }
273