xref: /original-bsd/usr.bin/cut/cut.c (revision 0f81f0ee)
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
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
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
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
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
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__
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