head: support -n -NUM and -c -NUM
No binary (build failed)
—
(log yok)
| @@ -21,7 +21,8 @@ | |
| //usage: "With more than one FILE, precede each with a filename header.\n" | |
| //usage: "\n -n N[kbm] Print first N lines" | |
| //usage: IF_FEATURE_FANCY_HEAD( | |
| - | //usage: "\n -c N[kbm] Print first N bytes" |
| + | //usage: "\n -n -N[kbm] Print all except N last lines" |
| + | //usage: "\n -c [-]N[kbm] Print first N bytes" |
| //usage: "\n -q Never print headers" | |
| //usage: "\n -v Always print headers" | |
| //usage: ) | |
| @@ -38,6 +39,110 @@ | |
| /* This is a NOEXEC applet. Be very careful! */ | |
| + | #if !ENABLE_FEATURE_FANCY_HEAD |
| + | # define print_first_N(fp,count,bytes) print_first_N(fp,count) |
| + | #endif |
| + | static void |
| + | print_first_N(FILE *fp, unsigned long count, bool count_bytes) |
| + | { |
| + | #if !ENABLE_FEATURE_FANCY_HEAD |
| + | const int count_bytes = 0; |
| + | #endif |
| + | while (count) { |
| + | int c = getc(fp); |
| + | if (c == EOF) |
| + | break; |
| + | if (count_bytes || (c == '\n')) |
| + | --count; |
| + | putchar(c); |
| + | } |
| + | } |
| + | |
| + | #if ENABLE_FEATURE_FANCY_HEAD |
| + | static void |
| + | print_except_N_last_bytes(FILE *fp, unsigned count) |
| + | { |
| + | unsigned char *circle = xmalloc(++count); |
| + | unsigned head = 0; |
| + | for(;;) { |
| + | int c; |
| + | c = getc(fp); |
| + | if (c == EOF) |
| + | goto ret; |
| + | circle[head++] = c; |
| + | if (head == count) |
| + | break; |
| + | } |
| + | for (;;) { |
| + | int c; |
| + | if (head == count) |
| + | head = 0; |
| + | putchar(circle[head]); |
| + | c = getc(fp); |
| + | if (c == EOF) |
| + | goto ret; |
| + | circle[head] = c; |
| + | head++; |
| + | } |
| + | ret: |
| + | free(circle); |
| + | } |
| + | |
| + | static void |
| + | print_except_N_last_lines(FILE *fp, unsigned count) |
| + | { |
| + | char **circle = xzalloc((++count) * sizeof(circle[0])); |
| + | unsigned head = 0; |
| + | for(;;) { |
| + | char *c; |
| + | c = xmalloc_fgets(fp); |
| + | if (!c) |
| + | goto ret; |
| + | circle[head++] = c; |
| + | if (head == count) |
| + | break; |
| + | } |
| + | for (;;) { |
| + | char *c; |
| + | if (head == count) |
| + | head = 0; |
| + | fputs(circle[head], stdout); |
| + | c = xmalloc_fgets(fp); |
| + | if (!c) |
| + | goto ret; |
| + | free(circle[head]); |
| + | circle[head++] = c; |
| + | } |
| + | ret: |
| + | head = 0; |
| + | for(;;) { |
| + | free(circle[head++]); |
| + | if (head == count) |
| + | break; |
| + | } |
| + | free(circle); |
| + | } |
| + | #else |
| + | /* Must never be called */ |
| + | void print_except_N_last_bytes(FILE *fp, unsigned count); |
| + | void print_except_N_last_lines(FILE *fp, unsigned count); |
| + | #endif |
| + | |
| + | #if !ENABLE_FEATURE_FANCY_HEAD |
| + | # define eat_num(negative_N,p) eat_num(p) |
| + | #endif |
| + | static unsigned long |
| + | eat_num(bool *negative_N, const char *p) |
| + | { |
| + | #if ENABLE_FEATURE_FANCY_HEAD |
| + | if (*p == '-') { |
| + | *negative_N = 1; |
| + | p++; |
| + | } |
| + | #endif |
| + | return xatoul_sfx(p, head_tail_suffixes); |
| + | } |
| + | |
| static const char head_opts[] ALIGN1 = | |
| "n:" | |
| #if ENABLE_FEATURE_FANCY_HEAD | |
| @@ -52,8 +157,13 @@ int head_main(int argc, char **argv) | |
| { | |
| unsigned long count = 10; | |
| #if ENABLE_FEATURE_FANCY_HEAD | |
| - | int count_bytes = 0; |
| int header_threshhold = 1; | |
| + | bool count_bytes = 0; |
| + | bool negative_N = 0; |
| + | #else |
| + | # define header_threshhold 1 |
| + | # define count_bytes 0 |
| + | # define negative_N 0 |
| #endif | |
| FILE *fp; | |
| const char *fmt; | |
| @@ -68,7 +178,7 @@ int head_main(int argc, char **argv) | |
| ) { | |
| --argc; | |
| ++argv; | |
| - | p = (*argv) + 1; |
| + | p = argv[0] + 1; |
| goto GET_COUNT; | |
| } | |
| #endif | |
| @@ -92,7 +202,7 @@ int head_main(int argc, char **argv) | |
| #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD | |
| GET_COUNT: | |
| #endif | |
| - | count = xatoul_sfx(p, head_tail_suffixes); |
| + | count = eat_num(&negative_N, p); |
| break; | |
| default: | |
| bb_show_usage(); | |
| @@ -105,39 +215,35 @@ int head_main(int argc, char **argv) | |
| *--argv = (char*)"-"; | |
| fmt = header_fmt_str + 1; | |
| - | #if ENABLE_FEATURE_FANCY_HEAD |
| if (argc <= header_threshhold) { | |
| + | #if ENABLE_FEATURE_FANCY_HEAD |
| header_threshhold = 0; | |
| - | } |
| #else | |
| - | if (argc <= 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 | |
| + | } |
| + | if (negative_N) { |
| + | if (count >= INT_MAX / sizeof(char*)) |
| + | bb_error_msg("count is too big: %lu", count); |
| + | } |
| do { | |
| fp = fopen_or_warn_stdin(*argv); | |
| if (fp) { | |
| - | unsigned long i; |
| - | |
| if (fp == stdin) { | |
| *argv = (char *) bb_msg_standard_input; | |
| } | |
| if (header_threshhold) { | |
| printf(fmt, *argv); | |
| } | |
| - | i = count; |
| - | while (i) { |
| - | int c = getc(fp); |
| - | if (c == EOF) |
| - | break; |
| - | if (count_bytes || (c == '\n')) |
| - | --i; |
| - | putchar(c); |
| + | if (negative_N) { |
| + | if (count_bytes) { |
| + | print_except_N_last_bytes(fp, count); |
| + | } else { |
| + | print_except_N_last_lines(fp, count); |
| + | } |
| + | } else { |
| + | print_first_N(fp, count, count_bytes); |
| } | |
| die_if_ferror_stdout(); | |
| if (fclose_if_not_stdin(fp)) { | |