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
57 static void
print_first_N(FILE * fp,unsigned long count,bool count_bytes)58 print_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
74 static void
print_except_N_last_bytes(FILE * fp,unsigned count)75 print_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 
103 static void
print_except_N_last_lines(FILE * fp,unsigned count)104 print_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 */
139 void print_except_N_last_bytes(FILE *fp, unsigned count);
140 void 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
146 static unsigned long
eat_num(bool * negative_N,const char * p)147 eat_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 
158 static 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 
167 int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
head_main(int argc,char ** argv)168 int 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