pidfile.c: not used anymore
[oweals/busybox.git] / coreutils / cut.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * cut.c - minimalist version of cut
4  *
5  * Copyright (C) 1999,2000,2001 by Lineo, inc.
6  * Written by Mark Whitley <markw@codepoet.org>
7  * debloated by Bernhard Fischer
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10  */
11
12 #include "busybox.h"
13
14 /* option vars */
15 static const char optstring[] = "b:c:f:d:sn";
16 #define CUT_OPT_BYTE_FLGS       (1<<0)
17 #define CUT_OPT_CHAR_FLGS       (1<<1)
18 #define CUT_OPT_FIELDS_FLGS     (1<<2)
19 #define CUT_OPT_DELIM_FLGS      (1<<3)
20 #define CUT_OPT_SUPPRESS_FLGS (1<<4)
21
22 static char delim = '\t';       /* delimiter, default is tab */
23
24 struct cut_list {
25         int startpos;
26         int endpos;
27 };
28
29 enum {
30         BOL = 0,
31         EOL = INT_MAX,
32         NON_RANGE = -1
33 };
34
35 /* growable array holding a series of lists */
36 static struct cut_list *cut_lists;
37 static unsigned int nlists;     /* number of elements in above list */
38
39
40 static int cmpfunc(const void *a, const void *b)
41 {
42         return (((struct cut_list *) a)->startpos -
43                         ((struct cut_list *) b)->startpos);
44
45 }
46
47 static void cut_file(FILE * file)
48 {
49         char *line = NULL;
50         unsigned int linenum = 0;       /* keep these zero-based to be consistent */
51
52         /* go through every line in the file */
53         while ((line = xmalloc_getline(file)) != NULL) {
54
55                 /* set up a list so we can keep track of what's been printed */
56                 char * printed = xzalloc(strlen(line) * sizeof(char));
57                 char * orig_line = line;
58                 unsigned int cl_pos = 0;
59                 int spos;
60
61                 /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */
62                 if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) {
63                         /* print the chars specified in each cut list */
64                         for (; cl_pos < nlists; cl_pos++) {
65                                 spos = cut_lists[cl_pos].startpos;
66                                 while (spos < strlen(line)) {
67                                         if (!printed[spos]) {
68                                                 printed[spos] = 'X';
69                                                 putchar(line[spos]);
70                                         }
71                                         spos++;
72                                         if (spos > cut_lists[cl_pos].endpos
73                                                 || cut_lists[cl_pos].endpos == NON_RANGE)
74                                                 break;
75                                 }
76                         }
77                 } else if (delim == '\n') {     /* cut by lines */
78                         spos = cut_lists[cl_pos].startpos;
79
80                         /* get out if we have no more lists to process or if the lines
81                          * are lower than what we're interested in */
82                         if (linenum < spos || cl_pos >= nlists)
83                                 goto next_line;
84
85                         /* if the line we're looking for is lower than the one we were
86                          * passed, it means we displayed it already, so move on */
87                         while (spos < linenum) {
88                                 spos++;
89                                 /* go to the next list if we're at the end of this one */
90                                 if (spos > cut_lists[cl_pos].endpos
91                                         || cut_lists[cl_pos].endpos == NON_RANGE) {
92                                         cl_pos++;
93                                         /* get out if there's no more lists to process */
94                                         if (cl_pos >= nlists)
95                                                 goto next_line;
96                                         spos = cut_lists[cl_pos].startpos;
97                                         /* get out if the current line is lower than the one
98                                          * we just became interested in */
99                                         if (linenum < spos)
100                                                 goto next_line;
101                                 }
102                         }
103
104                         /* If we made it here, it means we've found the line we're
105                          * looking for, so print it */
106                         puts(line);
107                         goto next_line;
108                 } else {                /* cut by fields */
109                         int ndelim = -1;        /* zero-based / one-based problem */
110                         int nfields_printed = 0;
111                         char *field = NULL;
112                         const char delimiter[2] = { delim, 0 };
113
114                         /* does this line contain any delimiters? */
115                         if (strchr(line, delim) == NULL) {
116                                 if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS))
117                                         puts(line);
118                                 goto next_line;
119                         }
120
121                         /* process each list on this line, for as long as we've got
122                          * a line to process */
123                         for (; cl_pos < nlists && line; cl_pos++) {
124                                 spos = cut_lists[cl_pos].startpos;
125                                 do {
126                                         /* find the field we're looking for */
127                                         while (line && ndelim < spos) {
128                                                 field = strsep(&line, delimiter);
129                                                 ndelim++;
130                                         }
131
132                                         /* we found it, and it hasn't been printed yet */
133                                         if (field && ndelim == spos && !printed[ndelim]) {
134                                                 /* if this isn't our first time through, we need to
135                                                  * print the delimiter after the last field that was
136                                                  * printed */
137                                                 if (nfields_printed > 0)
138                                                         putchar(delim);
139                                                 fputs(field, stdout);
140                                                 printed[ndelim] = 'X';
141                                                 nfields_printed++;      /* shouldn't overflow.. */
142                                         }
143
144                                         spos++;
145
146                                         /* keep going as long as we have a line to work with,
147                                          * this is a list, and we're not at the end of that
148                                          * list */
149                                 } while (spos <= cut_lists[cl_pos].endpos && line
150                                                  && cut_lists[cl_pos].endpos != NON_RANGE);
151                         }
152                 }
153                 /* if we printed anything at all, we need to finish it with a
154                  * newline cuz we were handed a chomped line */
155                 putchar('\n');
156  next_line:
157                 linenum++;
158                 free(printed);
159                 free(orig_line);
160         }
161 }
162
163 static const char _op_on_field[] = " only when operating on fields";
164
165 int cut_main(int argc, char **argv);
166 int cut_main(int argc, char **argv)
167 {
168         char *sopt, *ltok;
169
170         opt_complementary = "b--bcf:c--bcf:f--bcf";
171         getopt32(argc, argv, optstring, &sopt, &sopt, &sopt, &ltok);
172 //      argc -= optind;
173         argv += optind;
174         if (!(option_mask32 & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS)))
175                 bb_error_msg_and_die("expected a list of bytes, characters, or fields");
176         if (option_mask32 & BB_GETOPT_ERROR)
177                 bb_error_msg_and_die("only one type of list may be specified");
178
179         if (option_mask32 & CUT_OPT_DELIM_FLGS) {
180                 if (strlen(ltok) > 1) {
181                         bb_error_msg_and_die("the delimiter must be a single character");
182                 }
183                 delim = ltok[0];
184         }
185
186         /*  non-field (char or byte) cutting has some special handling */
187         if (!(option_mask32 & CUT_OPT_FIELDS_FLGS)) {
188                 if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) {
189                         bb_error_msg_and_die
190                                 ("suppressing non-delimited lines makes sense%s",
191                                  _op_on_field);
192                 }
193                 if (delim != '\t') {
194                         bb_error_msg_and_die
195                                 ("a delimiter may be specified%s", _op_on_field);
196                 }
197         }
198
199         /*
200          * parse list and put values into startpos and endpos.
201          * valid list formats: N, N-, N-M, -M
202          * more than one list can be separated by commas
203          */
204         {
205                 char *ntok;
206                 int s = 0, e = 0;
207
208                 /* take apart the lists, one by one (they are separated with commas */
209                 while ((ltok = strsep(&sopt, ",")) != NULL) {
210
211                         /* it's actually legal to pass an empty list */
212                         if (strlen(ltok) == 0)
213                                 continue;
214
215                         /* get the start pos */
216                         ntok = strsep(&ltok, "-");
217                         if (ntok == NULL) {
218                                 bb_error_msg
219                                         ("internal error: ntok is null for start pos!?\n");
220                         } else if (strlen(ntok) == 0) {
221                                 s = BOL;
222                         } else {
223                                 s = xatoi_u(ntok);
224                                 /* account for the fact that arrays are zero based, while
225                                  * the user expects the first char on the line to be char #1 */
226                                 if (s != 0)
227                                         s--;
228                         }
229
230                         /* get the end pos */
231                         ntok = strsep(&ltok, "-");
232                         if (ntok == NULL) {
233                                 e = NON_RANGE;
234                         } else if (strlen(ntok) == 0) {
235                                 e = EOL;
236                         } else {
237                                 e = xatoi_u(ntok);
238                                 /* if the user specified and end position of 0, that means "til the
239                                  * end of the line */
240                                 if (e == 0)
241                                         e = EOL;
242                                 e--;    /* again, arrays are zero based, lines are 1 based */
243                                 if (e == s)
244                                         e = NON_RANGE;
245                         }
246
247                         /* if there's something left to tokenize, the user passed
248                          * an invalid list */
249                         if (ltok)
250                                 bb_error_msg_and_die("invalid byte or field list");
251
252                         /* add the new list */
253                         cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists));
254                         cut_lists[nlists-1].startpos = s;
255                         cut_lists[nlists-1].endpos = e;
256                 }
257
258                 /* make sure we got some cut positions out of all that */
259                 if (nlists == 0)
260                         bb_error_msg_and_die("missing list of positions");
261
262                 /* now that the lists are parsed, we need to sort them to make life
263                  * easier on us when it comes time to print the chars / fields / lines
264                  */
265                 qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc);
266         }
267
268         /* argv[0..argc-1] should be names of file to process. If no
269          * files were specified or '-' was specified, take input from stdin.
270          * Otherwise, we process all the files specified. */
271         if (argv[0] == NULL || LONE_DASH(argv[0])) {
272                 cut_file(stdin);
273         } else {
274                 FILE *file;
275
276                 do {
277                         file = fopen_or_warn(argv[0], "r");
278                         if (file) {
279                                 cut_file(file);
280                                 fclose(file);
281                         }
282                 } while (*++argv);
283         }
284         if (ENABLE_FEATURE_CLEAN_UP)
285                 free(cut_lists);
286         return EXIT_SUCCESS;
287 }