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