diff options
-rw-r--r-- | coreutils/head.c | 150 |
1 files changed, 128 insertions, 22 deletions
diff --git a/coreutils/head.c b/coreutils/head.c index 598fccb64..291e1ce37 100644 --- a/coreutils/head.c +++ b/coreutils/head.c @@ -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)) { |