busybox/coreutils/head.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * head implementation for busybox
   4 *
   5 * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 */
   9
  10/* BB_AUDIT SUSv3 compliant */
  11/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
  12/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */
  13
  14//kbuild:lib-$(CONFIG_HEAD) += head.o
  15
  16//usage:#define head_trivial_usage
  17//usage:       "[OPTIONS] [FILE]..."
  18//usage:#define head_full_usage "\n\n"
  19//usage:       "Print first 10 lines of each FILE (or stdin) to stdout.\n"
  20//usage:       "With more than one FILE, precede each with a filename header.\n"
  21//usage:     "\n        -n N[kbm]       Print first N lines"
  22//usage:        IF_FEATURE_FANCY_HEAD(
  23//usage:     "\n        -n -N[kbm]      Print all except N last lines"
  24//usage:     "\n        -c [-]N[kbm]    Print first N bytes"
  25//usage:     "\n        -q              Never print headers"
  26//usage:     "\n        -v              Always print headers"
  27//usage:        )
  28//usage:     "\n"
  29//usage:     "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
  30//usage:
  31//usage:#define head_example_usage
  32//usage:       "$ head -n 2 /etc/passwd\n"
  33//usage:       "root:x:0:0:root:/root:/bin/bash\n"
  34//usage:       "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n"
  35
  36#include "libbb.h"
  37
  38/* This is a NOEXEC applet. Be very careful! */
  39
  40#if !ENABLE_FEATURE_FANCY_HEAD
  41# define print_first_N(fp,count,bytes) print_first_N(fp,count)
  42#endif
  43static void
  44print_first_N(FILE *fp, unsigned long count, bool count_bytes)
  45{
  46#if !ENABLE_FEATURE_FANCY_HEAD
  47        const int count_bytes = 0;
  48#endif
  49        while (count) {
  50                int c = getc(fp);
  51                if (c == EOF)
  52                        break;
  53                if (count_bytes || (c == '\n'))
  54                        --count;
  55                putchar(c);
  56        }
  57}
  58
  59#if ENABLE_FEATURE_FANCY_HEAD
  60static void
  61print_except_N_last_bytes(FILE *fp, unsigned count)
  62{
  63        unsigned char *circle = xmalloc(++count);
  64        unsigned head = 0;
  65        for(;;) {
  66                int c;
  67                c = getc(fp);
  68                if (c == EOF)
  69                        goto ret;
  70                circle[head++] = c;
  71                if (head == count)
  72                        break;
  73        }
  74        for (;;) {
  75                int c;
  76                if (head == count)
  77                        head = 0;
  78                putchar(circle[head]);
  79                c = getc(fp);
  80                if (c == EOF)
  81                        goto ret;
  82                circle[head] = c;
  83                head++;
  84        }
  85 ret:
  86        free(circle);
  87}
  88
  89static void
  90print_except_N_last_lines(FILE *fp, unsigned count)
  91{
  92        char **circle = xzalloc((++count) * sizeof(circle[0]));
  93        unsigned head = 0;
  94        for(;;) {
  95                char *c;
  96                c = xmalloc_fgets(fp);
  97                if (!c)
  98                        goto ret;
  99                circle[head++] = c;
 100                if (head == count)
 101                        break;
 102        }
 103        for (;;) {
 104                char *c;
 105                if (head == count)
 106                        head = 0;
 107                fputs(circle[head], stdout);
 108                c = xmalloc_fgets(fp);
 109                if (!c)
 110                        goto ret;
 111                free(circle[head]);
 112                circle[head++] = c;
 113        }
 114 ret:
 115        head = 0;
 116        for(;;) {
 117                free(circle[head++]);
 118                if (head == count)
 119                        break;
 120        }
 121        free(circle);
 122}
 123#else
 124/* Must never be called */
 125void print_except_N_last_bytes(FILE *fp, unsigned count);
 126void print_except_N_last_lines(FILE *fp, unsigned count);
 127#endif
 128
 129#if !ENABLE_FEATURE_FANCY_HEAD
 130# define eat_num(negative_N,p) eat_num(p)
 131#endif
 132static unsigned long
 133eat_num(bool *negative_N, const char *p)
 134{
 135#if ENABLE_FEATURE_FANCY_HEAD
 136        if (*p == '-') {
 137                *negative_N = 1;
 138                p++;
 139        }
 140#endif
 141        return xatoul_sfx(p, bkm_suffixes);
 142}
 143
 144static const char head_opts[] ALIGN1 =
 145        "n:"
 146#if ENABLE_FEATURE_FANCY_HEAD
 147        "c:qv"
 148#endif
 149        ;
 150
 151#define header_fmt_str "\n==> %s <==\n"
 152
 153int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 154int head_main(int argc, char **argv)
 155{
 156        unsigned long count = 10;
 157#if ENABLE_FEATURE_FANCY_HEAD
 158        int header_threshhold = 1;
 159        bool count_bytes = 0;
 160        bool negative_N = 0;
 161#else
 162# define header_threshhold 1
 163# define count_bytes       0
 164# define negative_N        0
 165#endif
 166        FILE *fp;
 167        const char *fmt;
 168        char *p;
 169        int opt;
 170        int retval = EXIT_SUCCESS;
 171
 172#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
 173        /* Allow legacy syntax of an initial numeric option without -n. */
 174        if (argv[1] && argv[1][0] == '-'
 175         && isdigit(argv[1][1])
 176        ) {
 177                --argc;
 178                ++argv;
 179                p = argv[0] + 1;
 180                goto GET_COUNT;
 181        }
 182#endif
 183
 184        /* No size benefit in converting this to getopt32 */
 185        while ((opt = getopt(argc, argv, head_opts)) > 0) {
 186                switch (opt) {
 187#if ENABLE_FEATURE_FANCY_HEAD
 188                case 'q':
 189                        header_threshhold = INT_MAX;
 190                        break;
 191                case 'v':
 192                        header_threshhold = -1;
 193                        break;
 194                case 'c':
 195                        count_bytes = 1;
 196                        /* fall through */
 197#endif
 198                case 'n':
 199                        p = optarg;
 200#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
 201 GET_COUNT:
 202#endif
 203                        count = eat_num(&negative_N, p);
 204                        break;
 205                default:
 206                        bb_show_usage();
 207                }
 208        }
 209
 210        argc -= optind;
 211        argv += optind;
 212        if (!*argv)
 213                *--argv = (char*)"-";
 214
 215        fmt = header_fmt_str + 1;
 216        if (argc <= header_threshhold) {
 217#if ENABLE_FEATURE_FANCY_HEAD
 218                header_threshhold = 0;
 219#else
 220                fmt += 11; /* "" */
 221#endif
 222        }
 223        if (negative_N) {
 224                if (count >= INT_MAX / sizeof(char*))
 225                        bb_error_msg("count is too big: %lu", count);
 226        }
 227
 228        do {
 229                fp = fopen_or_warn_stdin(*argv);
 230                if (fp) {
 231                        if (fp == stdin) {
 232                                *argv = (char *) bb_msg_standard_input;
 233                        }
 234                        if (header_threshhold) {
 235                                printf(fmt, *argv);
 236                        }
 237                        if (negative_N) {
 238                                if (count_bytes) {
 239                                        print_except_N_last_bytes(fp, count);
 240                                } else {
 241                                        print_except_N_last_lines(fp, count);
 242                                }
 243                        } else {
 244                                print_first_N(fp, count, count_bytes);
 245                        }
 246                        die_if_ferror_stdout();
 247                        if (fclose_if_not_stdin(fp)) {
 248                                bb_simple_perror_msg(*argv);
 249                                retval = EXIT_FAILURE;
 250                        }
 251                } else {
 252                        retval = EXIT_FAILURE;
 253                }
 254                fmt = header_fmt_str;
 255        } while (*++argv);
 256
 257        fflush_stdout_and_exit(retval);
 258}
 259