~mmach/netext73/busybox

« back to all changes in this revision

Viewing changes to coreutils/wc.c

  • Committer: mmach
  • Date: 2021-04-14 13:54:24 UTC
  • Revision ID: netbit73@gmail.com-20210414135424-8x3fxf716zs4wflb
1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vi: set sw=4 ts=4: */
 
2
/*
 
3
 * wc implementation for busybox
 
4
 *
 
5
 * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
 
6
 *
 
7
 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 
8
 */
 
9
/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
 
10
 *
 
11
 * Rewritten to fix a number of problems and do some size optimizations.
 
12
 * Problems in the previous busybox implementation (besides bloat) included:
 
13
 *  1) broken 'wc -c' optimization (read note below)
 
14
 *  2) broken handling of '-' args
 
15
 *  3) no checking of ferror on EOF returns
 
16
 *  4) isprint() wasn't considered when word counting.
 
17
 *
 
18
 * NOTES:
 
19
 *
 
20
 * The previous busybox wc attempted an optimization using stat for the
 
21
 * case of counting chars only.  I omitted that because it was broken.
 
22
 * It didn't take into account the possibility of input coming from a
 
23
 * pipe, or input from a file with file pointer not at the beginning.
 
24
 *
 
25
 * To implement such a speed optimization correctly, not only do you
 
26
 * need the size, but also the file position.  Note also that the
 
27
 * file position may be past the end of file.  Consider the example
 
28
 * (adapted from example in gnu wc.c)
 
29
 *
 
30
 *      echo hello > /tmp/testfile &&
 
31
 *      (dd ibs=1k skip=1 count=0 &> /dev/null; wc -c) < /tmp/testfile
 
32
 *
 
33
 * for which 'wc -c' should output '0'.
 
34
 */
 
35
//config:config WC
 
36
//config:       bool "wc (4.5 kb)"
 
37
//config:       default y
 
38
//config:       help
 
39
//config:       wc is used to print the number of bytes, words, and lines,
 
40
//config:       in specified files.
 
41
//config:
 
42
//config:config FEATURE_WC_LARGE
 
43
//config:       bool "Support very large counts"
 
44
//config:       default y
 
45
//config:       depends on WC
 
46
//config:       help
 
47
//config:       Use "unsigned long long" for counter variables.
 
48
 
 
49
//applet:IF_WC(APPLET(wc, BB_DIR_USR_BIN, BB_SUID_DROP))
 
50
 
 
51
//kbuild:lib-$(CONFIG_WC) += wc.o
 
52
 
 
53
/* BB_AUDIT SUSv3 compliant. */
 
54
/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */
 
55
 
 
56
#include "libbb.h"
 
57
#include "unicode.h"
 
58
 
 
59
#if !ENABLE_LOCALE_SUPPORT
 
60
# undef isprint
 
61
# undef isspace
 
62
# define isprint(c) ((unsigned)((c) - 0x20) <= (0x7e - 0x20))
 
63
# define isspace(c) ((c) == ' ')
 
64
#endif
 
65
 
 
66
#if ENABLE_FEATURE_WC_LARGE
 
67
# define COUNT_T unsigned long long
 
68
# define COUNT_FMT "llu"
 
69
#else
 
70
# define COUNT_T unsigned
 
71
# define COUNT_FMT "u"
 
72
#endif
 
73
 
 
74
/* We support -m even when UNICODE_SUPPORT is off,
 
75
 * we just don't advertise it in help text,
 
76
 * since it is the same as -c in this case.
 
77
 */
 
78
 
 
79
//usage:#define wc_trivial_usage
 
80
//usage:       "[-c"IF_UNICODE_SUPPORT("m")"lwL] [FILE]..."
 
81
//usage:
 
82
//usage:#define wc_full_usage "\n\n"
 
83
//usage:       "Count lines, words, and bytes for each FILE (or stdin)\n"
 
84
//usage:     "\n        -c      Count bytes"
 
85
//usage:        IF_UNICODE_SUPPORT(
 
86
//usage:     "\n        -m      Count characters"
 
87
//usage:        )
 
88
//usage:     "\n        -l      Count newlines"
 
89
//usage:     "\n        -w      Count words"
 
90
//usage:     "\n        -L      Print longest line length"
 
91
//usage:
 
92
//usage:#define wc_example_usage
 
93
//usage:       "$ wc /etc/passwd\n"
 
94
//usage:       "     31      46    1365 /etc/passwd\n"
 
95
 
 
96
/* Order is important if we want to be compatible with
 
97
 * column order in "wc -cmlwL" output:
 
98
 */
 
99
enum {
 
100
        WC_LINES    = 0, /* -l */
 
101
        WC_WORDS    = 1, /* -w */
 
102
        WC_UNICHARS = 2, /* -m */
 
103
        WC_BYTES    = 3, /* -c */
 
104
        WC_LENGTH   = 4, /* -L */
 
105
        NUM_WCS     = 5,
 
106
};
 
107
 
 
108
int wc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 
109
int wc_main(int argc UNUSED_PARAM, char **argv)
 
110
{
 
111
        const char *arg;
 
112
        const char *start_fmt = " %9"COUNT_FMT + 1;
 
113
        const char *fname_fmt = " %s\n";
 
114
        COUNT_T *pcounts;
 
115
        COUNT_T counts[NUM_WCS];
 
116
        COUNT_T totals[NUM_WCS];
 
117
        int num_files;
 
118
        smallint status = EXIT_SUCCESS;
 
119
        unsigned print_type;
 
120
 
 
121
        init_unicode();
 
122
 
 
123
        print_type = getopt32(argv, "lwmcL");
 
124
 
 
125
        if (print_type == 0) {
 
126
                print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_BYTES);
 
127
        }
 
128
 
 
129
        argv += optind;
 
130
        if (!argv[0]) {
 
131
                *--argv = (char *) bb_msg_standard_input;
 
132
                fname_fmt = "\n";
 
133
        }
 
134
        if (!argv[1]) { /* zero or one filename? */
 
135
                if (!((print_type-1) & print_type)) /* exactly one option? */
 
136
                        start_fmt = "%"COUNT_FMT;
 
137
        }
 
138
 
 
139
        memset(totals, 0, sizeof(totals));
 
140
 
 
141
        pcounts = counts;
 
142
 
 
143
        num_files = 0;
 
144
        while ((arg = *argv++) != NULL) {
 
145
                FILE *fp;
 
146
                const char *s;
 
147
                unsigned u;
 
148
                unsigned linepos;
 
149
                smallint in_word;
 
150
 
 
151
                ++num_files;
 
152
                fp = fopen_or_warn_stdin(arg);
 
153
                if (!fp) {
 
154
                        status = EXIT_FAILURE;
 
155
                        continue;
 
156
                }
 
157
 
 
158
                memset(counts, 0, sizeof(counts));
 
159
                linepos = 0;
 
160
                in_word = 0;
 
161
 
 
162
                while (1) {
 
163
                        int c;
 
164
                        /* Our -w doesn't match GNU wc exactly... oh well */
 
165
 
 
166
                        c = getc(fp);
 
167
                        if (c == EOF) {
 
168
                                if (ferror(fp)) {
 
169
                                        bb_simple_perror_msg(arg);
 
170
                                        status = EXIT_FAILURE;
 
171
                                }
 
172
                                goto DO_EOF;  /* Treat an EOF as '\r'. */
 
173
                        }
 
174
 
 
175
                        /* Cater for -c and -m */
 
176
                        ++counts[WC_BYTES];
 
177
                        if (unicode_status != UNICODE_ON /* every byte is a new char */
 
178
                         || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */
 
179
                        ) {
 
180
                                ++counts[WC_UNICHARS];
 
181
                        }
 
182
 
 
183
                        if (isprint_asciionly(c)) { /* FIXME: not unicode-aware */
 
184
                                ++linepos;
 
185
                                if (!isspace(c)) {
 
186
                                        in_word = 1;
 
187
                                        continue;
 
188
                                }
 
189
                        } else if ((unsigned)(c - 9) <= 4) {
 
190
                                /* \t  9
 
191
                                 * \n 10
 
192
                                 * \v 11
 
193
                                 * \f 12
 
194
                                 * \r 13
 
195
                                 */
 
196
                                if (c == '\t') {
 
197
                                        linepos = (linepos | 7) + 1;
 
198
                                } else {  /* '\n', '\r', '\f', or '\v' */
 
199
 DO_EOF:
 
200
                                        if (linepos > counts[WC_LENGTH]) {
 
201
                                                counts[WC_LENGTH] = linepos;
 
202
                                        }
 
203
                                        if (c == '\n') {
 
204
                                                ++counts[WC_LINES];
 
205
                                        }
 
206
                                        if (c != '\v') {
 
207
                                                linepos = 0;
 
208
                                        }
 
209
                                }
 
210
                        } else {
 
211
                                continue;
 
212
                        }
 
213
 
 
214
                        counts[WC_WORDS] += in_word;
 
215
                        in_word = 0;
 
216
                        if (c == EOF) {
 
217
                                break;
 
218
                        }
 
219
                }
 
220
 
 
221
                fclose_if_not_stdin(fp);
 
222
 
 
223
                if (totals[WC_LENGTH] < counts[WC_LENGTH]) {
 
224
                        totals[WC_LENGTH] = counts[WC_LENGTH];
 
225
                }
 
226
                totals[WC_LENGTH] -= counts[WC_LENGTH];
 
227
 
 
228
 OUTPUT:
 
229
                /* coreutils wc tries hard to print pretty columns
 
230
                 * (saves results for all files, finds max col len etc...)
 
231
                 * we won't try that hard, it will bloat us too much */
 
232
                s = start_fmt;
 
233
                u = 0;
 
234
                do {
 
235
                        if (print_type & (1 << u)) {
 
236
                                printf(s, pcounts[u]);
 
237
                                s = " %9"COUNT_FMT; /* Ok... restore the leading space. */
 
238
                        }
 
239
                        totals[u] += pcounts[u];
 
240
                } while (++u < NUM_WCS);
 
241
                printf(fname_fmt, arg);
 
242
        }
 
243
 
 
244
        /* If more than one file was processed, we want the totals.  To save some
 
245
         * space, we set the pcounts ptr to the totals array.  This has the side
 
246
         * effect of trashing the totals array after outputting it, but that's
 
247
         * irrelavent since we no longer need it. */
 
248
        if (num_files > 1) {
 
249
                num_files = 0;  /* Make sure we don't get here again. */
 
250
                arg = "total";
 
251
                pcounts = totals;
 
252
                --argv;
 
253
                goto OUTPUT;
 
254
        }
 
255
 
 
256
        fflush_stdout_and_exit(status);
 
257
}