Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/coreutils/cut.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/coreutils/cut.c
r821 r1770 1 /* vi: set sw= 8 ts=8: */1 /* vi: set sw=4 ts=4: */ 2 2 /* 3 3 * cut.c - minimalist version of cut … … 5 5 * Copyright (C) 1999,2000,2001 by Lineo, inc. 6 6 * Written by Mark Whitley <markw@codepoet.org> 7 * debloated by Bernhard Fischer 7 8 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 9 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 22 10 */ 23 11 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <unistd.h> 27 #include <string.h> 28 #include <limits.h> 29 #include "busybox.h" 12 #include "libbb.h" 13 14 /* This is a NOEXEC applet. Be very careful! */ 30 15 31 16 32 17 /* option vars */ 33 static const char optstring[] = "b:c:f:d:sn"; 34 #define OPT_BYTE_FLGS 1 35 #define OPT_CHAR_FLGS 2 36 #define OPT_FIELDS_FLGS 4 37 #define OPT_DELIM_FLGS 8 38 #define OPT_SUPRESS_FLGS 16 39 static char part; /* (b)yte, (c)har, (f)ields */ 40 static unsigned int supress_non_delimited_lines; 41 static char delim = '\t'; /* delimiter, default is tab */ 18 static const char optstring[] ALIGN1 = "b:c:f:d:sn"; 19 #define CUT_OPT_BYTE_FLGS (1<<0) 20 #define CUT_OPT_CHAR_FLGS (1<<1) 21 #define CUT_OPT_FIELDS_FLGS (1<<2) 22 #define CUT_OPT_DELIM_FLGS (1<<3) 23 #define CUT_OPT_SUPPRESS_FLGS (1<<4) 24 25 static char delim = '\t'; /* delimiter, default is tab */ 42 26 43 27 struct cut_list { … … 52 36 }; 53 37 54 static struct cut_list *cut_lists = NULL; /* growable array holding a series of lists */ 55 static unsigned int nlists = 0; /* number of elements in above list */ 38 /* growable array holding a series of lists */ 39 static struct cut_list *cut_lists; 40 static unsigned int nlists; /* number of elements in above list */ 56 41 57 42 58 43 static int cmpfunc(const void *a, const void *b) 59 44 { 60 struct cut_list *la = (struct cut_list *)a; 61 struct cut_list *lb = (struct cut_list *)b; 62 63 if (la->startpos > lb->startpos) 64 return 1; 65 if (la->startpos < lb->startpos) 66 return -1; 67 return 0; 45 return (((struct cut_list *) a)->startpos - 46 ((struct cut_list *) b)->startpos); 47 68 48 } 69 49 70 71 /* 72 * parse_lists() - parses a list and puts values into startpos and endpos. 73 * valid list formats: N, N-, N-M, -M 74 * more than one list can be separated by commas 75 */ 76 static void parse_lists(char *lists) 77 { 78 char *ltok = NULL; 79 char *ntok = NULL; 80 char *junk; 81 int s = 0, e = 0; 82 83 /* take apart the lists, one by one (they are separated with commas */ 84 while ((ltok = strsep(&lists, ",")) != NULL) { 85 86 /* it's actually legal to pass an empty list */ 87 if (strlen(ltok) == 0) 88 continue; 89 90 /* get the start pos */ 91 ntok = strsep(<ok, "-"); 92 if (ntok == NULL) { 93 fprintf(stderr, "Help ntok is null for starting position! What do I do?\n"); 94 } else if (strlen(ntok) == 0) { 95 s = BOL; 96 } else { 97 s = strtoul(ntok, &junk, 10); 98 if(*junk != '\0' || s < 0) 99 bb_error_msg_and_die("invalid byte or field list"); 100 101 /* account for the fact that arrays are zero based, while the user 102 * expects the first char on the line to be char # 1 */ 103 if (s != 0) 104 s--; 105 } 106 107 /* get the end pos */ 108 ntok = strsep(<ok, "-"); 109 if (ntok == NULL) { 110 e = NON_RANGE; 111 } else if (strlen(ntok) == 0) { 112 e = EOL; 113 } else { 114 e = strtoul(ntok, &junk, 10); 115 if(*junk != '\0' || e < 0) 116 bb_error_msg_and_die("invalid byte or field list"); 117 /* if the user specified and end position of 0, that means "til the 118 * end of the line */ 119 if (e == 0) 120 e = INT_MAX; 121 e--; /* again, arrays are zero based, lines are 1 based */ 122 if (e == s) 123 e = NON_RANGE; 124 } 125 126 /* if there's something left to tokenize, the user past an invalid list */ 127 if (ltok) 128 bb_error_msg_and_die("invalid byte or field list"); 129 130 /* add the new list */ 131 cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists)); 132 cut_lists[nlists-1].startpos = s; 133 cut_lists[nlists-1].endpos = e; 134 } 135 136 /* make sure we got some cut positions out of all that */ 137 if (nlists == 0) 138 bb_error_msg_and_die("missing list of positions"); 139 140 /* now that the lists are parsed, we need to sort them to make life easier 141 * on us when it comes time to print the chars / fields / lines */ 142 qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc); 143 144 } 145 146 147 static void cut_line_by_chars(const char *line) 148 { 149 int c, l; 150 /* set up a list so we can keep track of what's been printed */ 151 char *printed = xcalloc(strlen(line), sizeof(char)); 152 153 /* print the chars specified in each cut list */ 154 for (c = 0; c < nlists; c++) { 155 l = cut_lists[c].startpos; 156 while (l < strlen(line)) { 157 if (!printed[l]) { 158 putchar(line[l]); 159 printed[l] = 'X'; 160 } 161 l++; 162 if (cut_lists[c].endpos == NON_RANGE || l > cut_lists[c].endpos) 163 break; 164 } 165 } 166 putchar('\n'); /* cuz we were handed a chomped line */ 167 free(printed); 168 } 169 170 171 static void cut_line_by_fields(char *line) 172 { 173 int c, f; 174 int ndelim = -1; /* zero-based / one-based problem */ 175 int nfields_printed = 0; 176 char *field = NULL; 177 char d[2] = { delim, 0 }; 178 char *printed; 179 180 /* test the easy case first: does this line contain any delimiters? */ 181 if (strchr(line, delim) == NULL) { 182 if (!supress_non_delimited_lines) 183 puts(line); 184 return; 185 } 186 187 /* set up a list so we can keep track of what's been printed */ 188 printed = xcalloc(strlen(line), sizeof(char)); 189 190 /* process each list on this line, for as long as we've got a line to process */ 191 for (c = 0; c < nlists && line; c++) { 192 f = cut_lists[c].startpos; 193 do { 194 195 /* find the field we're looking for */ 196 while (line && ndelim < f) { 197 field = strsep(&line, d); 198 ndelim++; 199 } 200 201 /* we found it, and it hasn't been printed yet */ 202 if (field && ndelim == f && !printed[ndelim]) { 203 /* if this isn't our first time through, we need to print the 204 * delimiter after the last field that was printed */ 205 if (nfields_printed > 0) 206 putchar(delim); 207 fputs(field, stdout); 208 printed[ndelim] = 'X'; 209 nfields_printed++; 210 } 211 212 f++; 213 214 /* keep going as long as we have a line to work with, this is a 215 * list, and we're not at the end of that list */ 216 } while (line && cut_lists[c].endpos != NON_RANGE && f <= cut_lists[c].endpos); 217 } 218 219 /* if we printed anything at all, we need to finish it with a newline cuz 220 * we were handed a chomped line */ 221 putchar('\n'); 222 223 free(printed); 224 } 225 226 227 static void cut_file_by_lines(const char *line, unsigned int linenum) 228 { 229 static int c = 0; 230 static int l = -1; 231 232 /* I can't initialize this above cuz the "initializer isn't 233 * constant" *sigh* */ 234 if (l == -1) 235 l = cut_lists[c].startpos; 236 237 /* get out if we have no more lists to process or if the lines are lower 238 * than what we're interested in */ 239 if (c >= nlists || linenum < l) 240 return; 241 242 /* if the line we're looking for is lower than the one we were passed, it 243 * means we displayed it already, so move on */ 244 while (l < linenum) { 245 l++; 246 /* move on to the next list if we're at the end of this one */ 247 if (cut_lists[c].endpos == NON_RANGE || l > cut_lists[c].endpos) { 248 c++; 249 /* get out if there's no more lists to process */ 250 if (c >= nlists) 251 return; 252 l = cut_lists[c].startpos; 253 /* get out if the current line is lower than the one we just became 254 * interested in */ 255 if (linenum < l) 256 return; 257 } 258 } 259 260 /* If we made it here, it means we've found the line we're looking for, so print it */ 261 puts(line); 262 } 263 264 265 /* 266 * snippy-snip 267 */ 268 static void cut_file(FILE *file) 50 static void cut_file(FILE * file) 269 51 { 270 52 char *line = NULL; 271 unsigned int linenum = 0; 53 unsigned int linenum = 0; /* keep these zero-based to be consistent */ 272 54 273 55 /* go through every line in the file */ 274 while ((line = bb_get_chomped_line_from_file(file)) != NULL) { 56 while ((line = xmalloc_getline(file)) != NULL) { 57 58 /* set up a list so we can keep track of what's been printed */ 59 char * printed = xzalloc(strlen(line) * sizeof(char)); 60 char * orig_line = line; 61 unsigned int cl_pos = 0; 62 int spos; 275 63 276 64 /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ 277 if ((part & (OPT_CHAR_FLGS | OPT_BYTE_FLGS))) 278 cut_line_by_chars(line); 279 280 /* cut based on fields */ 281 else { 282 if (delim == '\n') 283 cut_file_by_lines(line, linenum); 284 else 285 cut_line_by_fields(line); 286 } 287 65 if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { 66 /* print the chars specified in each cut list */ 67 for (; cl_pos < nlists; cl_pos++) { 68 spos = cut_lists[cl_pos].startpos; 69 while (spos < strlen(line)) { 70 if (!printed[spos]) { 71 printed[spos] = 'X'; 72 putchar(line[spos]); 73 } 74 spos++; 75 if (spos > cut_lists[cl_pos].endpos 76 || cut_lists[cl_pos].endpos == NON_RANGE) 77 break; 78 } 79 } 80 } else if (delim == '\n') { /* cut by lines */ 81 spos = cut_lists[cl_pos].startpos; 82 83 /* get out if we have no more lists to process or if the lines 84 * are lower than what we're interested in */ 85 if (linenum < spos || cl_pos >= nlists) 86 goto next_line; 87 88 /* if the line we're looking for is lower than the one we were 89 * passed, it means we displayed it already, so move on */ 90 while (spos < linenum) { 91 spos++; 92 /* go to the next list if we're at the end of this one */ 93 if (spos > cut_lists[cl_pos].endpos 94 || cut_lists[cl_pos].endpos == NON_RANGE) { 95 cl_pos++; 96 /* get out if there's no more lists to process */ 97 if (cl_pos >= nlists) 98 goto next_line; 99 spos = cut_lists[cl_pos].startpos; 100 /* get out if the current line is lower than the one 101 * we just became interested in */ 102 if (linenum < spos) 103 goto next_line; 104 } 105 } 106 107 /* If we made it here, it means we've found the line we're 108 * looking for, so print it */ 109 puts(line); 110 goto next_line; 111 } else { /* cut by fields */ 112 int ndelim = -1; /* zero-based / one-based problem */ 113 int nfields_printed = 0; 114 char *field = NULL; 115 const char delimiter[2] = { delim, 0 }; 116 117 /* does this line contain any delimiters? */ 118 if (strchr(line, delim) == NULL) { 119 if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS)) 120 puts(line); 121 goto next_line; 122 } 123 124 /* process each list on this line, for as long as we've got 125 * a line to process */ 126 for (; cl_pos < nlists && line; cl_pos++) { 127 spos = cut_lists[cl_pos].startpos; 128 do { 129 /* find the field we're looking for */ 130 while (line && ndelim < spos) { 131 field = strsep(&line, delimiter); 132 ndelim++; 133 } 134 135 /* we found it, and it hasn't been printed yet */ 136 if (field && ndelim == spos && !printed[ndelim]) { 137 /* if this isn't our first time through, we need to 138 * print the delimiter after the last field that was 139 * printed */ 140 if (nfields_printed > 0) 141 putchar(delim); 142 fputs(field, stdout); 143 printed[ndelim] = 'X'; 144 nfields_printed++; /* shouldn't overflow.. */ 145 } 146 147 spos++; 148 149 /* keep going as long as we have a line to work with, 150 * this is a list, and we're not at the end of that 151 * list */ 152 } while (spos <= cut_lists[cl_pos].endpos && line 153 && cut_lists[cl_pos].endpos != NON_RANGE); 154 } 155 } 156 /* if we printed anything at all, we need to finish it with a 157 * newline cuz we were handed a chomped line */ 158 putchar('\n'); 159 next_line: 288 160 linenum++; 289 free(line); 161 free(printed); 162 free(orig_line); 290 163 } 291 164 } 292 165 293 166 static const char _op_on_field[] ALIGN1 = " only when operating on fields"; 167 168 int cut_main(int argc, char **argv); 294 169 int cut_main(int argc, char **argv) 295 170 { 296 unsigned long opt; 297 char *sopt, *sdopt; 298 299 bb_opt_complementally = "b--bcf:c--bcf:f--bcf"; 300 opt = bb_getopt_ulflags(argc, argv, optstring, &sopt, &sopt, &sopt, &sdopt); 301 part = opt & (OPT_BYTE_FLGS|OPT_CHAR_FLGS|OPT_FIELDS_FLGS); 302 if(part == 0) 303 bb_error_msg_and_die("you must specify a list of bytes, characters, or fields"); 304 if(opt & BB_GETOPT_ERROR) 305 bb_error_msg_and_die("only one type of list may be specified"); 306 parse_lists(sopt); 307 if((opt & (OPT_DELIM_FLGS))) { 308 if (strlen(sdopt) > 1) { 171 char *sopt, *ltok; 172 173 opt_complementary = "b--bcf:c--bcf:f--bcf"; 174 getopt32(argv, optstring, &sopt, &sopt, &sopt, <ok); 175 // argc -= optind; 176 argv += optind; 177 if (!(option_mask32 & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS))) 178 bb_error_msg_and_die("expected a list of bytes, characters, or fields"); 179 180 if (option_mask32 & CUT_OPT_DELIM_FLGS) { 181 if (strlen(ltok) > 1) { 309 182 bb_error_msg_and_die("the delimiter must be a single character"); 310 183 } 311 delim = sdopt[0]; 312 } 313 supress_non_delimited_lines = opt & OPT_SUPRESS_FLGS; 184 delim = ltok[0]; 185 } 314 186 315 187 /* non-field (char or byte) cutting has some special handling */ 316 if (part != OPT_FIELDS_FLGS) { 317 if (supress_non_delimited_lines) { 318 bb_error_msg_and_die("suppressing non-delimited lines makes sense" 319 " only when operating on fields"); 188 if (!(option_mask32 & CUT_OPT_FIELDS_FLGS)) { 189 if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) { 190 bb_error_msg_and_die 191 ("suppressing non-delimited lines makes sense%s", 192 _op_on_field); 320 193 } 321 194 if (delim != '\t') { 322 bb_error_msg_and_die("a delimiter may be specified only when operating on fields"); 323 } 324 } 325 326 /* argv[(optind)..(argc-1)] should be names of file to process. If no 195 bb_error_msg_and_die 196 ("a delimiter may be specified%s", _op_on_field); 197 } 198 } 199 200 /* 201 * parse list and put values into startpos and endpos. 202 * valid list formats: N, N-, N-M, -M 203 * more than one list can be separated by commas 204 */ 205 { 206 char *ntok; 207 int s = 0, e = 0; 208 209 /* take apart the lists, one by one (they are separated with commas */ 210 while ((ltok = strsep(&sopt, ",")) != NULL) { 211 212 /* it's actually legal to pass an empty list */ 213 if (strlen(ltok) == 0) 214 continue; 215 216 /* get the start pos */ 217 ntok = strsep(<ok, "-"); 218 if (ntok == NULL) { 219 bb_error_msg 220 ("internal error: ntok is null for start pos!?\n"); 221 } else if (strlen(ntok) == 0) { 222 s = BOL; 223 } else { 224 s = xatoi_u(ntok); 225 /* account for the fact that arrays are zero based, while 226 * the user expects the first char on the line to be char #1 */ 227 if (s != 0) 228 s--; 229 } 230 231 /* get the end pos */ 232 ntok = strsep(<ok, "-"); 233 if (ntok == NULL) { 234 e = NON_RANGE; 235 } else if (strlen(ntok) == 0) { 236 e = EOL; 237 } else { 238 e = xatoi_u(ntok); 239 /* if the user specified and end position of 0, that means "til the 240 * end of the line */ 241 if (e == 0) 242 e = EOL; 243 e--; /* again, arrays are zero based, lines are 1 based */ 244 if (e == s) 245 e = NON_RANGE; 246 } 247 248 /* if there's something left to tokenize, the user passed 249 * an invalid list */ 250 if (ltok) 251 bb_error_msg_and_die("invalid byte or field list"); 252 253 /* add the new list */ 254 cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists)); 255 cut_lists[nlists-1].startpos = s; 256 cut_lists[nlists-1].endpos = e; 257 } 258 259 /* make sure we got some cut positions out of all that */ 260 if (nlists == 0) 261 bb_error_msg_and_die("missing list of positions"); 262 263 /* now that the lists are parsed, we need to sort them to make life 264 * easier on us when it comes time to print the chars / fields / lines 265 */ 266 qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc); 267 } 268 269 /* argv[0..argc-1] should be names of file to process. If no 327 270 * files were specified or '-' was specified, take input from stdin. 328 271 * Otherwise, we process all the files specified. */ 329 if (argv[ optind] == NULL || (strcmp(argv[optind], "-") == 0)) {272 if (argv[0] == NULL || LONE_DASH(argv[0])) { 330 273 cut_file(stdin); 331 } 332 else { 333 int i; 274 } else { 334 275 FILE *file; 335 for (i = optind; i < argc; i++) { 336 file = bb_wfopen(argv[i], "r"); 337 if(file) { 276 277 do { 278 file = fopen_or_warn(argv[0], "r"); 279 if (file) { 338 280 cut_file(file); 339 281 fclose(file); 340 282 } 341 } 342 } 343 283 } while (*++argv); 284 } 285 if (ENABLE_FEATURE_CLEAN_UP) 286 free(cut_lists); 344 287 return EXIT_SUCCESS; 345 288 }
Note:
See TracChangeset
for help on using the changeset viewer.