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