xref: /original-bsd/usr.bin/cut/cut.c (revision 8ca26665)
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  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 char 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
18 static char sccsid[] = "@(#)cut.c	5.2 (Berkeley) 06/01/90";
19 #endif /* not lint */
20 
21 #include <limits.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 
25 int	cflag;
26 char	dchar;
27 int	dflag;
28 int	fflag;
29 int	sflag;
30 
31 main(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 
89 int autostart, autostop, maxval;
90 
91 char positions[LINE_MAX + 1];
92 
93 get_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");
132 		if (stop > LINE_MAX) {
133 			/* positions used rather than allocate a new buffer */
134 			(void)sprintf(positions, "%d too large (max %d)",
135 			    stop, LINE_MAX);
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 */
153 c_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 
180 f_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;
187 	char lbuf[LINE_MAX + 1];
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 
232 badlist(msg)
233 	char *msg;
234 {
235 	(void)fprintf(stderr, "cut: [-cf] list: %s.\n", msg);
236 	exit(1);
237 }
238 
239 usage()
240 {
241 	(void)fprintf(stderr,
242 "usage:\tcut -c list [file1 ...]\n\tcut -f list [-s] [-d delim] [file ...]\n");
243 	exit(1);
244 }
245