/* Get next line of input from G.input_file_list, flushing append buffer and * noting if we ran out of files without a newline on the last line we read. */ static char *get_next_line(char *gets_char, char *last_puts_char) { char *temp = NULL; size_t len; char gc; flush_append(last_puts_char); /* will be returned if last line in the file * doesn't end with either '\n' or '\0' */ gc = NO_EOL_CHAR; for (; G.current_input_file <= G.last_input_file; G.current_input_file++) { FILE *fp = G.current_fp; if (!fp) { const char *path = G.input_file_list[G.current_input_file]; fp = stdin; if (path != bb_msg_standard_input) { fp = fopen_or_warn(path, "r"); if (!fp) { G.exitcode = EXIT_FAILURE; continue; } } G.current_fp = fp; } /* Read line up to a newline or NUL byte, inclusive, * return malloc'ed char[]. length of the chunk read * is stored in len. NULL if EOF/error */ temp = bb_get_chunk_from_file(fp, &len); if (temp) { /* len > 0 here, it's ok to do temp[len-1] */ char c = temp[len-1]; if (c == '\n' || c == '\0') { temp[len-1] = '\0'; gc = c; if (c == '\0') { int ch = fgetc(fp); if (ch != EOF) ungetc(ch, fp); else gc = LAST_IS_NUL; } } /* else we put NO_EOL_CHAR into *gets_char */ break; /* NB: I had the idea of peeking next file(s) and returning * NO_EOL_CHAR only if it is the *last* non-empty * input file. But there is a case where this won't work: * file1: "a woo\nb woo" * file2: "c no\nd no" * sed -ne 's/woo/bang/p' input1 input2 => "a bang\nb bang" * (note: *no* newline after "b bang"!) */ } /* Close this file and advance to next one */ fclose_if_not_stdin(fp); G.current_fp = NULL; } *gets_char = gc; return temp; }
int expand_main(int argc UNUSED_PARAM, char **argv) { /* Default 8 spaces for 1 tab */ const char *opt_t = "8"; FILE *file; unsigned tab_size; unsigned opt; int exit_status = EXIT_SUCCESS; #if ENABLE_FEATURE_EXPAND_LONG_OPTIONS static const char expand_longopts[] ALIGN1 = /* name, has_arg, val */ "initial\0" No_argument "i" "tabs\0" Required_argument "t" ; #endif #if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS static const char unexpand_longopts[] ALIGN1 = /* name, has_arg, val */ "first-only\0" No_argument "i" "tabs\0" Required_argument "t" "all\0" No_argument "a" ; #endif init_unicode(); if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) { IF_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts); opt = getopt32(argv, "it:", &opt_t); } else { IF_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts); /* -t NUM sets also -a */ opt_complementary = "ta"; opt = getopt32(argv, "ft:a", &opt_t); /* -f --first-only is the default */ if (!(opt & OPT_ALL)) opt |= OPT_INITIAL; } tab_size = xatou_range(opt_t, 1, UINT_MAX); argv += optind; if (!*argv) { *--argv = (char*)bb_msg_standard_input; } do { file = fopen_or_warn_stdin(*argv); if (!file) { exit_status = EXIT_FAILURE; continue; } if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) IF_EXPAND(expand(file, tab_size, opt)); else IF_UNEXPAND(unexpand(file, tab_size, opt)); /* Check and close the file */ if (fclose_if_not_stdin(file)) { bb_simple_perror_msg(*argv); exit_status = EXIT_FAILURE; } /* If stdin also clear EOF */ if (file == stdin) clearerr(file); } while (*++argv); /* Now close stdin also */ /* (if we didn't read from it, it's a no-op) */ if (fclose(stdin)) bb_perror_msg_and_die(bb_msg_standard_input); fflush_stdout_and_exit(exit_status); }
int strings_main(int argc UNUSED_PARAM, char **argv) { int c, status = EXIT_SUCCESS; unsigned n, count; off_t offset; FILE *file; char *string; const char *fmt = "%s: "; const char *n_arg = "4"; getopt32(argv, "afon:", &n_arg); /* -a is our default behaviour */ /*argc -= optind;*/ argv += optind; n = xatou_range(n_arg, 1, INT_MAX); string = xzalloc(n + 1); n--; if (!*argv) { fmt = "{%s}: "; *--argv = (char *)bb_msg_standard_input; } do { file = fopen_or_warn_stdin(*argv); if (!file) { status = EXIT_FAILURE; continue; } offset = 0; count = 0; do { c = fgetc(file); if (isprint_asciionly(c) || c == '\t') { if (count > n) { bb_putchar(c); } else { string[count] = c; if (count == n) { if (option_mask32 & PRINT_NAME) { printf(fmt, *argv); } if (option_mask32 & PRINT_OFFSET) { printf("%7"OFF_FMT"o ", offset - n); } fputs(string, stdout); } count++; } } else { if (count > n) { bb_putchar('\n'); } count = 0; } offset++; } while (c != EOF); fclose_if_not_stdin(file); } while (*++argv); if (ENABLE_FEATURE_CLEAN_UP) free(string); fflush_stdout_and_exit(status); }
int sed_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; llist_t *opt_e, *opt_f; char *opt_i; #if ENABLE_LONG_OPTS static const char sed_longopts[] ALIGN1 = /* name has_arg short */ "in-place\0" Optional_argument "i" "regexp-extended\0" No_argument "r" "quiet\0" No_argument "n" "silent\0" No_argument "n" "expression\0" Required_argument "e" "file\0" Required_argument "f"; #endif INIT_G(); /* destroy command strings on exit */ if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff); /* Lie to autoconf when it starts asking stupid questions. */ if (argv[1] && strcmp(argv[1], "--version") == 0) { puts("This is not GNU sed version 4.0"); return 0; } /* do normal option parsing */ opt_e = opt_f = NULL; opt_i = NULL; /* -i must be first, to match OPT_in_place definition */ /* -E is a synonym of -r: * GNU sed 4.2.1 mentions it in neither --help * nor manpage, but does recognize it. */ opt = getopt32long(argv, "^" "i::rEne:*f:*" "\0" "nn"/*count -n*/, sed_longopts, &opt_i, &opt_e, &opt_f, &G.be_quiet); /* counter for -n */ //argc -= optind; argv += optind; if (opt & OPT_in_place) { // -i die_func = cleanup_outname; } if (opt & (2|4)) G.regex_type |= REG_EXTENDED; // -r or -E //if (opt & 8) // G.be_quiet++; // -n (implemented with a counter instead) while (opt_e) { // -e add_cmd_block(llist_pop(&opt_e)); } while (opt_f) { // -f char *line; FILE *cmdfile; cmdfile = xfopen_stdin(llist_pop(&opt_f)); while ((line = xmalloc_fgetline(cmdfile)) != NULL) { add_cmd(line); free(line); } fclose_if_not_stdin(cmdfile); } /* if we didn't get a pattern from -e or -f, use argv[0] */ if (!(opt & 0x30)) { if (!*argv) bb_show_usage(); add_cmd_block(*argv++); } /* Flush any unfinished commands. */ add_cmd(""); /* By default, we write to stdout */ G.nonstdout = stdout; /* argv[0..(argc-1)] should be names of file to process. If no * files were specified or '-' was specified, take input from stdin. * Otherwise, we process all the files specified. */ G.input_file_list = argv; if (!argv[0]) { if (opt & OPT_in_place) bb_error_msg_and_die(bb_msg_requires_arg, "-i"); argv[0] = (char*)bb_msg_standard_input; /* G.last_input_file = 0; - already is */ } else { goto start; for (; *argv; argv++) { struct stat statbuf; int nonstdoutfd; sed_cmd_t *sed_cmd; G.last_input_file++; start: if (!(opt & OPT_in_place)) { if (LONE_DASH(*argv)) { *argv = (char*)bb_msg_standard_input; process_files(); } continue; } /* -i: process each FILE separately: */ if (stat(*argv, &statbuf) != 0) { bb_simple_perror_msg(*argv); G.exitcode = EXIT_FAILURE; G.current_input_file++; continue; } G.outname = xasprintf("%sXXXXXX", *argv); nonstdoutfd = xmkstemp(G.outname); G.nonstdout = xfdopen_for_write(nonstdoutfd); /* Set permissions/owner of output file */ /* chmod'ing AFTER chown would preserve suid/sgid bits, * but GNU sed 4.2.1 does not preserve them either */ fchmod(nonstdoutfd, statbuf.st_mode); fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid); process_files(); fclose(G.nonstdout); G.nonstdout = stdout; if (opt_i) { char *backupname = xasprintf("%s%s", *argv, opt_i); xrename(*argv, backupname); free(backupname); } /* else unlink(*argv); - rename below does this */ xrename(G.outname, *argv); //TODO: rollback backup on error? free(G.outname); G.outname = NULL; /* Fix disabled range matches and mangled ",+N" ranges */ for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) { sed_cmd->beg_line = sed_cmd->beg_line_orig; sed_cmd->end_line = sed_cmd->end_line_orig; } } /* Here, to handle "sed 'cmds' nonexistent_file" case we did: * if (G.current_input_file[G.current_input_file] == NULL) * return G.exitcode; * but it's not needed since process_files() works correctly * in this case too. */ } process_files(); return G.exitcode; }
int cut_main(int argc ATTRIBUTE_UNUSED, char **argv) { char delim = '\t'; /* delimiter, default is tab */ char *sopt, *ltok; opt_complementary = "b--bcf:c--bcf:f--bcf"; getopt32(argv, optstring, &sopt, &sopt, &sopt, <ok); // argc -= optind; argv += optind; if (!(option_mask32 & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS))) bb_error_msg_and_die("expected a list of bytes, characters, or fields"); if (option_mask32 & CUT_OPT_DELIM_FLGS) { if (ltok[0] && ltok[1]) { /* more than 1 char? */ bb_error_msg_and_die("the delimiter must be a single character"); } delim = ltok[0]; } /* non-field (char or byte) cutting has some special handling */ if (!(option_mask32 & CUT_OPT_FIELDS_FLGS)) { static const char _op_on_field[] ALIGN1 = " only when operating on fields"; if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) { bb_error_msg_and_die ("suppressing non-delimited lines makes sense%s", _op_on_field); } if (delim != '\t') { bb_error_msg_and_die ("a delimiter may be specified%s", _op_on_field); } } /* * parse list and put values into startpos and endpos. * valid list formats: N, N-, N-M, -M * more than one list can be separated by commas */ { char *ntok; int s = 0, e = 0; /* take apart the lists, one by one (they are separated with commas) */ while ((ltok = strsep(&sopt, ",")) != NULL) { /* it's actually legal to pass an empty list */ if (!ltok[0]) continue; /* get the start pos */ ntok = strsep(<ok, "-"); if (!ntok[0]) { s = BOL; } else { s = xatoi_u(ntok); /* account for the fact that arrays are zero based, while * the user expects the first char on the line to be char #1 */ if (s != 0) s--; } /* get the end pos */ if (ltok == NULL) { e = NON_RANGE; } else if (!ltok[0]) { e = EOL; } else { e = xatoi_u(ltok); /* if the user specified and end position of 0, that means "til the * end of the line */ if (e == 0) e = EOL; e--; /* again, arrays are zero based, lines are 1 based */ if (e == s) e = NON_RANGE; } /* add the new list */ cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists)); cut_lists[nlists-1].startpos = s; cut_lists[nlists-1].endpos = e; } /* make sure we got some cut positions out of all that */ if (nlists == 0) bb_error_msg_and_die("missing list of positions"); /* now that the lists are parsed, we need to sort them to make life * easier on us when it comes time to print the chars / fields / lines */ qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc); } { int retval = EXIT_SUCCESS; if (!*argv) *--argv = (char *)"-"; do { FILE *file = fopen_or_warn_stdin(*argv); if (!file) { retval = EXIT_FAILURE; continue; } cut_file(file, delim); fclose_if_not_stdin(file); } while (*++argv); if (ENABLE_FEATURE_CLEAN_UP) free(cut_lists); fflush_stdout_and_exit(retval); } }
int fold_main(int argc UNUSED_PARAM, char **argv) { char *line_out = NULL; const char *w_opt = "80"; unsigned width; smallint exitcode = EXIT_SUCCESS; init_unicode(); if (ENABLE_INCLUDE_SUSv2) { /* Turn any numeric options into -w options. */ int i; for (i = 1; argv[i]; i++) { const char *a = argv[i]; if (*a == '-') { a++; if (*a == '-' && !a[1]) /* "--" */ break; if (isdigit(*a)) argv[i] = xasprintf("-w%s", a); } } } getopt32(argv, "bsw:", &w_opt); width = xatou_range(w_opt, 1, 10000); argv += optind; if (!*argv) *--argv = (char*)"-"; do { FILE *istream = fopen_or_warn_stdin(*argv); int c; unsigned column = 0; /* Screen column where next char will go */ unsigned offset_out = 0; /* Index in 'line_out' for next char */ if (istream == NULL) { exitcode = EXIT_FAILURE; continue; } while ((c = getc(istream)) != EOF) { /* We grow line_out in chunks of 0x1000 bytes */ if ((offset_out & 0xfff) == 0) { line_out = xrealloc(line_out, offset_out + 0x1000); } rescan: line_out[offset_out] = c; if (c == '\n') { write2stdout(line_out, offset_out + 1); column = offset_out = 0; continue; } column = adjust_column(column, c); if (column <= width || offset_out == 0) { /* offset_out == 0 case happens * with small width (say, 1) and tabs. * The very first tab already goes to column 8, * but we must not wrap it */ offset_out++; continue; } /* This character would make the line too long. * Print the line plus a newline, and make this character * start the next line */ if (option_mask32 & FLAG_BREAK_SPACES) { unsigned i; unsigned logical_end; /* Look for the last blank. */ for (logical_end = offset_out - 1; (int)logical_end >= 0; logical_end--) { if (!isblank(line_out[logical_end])) continue; /* Found a space or tab. * Output up to and including it, and start a new line */ logical_end++; /*line_out[logical_end] = '\n'; - NO! this nukes one buffered character */ write2stdout(line_out, logical_end); putchar('\n'); /* Move the remainder to the beginning of the next line. * The areas being copied here might overlap. */ memmove(line_out, line_out + logical_end, offset_out - logical_end); offset_out -= logical_end; for (column = i = 0; i < offset_out; i++) { column = adjust_column(column, line_out[i]); } goto rescan; } /* No blank found, wrap will split the overlong word */ } /* Output what we accumulated up to now, and start a new line */ line_out[offset_out] = '\n'; write2stdout(line_out, offset_out + 1); column = offset_out = 0; goto rescan; } /* while (not EOF) */ if (offset_out) { write2stdout(line_out, offset_out); } if (fclose_if_not_stdin(istream)) { bb_simple_perror_msg(*argv); exitcode = EXIT_FAILURE; } } while (*++argv); fflush_stdout_and_exit(exitcode); }
int head_main(int argc, char **argv) { unsigned long count = 10; unsigned long i; #if ENABLE_FEATURE_FANCY_HEAD int count_bytes = 0; int header_threshhold = 1; #endif FILE *fp; const char *fmt; char *p; int opt; int c; int retval = EXIT_SUCCESS; #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD /* Allow legacy syntax of an initial numeric option without -n. */ if (argc > 1 && argv[1][0] == '-' && isdigit(argv[1][1]) ) { --argc; ++argv; p = (*argv) + 1; goto GET_COUNT; } #endif /* No size benefit in converting this to getopt32 */ while ((opt = getopt(argc, argv, head_opts)) > 0) { switch (opt) { #if ENABLE_FEATURE_FANCY_HEAD case 'q': header_threshhold = INT_MAX; break; case 'v': header_threshhold = -1; break; case 'c': count_bytes = 1; /* fall through */ #endif case 'n': p = optarg; #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD GET_COUNT: #endif #if !ENABLE_FEATURE_FANCY_HEAD count = xatoul(p); #else count = xatoul_sfx(p, head_suffixes); #endif break; default: bb_show_usage(); } } argv += optind; if (!*argv) { *--argv = "-"; } fmt = header_fmt_str + 1; #if ENABLE_FEATURE_FANCY_HEAD if (argc - optind <= header_threshhold) { header_threshhold = 0; } #else if (argc <= optind + 1) { fmt += 11; } /* Now define some things here to avoid #ifdefs in the code below. * These should optimize out of the if conditions below. */ #define header_threshhold 1 #define count_bytes 0 #endif do { fp = fopen_or_warn_stdin(*argv); if (fp) { if (fp == stdin) { *argv = (char *) bb_msg_standard_input; } if (header_threshhold) { printf(fmt, *argv); } i = count; while (i && ((c = getc(fp)) != EOF)) { if (count_bytes || (c == '\n')) { --i; } putchar(c); } if (fclose_if_not_stdin(fp)) { bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */ retval = EXIT_FAILURE; } die_if_ferror_stdout(); } fmt = header_fmt_str; } while (*++argv); fflush_stdout_and_exit(retval); }
int grep_main(int argc, char **argv) { FILE *file; int matched; llist_t *fopt = NULL; /* do normal option parsing */ #if ENABLE_FEATURE_GREP_CONTEXT char *slines_after; char *slines_before; char *Copt; opt_complementary = "H-h:e::f::C-AB"; getopt32(argc, argv, GREP_OPTS GREP_OPT_CONTEXT OPT_EGREP, &pattern_head, &fopt, &slines_after, &slines_before, &Copt); if (option_mask32 & GREP_OPT_C) { /* -C unsets prev -A and -B, but following -A or -B may override it */ if (!(option_mask32 & GREP_OPT_A)) /* not overridden */ slines_after = Copt; if (!(option_mask32 & GREP_OPT_B)) /* not overridden */ slines_before = Copt; option_mask32 |= GREP_OPT_A|GREP_OPT_B; /* for parser */ } if (option_mask32 & GREP_OPT_A) { lines_after = xatoi_u(slines_after); } if (option_mask32 & GREP_OPT_B) { lines_before = xatoi_u(slines_before); } /* sanity checks */ if (option_mask32 & (GREP_OPT_c|GREP_OPT_q|GREP_OPT_l|GREP_OPT_L)) { option_mask32 &= ~GREP_OPT_n; lines_before = 0; lines_after = 0; } else if (lines_before > 0) before_buf = xzalloc(lines_before * sizeof(char *)); #else /* with auto sanity checks */ opt_complementary = "H-h:e::f::c-n:q-n:l-n"; getopt32(argc, argv, GREP_OPTS OPT_EGREP, &pattern_head, &fopt); #endif invert_search = ((option_mask32 & GREP_OPT_v) != 0); /* 0 | 1 */ if (pattern_head != NULL) { /* convert char *argv[] to grep_list_data_t */ llist_t *cur; for (cur = pattern_head; cur; cur = cur->link) cur->data = new_grep_list_data(cur->data, 0); } if (option_mask32 & GREP_OPT_f) load_regexes_from_file(fopt); if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f') option_mask32 |= GREP_OPT_F; if (!(option_mask32 & GREP_OPT_o)) reflags = REG_NOSUB; if (ENABLE_FEATURE_GREP_EGREP_ALIAS && (applet_name[0] == 'e' || (option_mask32 & GREP_OPT_E))) reflags |= REG_EXTENDED; if (option_mask32 & GREP_OPT_i) reflags |= REG_ICASE; argv += optind; argc -= optind; /* if we didn't get a pattern from a -e and no command file was specified, * argv[optind] should be the pattern. no pattern, no worky */ if (pattern_head == NULL) { if (*argv == NULL) bb_show_usage(); else { char *pattern = new_grep_list_data(*argv++, 0); llist_add_to(&pattern_head, pattern); argc--; } } /* argv[(optind)..(argc-1)] should be names of file to grep through. If * there is more than one file to grep, we will print the filenames. */ if (argc > 1) print_filename = 1; /* -H / -h of course override */ if (option_mask32 & GREP_OPT_H) print_filename = 1; if (option_mask32 & GREP_OPT_h) print_filename = 0; /* If no files were specified, or '-' was specified, take input from * stdin. Otherwise, we grep through all the files specified. */ if (argc == 0) argc++; matched = 0; while (argc--) { cur_file = *argv++; file = stdin; if (!cur_file || (*cur_file == '-' && !cur_file[1])) { cur_file = "(standard input)"; } else { if (option_mask32 & GREP_OPT_r) { struct stat st; if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) { if (!(option_mask32 & GREP_OPT_h)) print_filename = 1; matched += grep_dir(cur_file); goto grep_done; } } /* else: fopen(dir) will succeed, but reading won't */ file = fopen(cur_file, "r"); if (file == NULL) { if (!SUPPRESS_ERR_MSGS) bb_perror_msg("%s", cur_file); open_errors = 1; continue; } } matched += grep_file(file); fclose_if_not_stdin(file); grep_done: if (matched < 0) { /* we found a match but were told to be quiet, stop here and * return success */ break; } } /* destroy all the elments in the pattern list */ if (ENABLE_FEATURE_CLEAN_UP) { while (pattern_head) { llist_t *pattern_head_ptr = pattern_head; grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data; pattern_head = pattern_head->link; if ((gl->flg_mem_alocated_compiled & PATTERN_MEM_A)) free(gl->pattern); if ((gl->flg_mem_alocated_compiled & COMPILED)) regfree(&(gl->preg)); free(pattern_head_ptr); } } /* 0 = success, 1 = failed, 2 = error */ /* If the -q option is specified, the exit status shall be zero * if an input line is selected, even if an error was detected. */ if (BE_QUIET && matched) return 0; if (open_errors) return 2; return !matched; /* invert return value 0 = success, 1 = failed */ }
int wc_main(int argc ATTRIBUTE_UNUSED, char **argv) { FILE *fp; const char *s, *arg; const char *start_fmt = " %9"COUNT_FMT + 1; const char *fname_fmt = " %s\n"; COUNT_T *pcounts; COUNT_T counts[4]; COUNT_T totals[4]; unsigned linepos; unsigned u; int num_files = 0; int c; smallint status = EXIT_SUCCESS; smallint in_word; unsigned print_type; print_type = getopt32(argv, "lwcL"); if (print_type == 0) { print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_CHARS); } argv += optind; if (!argv[0]) { *--argv = (char *) bb_msg_standard_input; fname_fmt = "\n"; if (!((print_type-1) & print_type)) /* exactly one option? */ start_fmt = "%"COUNT_FMT; } memset(totals, 0, sizeof(totals)); pcounts = counts; while ((arg = *argv++) != 0) { ++num_files; fp = fopen_or_warn_stdin(arg); if (!fp) { status = EXIT_FAILURE; continue; } memset(counts, 0, sizeof(counts)); linepos = 0; in_word = 0; do { /* Our -w doesn't match GNU wc exactly... oh well */ ++counts[WC_CHARS]; c = getc(fp); if (isprint(c)) { ++linepos; if (!isspace_given_isprint(c)) { in_word = 1; continue; } } else if (((unsigned int)(c - 9)) <= 4) { /* \t 9 * \n 10 * \v 11 * \f 12 * \r 13 */ if (c == '\t') { linepos = (linepos | 7) + 1; } else { /* '\n', '\r', '\f', or '\v' */ DO_EOF: if (linepos > counts[WC_LENGTH]) { counts[WC_LENGTH] = linepos; } if (c == '\n') { ++counts[WC_LINES]; } if (c != '\v') { linepos = 0; } } } else if (c == EOF) { if (ferror(fp)) { bb_simple_perror_msg(arg); status = EXIT_FAILURE; } --counts[WC_CHARS]; goto DO_EOF; /* Treat an EOF as '\r'. */ } else { continue; } counts[WC_WORDS] += in_word; in_word = 0; if (c == EOF) { break; } } while (1); if (totals[WC_LENGTH] < counts[WC_LENGTH]) { totals[WC_LENGTH] = counts[WC_LENGTH]; } totals[WC_LENGTH] -= counts[WC_LENGTH]; fclose_if_not_stdin(fp); OUTPUT: /* coreutils wc tries hard to print pretty columns * (saves results for all files, find max col len etc...) * we won't try that hard, it will bloat us too much */ s = start_fmt; u = 0; do { if (print_type & (1 << u)) { printf(s, pcounts[u]); s = " %9"COUNT_FMT; /* Ok... restore the leading space. */ } totals[u] += pcounts[u]; } while (++u < 4); printf(fname_fmt, arg); } /* If more than one file was processed, we want the totals. To save some * space, we set the pcounts ptr to the totals array. This has the side * effect of trashing the totals array after outputting it, but that's * irrelavent since we no longer need it. */ if (num_files > 1) { num_files = 0; /* Make sure we don't get here again. */ arg = "total"; pcounts = totals; --argv; goto OUTPUT; } fflush_stdout_and_exit(status); }
int sort_main(int argc UNUSED_PARAM, char **argv) { FILE *fp, *outfile = stdout; char *line, **lines = NULL; char *str_ignored, *str_o, *str_t; llist_t *lst_k = NULL; int i, flag; int linecount = 0; xfunc_error_retval = 2; /* Parse command line options */ /* -o and -t can be given at most once */ opt_complementary = "o--o:t--t:" /* -t, -o: maximum one of each */ "k::"; /* -k takes list */ getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t); #if ENABLE_FEATURE_SORT_BIG if (option_mask32 & FLAG_o) outfile = xfopen_for_write(str_o); if (option_mask32 & FLAG_t) { if (!str_t[0] || str_t[1]) bb_error_msg_and_die("bad -t parameter"); key_separator = str_t[0]; } /* parse sort key */ while (lst_k) { enum { FLAG_allowed_for_k = FLAG_n | /* Numeric sort */ FLAG_g | /* Sort using strtod() */ FLAG_M | /* Sort date */ FLAG_b | /* Ignore leading blanks */ FLAG_r | /* Reverse */ FLAG_d | /* Ignore !(isalnum()|isspace()) */ FLAG_f | /* Force uppercase */ FLAG_i | /* Ignore !isprint() */ 0 }; struct sort_key *key = add_key(); char *str_k = llist_pop(&lst_k); const char *temp2; i = 0; /* i==0 before comma, 1 after (-k3,6) */ while (*str_k) { /* Start of range */ /* Cannot use bb_strtou - suffix can be a letter */ key->range[2*i] = str2u(&str_k); if (*str_k == '.') { str_k++; key->range[2*i+1] = str2u(&str_k); } while (*str_k) { if (*str_k == ',' && !i++) { str_k++; break; } /* no else needed: fall through to syntax error because comma isn't in OPT_STR */ temp2 = strchr(OPT_STR, *str_k); if (!temp2) bb_error_msg_and_die("unknown key option"); flag = 1 << (temp2 - OPT_STR); if (flag & ~FLAG_allowed_for_k) bb_error_msg_and_die("unknown sort type"); /* b after ',' means strip _trailing_ space */ if (i && flag == FLAG_b) flag = FLAG_bb; key->flags |= flag; str_k++; } } } #endif /* global b strips leading and trailing spaces */ if (option_mask32 & FLAG_b) option_mask32 |= FLAG_bb; /* Open input files and read data */ argv += optind; if (!*argv) *--argv = (char*)"-"; do { /* coreutils 6.9 compat: abort on first open error, * do not continue to next file: */ fp = xfopen_stdin(*argv); for (;;) { line = GET_LINE(fp); if (!line) break; lines = xrealloc_vector(lines, 6, linecount); lines[linecount++] = line; } fclose_if_not_stdin(fp); } while (*++argv); #if ENABLE_FEATURE_SORT_BIG /* if no key, perform alphabetic sort */ if (!key_list) add_key()->range[0] = 1; /* handle -c */ if (option_mask32 & FLAG_c) { int j = (option_mask32 & FLAG_u) ? -1 : 0; for (i = 1; i < linecount; i++) if (compare_keys(&lines[i-1], &lines[i]) > j) { fprintf(stderr, "Check line %d\n", i); return EXIT_FAILURE; } return EXIT_SUCCESS; } #endif /* Perform the actual sort */ qsort(lines, linecount, sizeof(char *), compare_keys); /* handle -u */ if (option_mask32 & FLAG_u) { flag = 0; /* coreutils 6.3 drop lines for which only key is the same */ /* -- disabling last-resort compare... */ option_mask32 |= FLAG_s; for (i = 1; i < linecount; i++) { if (!compare_keys(&lines[flag], &lines[i])) free(lines[i]); else lines[++flag] = lines[i]; } if (linecount) linecount = flag+1; } /* Print it */ flag = (option_mask32 & FLAG_z) ? '\0' : '\n'; for (i = 0; i < linecount; i++) fprintf(outfile, "%s%c", lines[i], flag); fflush_stdout_and_exit(EXIT_SUCCESS); }
int sort_main(int argc UNUSED_PARAM, char **argv) { char *line, **lines; char *str_ignored, *str_o, *str_t; llist_t *lst_k = NULL; int i; int linecount; unsigned opts; xfunc_error_retval = 2; /* Parse command line options */ /* -o and -t can be given at most once */ opt_complementary = "o--o:t--t"; /* -t, -o: at most one of each */ opts = getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t); /* global b strips leading and trailing spaces */ if (opts & FLAG_b) option_mask32 |= FLAG_bb; #if ENABLE_FEATURE_SORT_BIG if (opts & FLAG_t) { if (!str_t[0] || str_t[1]) bb_error_msg_and_die("bad -t parameter"); key_separator = str_t[0]; } /* note: below this point we use option_mask32, not opts, * since that reduces register pressure and makes code smaller */ /* Parse sort key */ while (lst_k) { enum { FLAG_allowed_for_k = FLAG_n | /* Numeric sort */ FLAG_g | /* Sort using strtod() */ FLAG_M | /* Sort date */ FLAG_b | /* Ignore leading blanks */ FLAG_r | /* Reverse */ FLAG_d | /* Ignore !(isalnum()|isspace()) */ FLAG_f | /* Force uppercase */ FLAG_i | /* Ignore !isprint() */ 0 }; struct sort_key *key = add_key(); char *str_k = llist_pop(&lst_k); i = 0; /* i==0 before comma, 1 after (-k3,6) */ while (*str_k) { /* Start of range */ /* Cannot use bb_strtou - suffix can be a letter */ key->range[2*i] = str2u(&str_k); if (*str_k == '.') { str_k++; key->range[2*i+1] = str2u(&str_k); } while (*str_k) { int flag; const char *idx; if (*str_k == ',' && !i++) { str_k++; break; } /* no else needed: fall through to syntax error because comma isn't in OPT_STR */ idx = strchr(OPT_STR, *str_k); if (!idx) bb_error_msg_and_die("unknown key option"); flag = 1 << (idx - OPT_STR); if (flag & ~FLAG_allowed_for_k) bb_error_msg_and_die("unknown sort type"); /* b after ',' means strip _trailing_ space */ if (i && flag == FLAG_b) flag = FLAG_bb; key->flags |= flag; str_k++; } } } #endif /* Open input files and read data */ argv += optind; if (!*argv) *--argv = (char*)"-"; linecount = 0; lines = NULL; do { /* coreutils 6.9 compat: abort on first open error, * do not continue to next file: */ FILE *fp = xfopen_stdin(*argv); for (;;) { line = GET_LINE(fp); if (!line) break; lines = xrealloc_vector(lines, 6, linecount); lines[linecount++] = line; } fclose_if_not_stdin(fp); } while (*++argv); #if ENABLE_FEATURE_SORT_BIG /* If no key, perform alphabetic sort */ if (!key_list) add_key()->range[0] = 1; /* Handle -c */ if (option_mask32 & FLAG_c) { int j = (option_mask32 & FLAG_u) ? -1 : 0; for (i = 1; i < linecount; i++) { if (compare_keys(&lines[i-1], &lines[i]) > j) { fprintf(stderr, "Check line %u\n", i); return EXIT_FAILURE; } } return EXIT_SUCCESS; } #endif /* Perform the actual sort */ qsort(lines, linecount, sizeof(lines[0]), compare_keys); /* Handle -u */ if (option_mask32 & FLAG_u) { int j = 0; /* coreutils 6.3 drop lines for which only key is the same */ /* -- disabling last-resort compare... */ option_mask32 |= FLAG_s; for (i = 1; i < linecount; i++) { if (compare_keys(&lines[j], &lines[i]) == 0) free(lines[i]); else lines[++j] = lines[i]; } if (linecount) linecount = j+1; } /* Print it */ #if ENABLE_FEATURE_SORT_BIG /* Open output file _after_ we read all input ones */ if (option_mask32 & FLAG_o) xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), STDOUT_FILENO); #endif { int ch = (option_mask32 & FLAG_z) ? '\0' : '\n'; for (i = 0; i < linecount; i++) printf("%s%c", lines[i], ch); } fflush_stdout_and_exit(EXIT_SUCCESS); }