]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/cut/cut.c
Implement pci_enable_msi() and pci_disable_msi() in the LinuxKPI.
[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         /* reversed range with autostart */
214         if (maxval < autostart) {
215                 maxval = autostart;
216                 needpos(maxval + 1);
217         }
218
219         /* set autostart */
220         if (autostart)
221                 memset(positions + 1, '1', autostart);
222 }
223
224 static void
225 needpos(size_t n)
226 {
227         static size_t npos;
228         size_t oldnpos;
229
230         /* Grow the positions array to at least the specified size. */
231         if (n > npos) {
232                 oldnpos = npos;
233                 if (npos == 0)
234                         npos = n;
235                 while (n > npos)
236                         npos *= 2;
237                 if ((positions = realloc(positions, npos)) == NULL)
238                         err(1, "realloc");
239                 memset((char *)positions + oldnpos, 0, npos - oldnpos);
240         }
241 }
242
243 static int
244 b_cut(FILE *fp, const char *fname __unused)
245 {
246         int ch, col;
247         char *pos;
248
249         ch = 0;
250         for (;;) {
251                 pos = positions + 1;
252                 for (col = maxval; col; --col) {
253                         if ((ch = getc(fp)) == EOF)
254                                 return (0);
255                         if (ch == '\n')
256                                 break;
257                         if (*pos++)
258                                 (void)putchar(ch);
259                 }
260                 if (ch != '\n') {
261                         if (autostop)
262                                 while ((ch = getc(fp)) != EOF && ch != '\n')
263                                         (void)putchar(ch);
264                         else
265                                 while ((ch = getc(fp)) != EOF && ch != '\n');
266                 }
267                 (void)putchar('\n');
268         }
269         return (0);
270 }
271
272 /*
273  * Cut based on byte positions, taking care not to split multibyte characters.
274  * Although this function also handles the case where -n is not specified,
275  * b_cut() ought to be much faster.
276  */
277 static int
278 b_n_cut(FILE *fp, const char *fname)
279 {
280         size_t col, i, lbuflen;
281         char *lbuf;
282         int canwrite, clen, warned;
283         mbstate_t mbs;
284
285         memset(&mbs, 0, sizeof(mbs));
286         warned = 0;
287         while ((lbuf = fgetln(fp, &lbuflen)) != NULL) {
288                 for (col = 0; lbuflen > 0; col += clen) {
289                         if ((clen = mbrlen(lbuf, lbuflen, &mbs)) < 0) {
290                                 if (!warned) {
291                                         warn("%s", fname);
292                                         warned = 1;
293                                 }
294                                 memset(&mbs, 0, sizeof(mbs));
295                                 clen = 1;
296                         }
297                         if (clen == 0 || *lbuf == '\n')
298                                 break;
299                         if (col < maxval && !positions[1 + col]) {
300                                 /*
301                                  * Print the character if (1) after an initial
302                                  * segment of un-selected bytes, the rest of
303                                  * it is selected, and (2) the last byte is
304                                  * selected.
305                                  */
306                                 i = col;
307                                 while (i < col + clen && i < maxval &&
308                                     !positions[1 + i])
309                                         i++;
310                                 canwrite = i < col + clen;
311                                 for (; i < col + clen && i < maxval; i++)
312                                         canwrite &= positions[1 + i];
313                                 if (canwrite)
314                                         fwrite(lbuf, 1, clen, stdout);
315                         } else {
316                                 /*
317                                  * Print the character if all of it has
318                                  * been selected.
319                                  */
320                                 canwrite = 1;
321                                 for (i = col; i < col + clen; i++)
322                                         if ((i >= maxval && !autostop) ||
323                                             (i < maxval && !positions[1 + i])) {
324                                                 canwrite = 0;
325                                                 break;
326                                         }
327                                 if (canwrite)
328                                         fwrite(lbuf, 1, clen, stdout);
329                         }
330                         lbuf += clen;
331                         lbuflen -= clen;
332                 }
333                 if (lbuflen > 0)
334                         putchar('\n');
335         }
336         return (warned);
337 }
338
339 static int
340 c_cut(FILE *fp, const char *fname)
341 {
342         wint_t ch;
343         int col;
344         char *pos;
345
346         ch = 0;
347         for (;;) {
348                 pos = positions + 1;
349                 for (col = maxval; col; --col) {
350                         if ((ch = getwc(fp)) == WEOF)
351                                 goto out;
352                         if (ch == '\n')
353                                 break;
354                         if (*pos++)
355                                 (void)putwchar(ch);
356                 }
357                 if (ch != '\n') {
358                         if (autostop)
359                                 while ((ch = getwc(fp)) != WEOF && ch != '\n')
360                                         (void)putwchar(ch);
361                         else
362                                 while ((ch = getwc(fp)) != WEOF && ch != '\n');
363                 }
364                 (void)putwchar('\n');
365         }
366 out:
367         if (ferror(fp)) {
368                 warn("%s", fname);
369                 return (1);
370         }
371         return (0);
372 }
373
374 static int
375 is_delim(wchar_t ch)
376 {
377         if (wflag) {
378                 if (ch == ' ' || ch == '\t')
379                         return 1;
380         } else {
381                 if (ch == dchar)
382                         return 1;
383         }
384         return 0;
385 }
386
387 static int
388 f_cut(FILE *fp, const char *fname)
389 {
390         wchar_t ch;
391         int field, i, isdelim;
392         char *pos, *p;
393         int output;
394         char *lbuf, *mlbuf;
395         size_t clen, lbuflen, reallen;
396
397         mlbuf = NULL;
398         while ((lbuf = fgetln(fp, &lbuflen)) != NULL) {
399                 reallen = lbuflen;
400                 /* Assert EOL has a newline. */
401                 if (*(lbuf + lbuflen - 1) != '\n') {
402                         /* Can't have > 1 line with no trailing newline. */
403                         mlbuf = malloc(lbuflen + 1);
404                         if (mlbuf == NULL)
405                                 err(1, "malloc");
406                         memcpy(mlbuf, lbuf, lbuflen);
407                         *(mlbuf + lbuflen) = '\n';
408                         lbuf = mlbuf;
409                         reallen++;
410                 }
411                 output = 0;
412                 for (isdelim = 0, p = lbuf;; p += clen) {
413                         clen = mbrtowc(&ch, p, lbuf + reallen - p, NULL);
414                         if (clen == (size_t)-1 || clen == (size_t)-2) {
415                                 warnc(EILSEQ, "%s", fname);
416                                 free(mlbuf);
417                                 return (1);
418                         }
419                         if (clen == 0)
420                                 clen = 1;
421                         /* this should work if newline is delimiter */
422                         if (is_delim(ch))
423                                 isdelim = 1;
424                         if (ch == '\n') {
425                                 if (!isdelim && !sflag)
426                                         (void)fwrite(lbuf, lbuflen, 1, stdout);
427                                 break;
428                         }
429                 }
430                 if (!isdelim)
431                         continue;
432
433                 pos = positions + 1;
434                 for (field = maxval, p = lbuf; field; --field, ++pos) {
435                         if (*pos && output++)
436                                 for (i = 0; dcharmb[i] != '\0'; i++)
437                                         putchar(dcharmb[i]);
438                         for (;;) {
439                                 clen = mbrtowc(&ch, p, lbuf + reallen - p,
440                                     NULL);
441                                 if (clen == (size_t)-1 || clen == (size_t)-2) {
442                                         warnc(EILSEQ, "%s", fname);
443                                         free(mlbuf);
444                                         return (1);
445                                 }
446                                 if (clen == 0)
447                                         clen = 1;
448                                 p += clen;
449                                 if (ch == '\n' || is_delim(ch)) {
450                                         /* compress whitespace */
451                                         if (wflag && ch != '\n')
452                                                 while (is_delim(*p))
453                                                         p++;
454                                         break;
455                                 }
456                                 if (*pos)
457                                         for (i = 0; i < (int)clen; i++)
458                                                 putchar(p[i - clen]);
459                         }
460                         if (ch == '\n')
461                                 break;
462                 }
463                 if (ch != '\n') {
464                         if (autostop) {
465                                 if (output)
466                                         for (i = 0; dcharmb[i] != '\0'; i++)
467                                                 putchar(dcharmb[i]);
468                                 for (; (ch = *p) != '\n'; ++p)
469                                         (void)putchar(ch);
470                         } else
471                                 for (; (ch = *p) != '\n'; ++p);
472                 }
473                 (void)putchar('\n');
474         }
475         free(mlbuf);
476         return (0);
477 }
478
479 static void
480 usage(void)
481 {
482         (void)fprintf(stderr, "%s\n%s\n%s\n",
483                 "usage: cut -b list [-n] [file ...]",
484                 "       cut -c list [file ...]",
485                 "       cut -f list [-s] [-w | -d delim] [file ...]");
486         exit(1);
487 }