1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved	*/
29
30/*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39/*
40 * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
41 * Copyright 2020 Oxide Computer Company
42 */
43
44
45#include <stdio.h>
46#include <stdlib.h>
47#include <unistd.h>
48#include <locale.h>
49#include <string.h>
50#include <ctype.h>
51#include <err.h>
52
53#define	DEF_LINE_COUNT	10
54
55static	void	copyout(off_t, int);
56static	void	Usage();
57static	FILE	*input;
58
59
60/*
61 * head - give the first few lines of a stream or of each of a set of files.
62 * Optionally shows a specific number of bytes instead.
63 */
64int
65main(int argc, char **argv)
66{
67	int	fileCount;
68	int	around = 0;
69	int	i;
70	int	opt;
71	off_t	linecnt	= DEF_LINE_COUNT;
72	int	isline = 1;
73	int	error = 0;
74	int	quiet = 0;
75	int	verbose = 0;
76
77	(void) setlocale(LC_ALL, "");
78#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
79#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
80#endif
81	(void) textdomain(TEXT_DOMAIN);
82
83	/* check for non-standard "-line-count" option */
84	for (i = 1; i < argc; i++) {
85		if (strcmp(argv[i], "--") == 0)
86			break;
87
88		if ((argv[i][0] == '-') && isdigit(argv[i][1])) {
89			if (strlen(&argv[i][1]) !=
90			    strspn(&argv[i][1], "0123456789")) {
91				(void) fprintf(stderr, gettext(
92				    "%s: Badly formed number\n"), argv[0]);
93				Usage();
94			}
95
96			linecnt = (off_t)strtoll(&argv[i][1], (char **)NULL,
97			    10);
98			while (i < argc) {
99				argv[i] = argv[i + 1];
100				i++;
101			}
102			argc--;
103		}
104	}
105
106	/* get options */
107	while ((opt = getopt(argc, argv, "qvn:c:")) != EOF) {
108		switch (opt) {
109		case 'n':
110		case 'c':
111			if ((strcmp(optarg, "--") == 0) || (optind > argc)) {
112				(void) fprintf(stderr, gettext(
113				    "%s: Missing -%c argument\n"), argv[0],
114				    optopt);
115				Usage();
116			}
117			linecnt = (off_t)strtoll(optarg, (char **)NULL, 10);
118			if (linecnt <= 0) {
119				(void) fprintf(stderr, gettext(
120				    "%s: Invalid \"-%c %s\" option\n"),
121				    argv[0], optopt, optarg);
122				Usage();
123			}
124			isline = optopt != 'c';
125			break;
126		case 'q':
127			quiet = 1;
128			verbose = 0;
129			break;
130		case 'v':
131			quiet = 0;
132			verbose = 1;
133			break;
134		default:
135			Usage();
136		}
137	}
138
139	fileCount = argc - optind;
140
141	do {
142		if ((argv[optind] == NULL) && around)
143			break;
144
145		if (argv[optind] != NULL) {
146			if (input != NULL)
147				(void) fclose(input);
148			if ((input = fopen(argv[optind], "r")) == NULL) {
149				perror(argv[optind]);
150				error = 1;
151				optind++;
152				continue;
153			}
154		} else {
155			input = stdin;
156		}
157
158		if (quiet == 0) {
159			if (around)
160				(void) putchar('\n');
161
162			if (fileCount > 1 || verbose != 0) {
163				const char *file = argv[optind];
164				if (file == NULL) {
165					file = "standard input";
166				}
167				(void) printf("==> %s <==\n", file);
168			}
169		}
170
171		if (argv[optind] != NULL)
172			optind++;
173
174		copyout(linecnt, isline);
175		(void) fflush(stdout);
176		around++;
177
178	} while (argv[optind] != NULL);
179
180	return (error);
181}
182
183static void
184copyout(off_t cnt, int isline)
185{
186	char lbuf[BUFSIZ];
187	size_t len;
188
189	while (cnt > 0) {
190		len = fread(lbuf, sizeof (char), sizeof (lbuf) / sizeof (char),
191		    input);
192		if (len == 0) {
193			return;
194		}
195
196		if (isline) {
197			size_t i;
198			for (i = 0; i < len; i++) {
199				if (lbuf[i] == '\n') {
200					cnt--;
201					if (cnt == 0) {
202						i++;
203						break;
204					}
205				}
206			}
207			len = i;
208		} else {
209			if (len > cnt) {
210				len = cnt;
211			}
212			cnt -= len;
213		}
214
215		if (fwrite(lbuf, sizeof (char), len, stdout) != len) {
216			err(EXIT_FAILURE, "failed to write to stdout");
217		}
218	}
219}
220
221static void
222Usage()
223{
224	(void) printf(gettext("usage: head [-q] [-v] [-n #] [-c #] [-#] "
225	    "[filename...]\n"));
226	exit(1);
227}
228