]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - usr.bin/cut/cut.c
MFC r243320,r243474:
[FreeBSD/stable/8.git] / usr.bin / cut / cut.c
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  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #ifndef lint
38 static const char copyright[] =
39 "@(#) Copyright (c) 1989, 1993\n\
40         The Regents of the University of California.  All rights reserved.\n";
41 static const char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95";
42 #endif /* not lint */
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
46 #include <ctype.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <limits.h>
50 #include <locale.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include <wchar.h>
56
57 static int      bflag;
58 static int      cflag;
59 static wchar_t  dchar;
60 static char     dcharmb[MB_LEN_MAX + 1];
61 static int      dflag;
62 static int      fflag;
63 static int      nflag;
64 static int      sflag;
65 static int      wflag;
66
67 static size_t   autostart, autostop, maxval;
68 static char *   positions;
69
70 static int      b_cut(FILE *, const char *);
71 static int      b_n_cut(FILE *, const char *);
72 static int      c_cut(FILE *, const char *);
73 static int      f_cut(FILE *, const char *);
74 static void     get_list(char *);
75 static int      is_delim(wchar_t);
76 static void     needpos(size_t);
77 static void     usage(void);
78
79 int
80 main(int argc, char *argv[])
81 {
82         FILE *fp;
83         int (*fcn)(FILE *, const char *);
84         int ch, rval;
85         size_t n;
86
87         setlocale(LC_ALL, "");
88
89         fcn = NULL;
90         dchar = '\t';                   /* default delimiter is \t */
91         strcpy(dcharmb, "\t");
92
93         while ((ch = getopt(argc, argv, "b:c:d:f:snw")) != -1)
94                 switch(ch) {
95                 case 'b':
96                         get_list(optarg);
97                         bflag = 1;
98                         break;
99                 case 'c':
100                         get_list(optarg);
101                         cflag = 1;
102                         break;
103                 case 'd':
104                         n = mbrtowc(&dchar, optarg, MB_LEN_MAX, NULL);
105                         if (dchar == '\0' || n != strlen(optarg))
106                                 errx(1, "bad delimiter");
107                         strcpy(dcharmb, optarg);
108                         dflag = 1;
109                         break;
110                 case 'f':
111                         get_list(optarg);
112                         fflag = 1;
113                         break;
114                 case 's':
115                         sflag = 1;
116                         break;
117                 case 'n':
118                         nflag = 1;
119                         break;
120                 case 'w':
121                         wflag = 1;
122                         break;
123                 case '?':
124                 default:
125                         usage();
126                 }
127         argc -= optind;
128         argv += optind;
129
130         if (fflag) {
131                 if (bflag || cflag || nflag || (wflag && dflag))
132                         usage();
133         } else if (!(bflag || cflag) || dflag || sflag || wflag)
134                 usage();
135         else if (!bflag && nflag)
136                 usage();
137
138         if (fflag)
139                 fcn = f_cut;
140         else if (cflag)
141                 fcn = MB_CUR_MAX > 1 ? c_cut : b_cut;
142         else if (bflag)
143                 fcn = nflag && MB_CUR_MAX > 1 ? b_n_cut : b_cut;
144
145         rval = 0;
146         if (*argv)
147                 for (; *argv; ++argv) {
148                         if (strcmp(*argv, "-") == 0)
149                                 rval |= fcn(stdin, "stdin");
150                         else {
151                                 if (!(fp = fopen(*argv, "r"))) {
152                                         warn("%s", *argv);
153                                         rval = 1;
154                                         continue;
155                                 }
156                                 fcn(fp, *argv);
157                                 (void)fclose(fp);
158                         }
159                 }
160         else
161                 rval = fcn(stdin, "stdin");
162         exit(rval);
163 }
164
165 static void
166 get_list(char *list)
167 {
168         size_t setautostart, start, stop;
169         char *pos;
170         char *p;
171
172         /*
173          * set a byte in the positions array to indicate if a field or
174          * column is to be selected; use +1, it's 1-based, not 0-based.
175          * Numbers and number ranges may be overlapping, repeated, and in
176          * any order. We handle "-3-5" although there's no real reason to.
177          */
178         for (; (p = strsep(&list, ", \t")) != NULL;) {
179                 setautostart = start = stop = 0;
180                 if (*p == '-') {
181                         ++p;
182                         setautostart = 1;
183                 }
184                 if (isdigit((unsigned char)*p)) {
185                         start = stop = strtol(p, &p, 10);
186                         if (setautostart && start > autostart)
187                                 autostart = start;
188                 }
189                 if (*p == '-') {
190                         if (isdigit((unsigned char)p[1]))
191                                 stop = strtol(p + 1, &p, 10);
192                         if (*p == '-') {
193                                 ++p;
194                                 if (!autostop || autostop > stop)
195                                         autostop = stop;
196                         }
197                 }
198                 if (*p)
199                         errx(1, "[-bcf] list: illegal list value");
200                 if (!stop || !start)
201                         errx(1, "[-bcf] list: values may not include zero");
202                 if (maxval < stop) {
203                         maxval = stop;
204                         needpos(maxval + 1);
205                 }
206                 for (pos = positions + start; start++ <= stop; *pos++ = 1);
207         }
208
209         /* overlapping ranges */
210         if (autostop && maxval > autostop) {
211                 maxval = autostop;
212                 needpos(maxval + 1);
213         }
214
215         /* set autostart */
216         if (autostart)
217                 memset(positions + 1, '1', autostart);
218 }
219
220 static void
221 needpos(size_t n)
222 {
223         static size_t npos;
224         size_t oldnpos;
225
226         /* Grow the positions array to at least the specified size. */
227         if (n > npos) {
228                 oldnpos = npos;
229                 if (npos == 0)
230                         npos = n;
231                 while (n > npos)
232                         npos *= 2;
233                 if ((positions = realloc(positions, npos)) == NULL)
234                         err(1, "realloc");
235                 memset((char *)positions + oldnpos, 0, npos - oldnpos);
236         }
237 }
238
239 static int
240 b_cut(FILE *fp, const char *fname __unused)
241 {
242         int ch, col;
243         char *pos;
244
245         ch = 0;
246         for (;;) {
247                 pos = positions + 1;
248                 for (col = maxval; col; --col) {
249                         if ((ch = getc(fp)) == EOF)
250                                 return (0);
251                         if (ch == '\n')
252                                 break;
253                         if (*pos++)
254                                 (void)putchar(ch);
255                 }
256                 if (ch != '\n') {
257                         if (autostop)
258                                 while ((ch = getc(fp)) != EOF && ch != '\n')
259                                         (void)putchar(ch);
260                         else
261                                 while ((ch = getc(fp)) != EOF && ch != '\n');
262                 }
263                 (void)putchar('\n');
264         }
265         return (0);
266 }
267
268 /*
269  * Cut based on byte positions, taking care not to split multibyte characters.
270  * Although this function also handles the case where -n is not specified,
271  * b_cut() ought to be much faster.
272  */
273 static int
274 b_n_cut(FILE *fp, const char *fname)
275 {
276         size_t col, i, lbuflen;
277         char *lbuf;
278         int canwrite, clen, warned;
279         mbstate_t mbs;
280
281         memset(&mbs, 0, sizeof(mbs));
282         warned = 0;
283         while ((lbuf = fgetln(fp, &lbuflen)) != NULL) {
284                 for (col = 0; lbuflen > 0; col += clen) {
285                         if ((clen = mbrlen(lbuf, lbuflen, &mbs)) < 0) {
286                                 if (!warned) {
287                                         warn("%s", fname);
288                                         warned = 1;
289                                 }
290                                 memset(&mbs, 0, sizeof(mbs));
291                                 clen = 1;
292                         }
293                         if (clen == 0 || *lbuf == '\n')
294                                 break;
295                         if (col < maxval && !positions[1 + col]) {
296                                 /*
297                                  * Print the character if (1) after an initial
298                                  * segment of un-selected bytes, the rest of
299                                  * it is selected, and (2) the last byte is
300                                  * selected.
301                                  */
302                                 i = col;
303                                 while (i < col + clen && i < maxval &&
304                                     !positions[1 + i])
305                                         i++;
306                                 canwrite = i < col + clen;
307                                 for (; i < col + clen && i < maxval; i++)
308                                         canwrite &= positions[1 + i];
309                                 if (canwrite)
310                                         fwrite(lbuf, 1, clen, stdout);
311                         } else {
312                                 /*
313                                  * Print the character if all of it has
314                                  * been selected.
315                                  */
316                                 canwrite = 1;
317                                 for (i = col; i < col + clen; i++)
318                                         if ((i >= maxval && !autostop) ||
319                                             (i < maxval && !positions[1 + i])) {
320                                                 canwrite = 0;
321                                                 break;
322                                         }
323                                 if (canwrite)
324                                         fwrite(lbuf, 1, clen, stdout);
325                         }
326                         lbuf += clen;
327                         lbuflen -= clen;
328                 }
329                 if (lbuflen > 0)
330                         putchar('\n');
331         }
332         return (warned);
333 }
334
335 static int
336 c_cut(FILE *fp, const char *fname)
337 {
338         wint_t ch;
339         int col;
340         char *pos;
341
342         ch = 0;
343         for (;;) {
344                 pos = positions + 1;
345                 for (col = maxval; col; --col) {
346                         if ((ch = getwc(fp)) == WEOF)
347                                 goto out;
348                         if (ch == '\n')
349                                 break;
350                         if (*pos++)
351                                 (void)putwchar(ch);
352                 }
353                 if (ch != '\n') {
354                         if (autostop)
355                                 while ((ch = getwc(fp)) != WEOF && ch != '\n')
356                                         (void)putwchar(ch);
357                         else
358                                 while ((ch = getwc(fp)) != WEOF && ch != '\n');
359                 }
360                 (void)putwchar('\n');
361         }
362 out:
363         if (ferror(fp)) {
364                 warn("%s", fname);
365                 return (1);
366         }
367         return (0);
368 }
369
370 static int
371 is_delim(wchar_t ch)
372 {
373         if (wflag) {
374                 if (ch == ' ' || ch == '\t')
375                         return 1;
376         } else {
377                 if (ch == dchar)
378                         return 1;
379         }
380         return 0;
381 }
382
383 static int
384 f_cut(FILE *fp, const char *fname)
385 {
386         wchar_t ch;
387         int field, i, isdelim;
388         char *pos, *p;
389         int output;
390         char *lbuf, *mlbuf;
391         size_t clen, lbuflen, reallen;
392
393         mlbuf = NULL;
394         while ((lbuf = fgetln(fp, &lbuflen)) != NULL) {
395                 reallen = lbuflen;
396                 /* Assert EOL has a newline. */
397                 if (*(lbuf + lbuflen - 1) != '\n') {
398                         /* Can't have > 1 line with no trailing newline. */
399                         mlbuf = malloc(lbuflen + 1);
400                         if (mlbuf == NULL)
401                                 err(1, "malloc");
402                         memcpy(mlbuf, lbuf, lbuflen);
403                         *(mlbuf + lbuflen) = '\n';
404                         lbuf = mlbuf;
405                         reallen++;
406                 }
407                 output = 0;
408                 for (isdelim = 0, p = lbuf;; p += clen) {
409                         clen = mbrtowc(&ch, p, lbuf + reallen - p, NULL);
410                         if (clen == (size_t)-1 || clen == (size_t)-2) {
411                                 warnc(EILSEQ, "%s", fname);
412                                 free(mlbuf);
413                                 return (1);
414                         }
415                         if (clen == 0)
416                                 clen = 1;
417                         /* this should work if newline is delimiter */
418                         if (is_delim(ch))
419                                 isdelim = 1;
420                         if (ch == '\n') {
421                                 if (!isdelim && !sflag)
422                                         (void)fwrite(lbuf, lbuflen, 1, stdout);
423                                 break;
424                         }
425                 }
426                 if (!isdelim)
427                         continue;
428
429                 pos = positions + 1;
430                 for (field = maxval, p = lbuf; field; --field, ++pos) {
431                         if (*pos && output++)
432                                 for (i = 0; dcharmb[i] != '\0'; i++)
433                                         putchar(dcharmb[i]);
434                         for (;;) {
435                                 clen = mbrtowc(&ch, p, lbuf + reallen - p,
436                                     NULL);
437                                 if (clen == (size_t)-1 || clen == (size_t)-2) {
438                                         warnc(EILSEQ, "%s", fname);
439                                         free(mlbuf);
440                                         return (1);
441                                 }
442                                 if (clen == 0)
443                                         clen = 1;
444                                 p += clen;
445                                 if (ch == '\n' || is_delim(ch)) {
446                                         /* compress whitespace */
447                                         if (wflag && ch != '\n')
448                                                 while (is_delim(*p))
449                                                         p++;
450                                         break;
451                                 }
452                                 if (*pos)
453                                         for (i = 0; i < (int)clen; i++)
454                                                 putchar(p[i - clen]);
455                         }
456                         if (ch == '\n')
457                                 break;
458                 }
459                 if (ch != '\n') {
460                         if (autostop) {
461                                 if (output)
462                                         for (i = 0; dcharmb[i] != '\0'; i++)
463                                                 putchar(dcharmb[i]);
464                                 for (; (ch = *p) != '\n'; ++p)
465                                         (void)putchar(ch);
466                         } else
467                                 for (; (ch = *p) != '\n'; ++p);
468                 }
469                 (void)putchar('\n');
470         }
471         free(mlbuf);
472         return (0);
473 }
474
475 static void
476 usage(void)
477 {
478         (void)fprintf(stderr, "%s\n%s\n%s\n",
479                 "usage: cut -b list [-n] [file ...]",
480                 "       cut -c list [file ...]",
481                 "       cut -f list [-s] [-w | -d delim] [file ...]");
482         exit(1);
483 }