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