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