blob: c7537a20e4e01483f3a6ffaf89eb2da25d7acf5f [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
John Beppu3157b1f1999-12-10 07:42:50 +00002/*
Manuel Novoa III cad53642003-03-19 09:13:01 +00003 * head implementation for busybox
John Beppu3157b1f1999-12-10 07:42:50 +00004 *
Manuel Novoa III cad53642003-03-19 09:13:01 +00005 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
John Beppu3157b1f1999-12-10 07:42:50 +00006 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02007 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
John Beppu3157b1f1999-12-10 07:42:50 +00008 */
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +01009//config:config HEAD
Denys Vlasenkob097a842018-12-28 03:20:17 +010010//config: bool "head (3.8 kb)"
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010011//config: default y
12//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020013//config: head is used to print the first specified number of lines
14//config: from files.
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010015//config:
16//config:config FEATURE_FANCY_HEAD
Denys Vlasenkof5604222017-01-10 14:58:54 +010017//config: bool "Enable -c, -q, and -v"
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010018//config: default y
19//config: depends on HEAD
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010020
21//applet:IF_HEAD(APPLET_NOEXEC(head, head, BB_DIR_USR_BIN, BB_SUID_DROP, head))
22
23//kbuild:lib-$(CONFIG_HEAD) += head.o
John Beppu3157b1f1999-12-10 07:42:50 +000024
Manuel Novoa III cad53642003-03-19 09:13:01 +000025/* 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
Pere Orga34425382011-03-31 14:43:25 +020029//usage:#define head_trivial_usage
30//usage: "[OPTIONS] [FILE]..."
31//usage:#define head_full_usage "\n\n"
Denys Vlasenkob9258b82021-06-02 04:01:10 +020032//usage: "Print first 10 lines of FILEs (or stdin).\n"
Pere Orga34425382011-03-31 14:43:25 +020033//usage: "With more than one FILE, precede each with a filename header.\n"
Denys Vlasenkob9258b82021-06-02 04:01:10 +020034//usage: "\n -n N[bkm] Print first N lines"
Pere Orga34425382011-03-31 14:43:25 +020035//usage: IF_FEATURE_FANCY_HEAD(
Denys Vlasenkob9258b82021-06-02 04:01:10 +020036//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(
Pere Orga34425382011-03-31 14:43:25 +020041//usage: "\n -q Never print headers"
42//usage: "\n -v Always print headers"
43//usage: )
Pere Orga34425382011-03-31 14:43:25 +020044//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
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000050#include "libbb.h"
John Beppu3157b1f1999-12-10 07:42:50 +000051
Dan Fandrich2d1a78b2010-09-30 14:31:12 -070052/* This is a NOEXEC applet. Be very careful! */
53
Denys Vlasenko40c6da42013-02-25 01:26:09 +010054#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;
Denys Vlasenko1e825ac2022-01-18 00:31:27 +010079 for (;;) {
Denys Vlasenko40c6da42013-02-25 01:26:09 +010080 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;
Denys Vlasenko1e825ac2022-01-18 00:31:27 +0100108 for (;;) {
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100109 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;
Ron Yorstoncad3fc72021-02-03 20:47:14 +0100121 fputs_stdout(circle[head]);
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100122 c = xmalloc_fgets(fp);
123 if (!c)
124 goto ret;
125 free(circle[head]);
126 circle[head++] = c;
127 }
128 ret:
129 head = 0;
Denys Vlasenko1e825ac2022-01-18 00:31:27 +0100130 for (;;) {
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100131 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
Denys Vlasenkoc72b43c2013-07-13 23:49:45 +0200155 return xatoul_sfx(p, bkm_suffixes);
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100156}
157
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000158static const char head_opts[] ALIGN1 =
Manuel Novoa III cad53642003-03-19 09:13:01 +0000159 "n:"
Bernhard Reutner-Fischer5816ccb2005-12-13 10:48:45 +0000160#if ENABLE_FEATURE_FANCY_HEAD
Manuel Novoa III cad53642003-03-19 09:13:01 +0000161 "c:qv"
162#endif
163 ;
John Beppu3157b1f1999-12-10 07:42:50 +0000164
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200165#define header_fmt_str "\n==> %s <==\n"
John Beppu3157b1f1999-12-10 07:42:50 +0000166
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000167int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000168int head_main(int argc, char **argv)
John Beppu3157b1f1999-12-10 07:42:50 +0000169{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000170 unsigned long count = 10;
Bernhard Reutner-Fischer5816ccb2005-12-13 10:48:45 +0000171#if ENABLE_FEATURE_FANCY_HEAD
Manuel Novoa III cad53642003-03-19 09:13:01 +0000172 int header_threshhold = 1;
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100173 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
Manuel Novoa III cad53642003-03-19 09:13:01 +0000179#endif
Matt Kraaic0321f92000-09-27 04:09:22 +0000180 FILE *fp;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000181 const char *fmt;
182 char *p;
183 int opt;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000184 int retval = EXIT_SUCCESS;
John Beppu3157b1f1999-12-10 07:42:50 +0000185
Denis Vlasenko08492072006-12-22 13:56:36 +0000186#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
Manuel Novoa III cad53642003-03-19 09:13:01 +0000187 /* Allow legacy syntax of an initial numeric option without -n. */
Denis Vlasenko0d873672008-11-11 22:43:10 +0000188 if (argv[1] && argv[1][0] == '-'
Denis Vlasenko459903b2006-11-27 14:44:18 +0000189 && isdigit(argv[1][1])
Manuel Novoa III cad53642003-03-19 09:13:01 +0000190 ) {
191 --argc;
192 ++argv;
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100193 p = argv[0] + 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000194 goto GET_COUNT;
Robert Griebl13c26fc2002-05-17 22:18:04 +0000195 }
Glenn L McGrath0bd02572005-12-11 03:09:05 +0000196#endif
Robert Griebl13c26fc2002-05-17 22:18:04 +0000197
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000198 /* No size benefit in converting this to getopt32 */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000199 while ((opt = getopt(argc, argv, head_opts)) > 0) {
Denis Vlasenkoc16bd212006-09-27 19:51:06 +0000200 switch (opt) {
Bernhard Reutner-Fischer5816ccb2005-12-13 10:48:45 +0000201#if ENABLE_FEATURE_FANCY_HEAD
Denis Vlasenko13858992006-10-08 12:49:22 +0000202 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 */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000211#endif
Denis Vlasenko13858992006-10-08 12:49:22 +0000212 case 'n':
213 p = optarg;
Denis Vlasenko08492072006-12-22 13:56:36 +0000214#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
215 GET_COUNT:
Bernhard Reutner-Fischer5816ccb2005-12-13 10:48:45 +0000216#endif
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100217 count = eat_num(&negative_N, p);
Denis Vlasenko13858992006-10-08 12:49:22 +0000218 break;
219 default:
220 bb_show_usage();
Erik Andersene49d5ec2000-02-08 19:58:47 +0000221 }
222 }
223
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000224 argc -= optind;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000225 argv += optind;
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000226 if (!*argv)
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000227 *--argv = (char*)"-";
John Beppu3157b1f1999-12-10 07:42:50 +0000228
Manuel Novoa III cad53642003-03-19 09:13:01 +0000229 fmt = header_fmt_str + 1;
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000230 if (argc <= header_threshhold) {
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100231#if ENABLE_FEATURE_FANCY_HEAD
Manuel Novoa III cad53642003-03-19 09:13:01 +0000232 header_threshhold = 0;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000233#else
Denis Vlasenko62a90cd2008-03-17 09:07:36 +0000234 fmt += 11; /* "" */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000235#endif
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100236 }
237 if (negative_N) {
238 if (count >= INT_MAX / sizeof(char*))
239 bb_error_msg("count is too big: %lu", count);
240 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000241
242 do {
Denis Vlasenkoddec5af2006-10-26 23:25:17 +0000243 fp = fopen_or_warn_stdin(*argv);
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000244 if (fp) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000245 if (fp == stdin) {
246 *argv = (char *) bb_msg_standard_input;
247 }
248 if (header_threshhold) {
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000249 printf(fmt, *argv);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000250 }
Denys Vlasenko40c6da42013-02-25 01:26:09 +0100251 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);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000259 }
Denys Vlasenkoaf0255f2013-02-25 01:24:32 +0100260 die_if_ferror_stdout();
Denis Vlasenkoddec5af2006-10-26 23:25:17 +0000261 if (fclose_if_not_stdin(fp)) {
Denis Vlasenko0d873672008-11-11 22:43:10 +0000262 bb_simple_perror_msg(*argv);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000263 retval = EXIT_FAILURE;
264 }
Denis Vlasenko0d873672008-11-11 22:43:10 +0000265 } else {
266 retval = EXIT_FAILURE;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000267 }
268 fmt = header_fmt_str;
269 } while (*++argv);
270
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000271 fflush_stdout_and_exit(retval);
Matt Kraaic0321f92000-09-27 04:09:22 +0000272}