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