xref: /freebsd/tests/sys/kern/tty/readsz.c (revision 096c39fae4ad5135a317925d8749b7d83f65ebf8)
1*096c39faSKyle Evans /*-
2*096c39faSKyle Evans  * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
3*096c39faSKyle Evans  *
4*096c39faSKyle Evans  * SPDX-License-Identifier: BSD-2-Clause
5*096c39faSKyle Evans  */
6*096c39faSKyle Evans 
7*096c39faSKyle Evans #include <sys/param.h>
8*096c39faSKyle Evans 
9*096c39faSKyle Evans #include <err.h>
10*096c39faSKyle Evans #include <errno.h>
11*096c39faSKyle Evans #include <limits.h>
12*096c39faSKyle Evans #include <stdbool.h>
13*096c39faSKyle Evans #include <stdint.h>
14*096c39faSKyle Evans #include <stdio.h>
15*096c39faSKyle Evans #include <stdlib.h>
16*096c39faSKyle Evans #include <unistd.h>
17*096c39faSKyle Evans 
18*096c39faSKyle Evans static void
usage(void)19*096c39faSKyle Evans usage(void)
20*096c39faSKyle Evans {
21*096c39faSKyle Evans 
22*096c39faSKyle Evans 	fprintf(stderr, "usage: %s [-b bytes | -c lines | -e] [-s buffer-size]\n",
23*096c39faSKyle Evans 	    getprogname());
24*096c39faSKyle Evans 	exit(1);
25*096c39faSKyle Evans }
26*096c39faSKyle Evans 
27*096c39faSKyle Evans int
main(int argc,char * argv[])28*096c39faSKyle Evans main(int argc, char *argv[])
29*096c39faSKyle Evans {
30*096c39faSKyle Evans 	char *buf;
31*096c39faSKyle Evans 	const char *errstr;
32*096c39faSKyle Evans 	size_t bufsz = 0, reps;
33*096c39faSKyle Evans 	ssize_t ret;
34*096c39faSKyle Evans 	enum { MODE_BYTES, MODE_COUNT, MODE_EOF } mode;
35*096c39faSKyle Evans 	int ch;
36*096c39faSKyle Evans 
37*096c39faSKyle Evans 	/*
38*096c39faSKyle Evans 	 * -b specifies number of bytes.
39*096c39faSKyle Evans 	 * -c specifies number of read() calls.
40*096c39faSKyle Evans 	 * -e specifies eof (default)
41*096c39faSKyle Evans 	 * -s to pass a buffer size
42*096c39faSKyle Evans 	 *
43*096c39faSKyle Evans 	 * Reading N lines is the same as -c with a high buffer size.
44*096c39faSKyle Evans 	 */
45*096c39faSKyle Evans 	mode = MODE_EOF;
46*096c39faSKyle Evans 	while ((ch = getopt(argc, argv, "b:c:es:")) != -1) {
47*096c39faSKyle Evans 		switch (ch) {
48*096c39faSKyle Evans 		case 'b':
49*096c39faSKyle Evans 			mode = MODE_BYTES;
50*096c39faSKyle Evans 			reps = strtonum(optarg, 0, SSIZE_MAX, &errstr);
51*096c39faSKyle Evans 			if (errstr != NULL)
52*096c39faSKyle Evans 				errx(1, "strtonum: %s", errstr);
53*096c39faSKyle Evans 			break;
54*096c39faSKyle Evans 		case 'c':
55*096c39faSKyle Evans 			mode = MODE_COUNT;
56*096c39faSKyle Evans 			reps = strtonum(optarg, 1, SSIZE_MAX, &errstr);
57*096c39faSKyle Evans 			if (errstr != NULL)
58*096c39faSKyle Evans 				errx(1, "strtonum: %s", errstr);
59*096c39faSKyle Evans 			break;
60*096c39faSKyle Evans 		case 'e':
61*096c39faSKyle Evans 			mode = MODE_EOF;
62*096c39faSKyle Evans 			break;
63*096c39faSKyle Evans 		case 's':
64*096c39faSKyle Evans 			bufsz = strtonum(optarg, 1, SSIZE_MAX, &errstr);
65*096c39faSKyle Evans 			if (errstr != NULL)
66*096c39faSKyle Evans 				errx(1, "strtonum: %s", errstr);
67*096c39faSKyle Evans 			break;
68*096c39faSKyle Evans 		default:
69*096c39faSKyle Evans 			usage();
70*096c39faSKyle Evans 		}
71*096c39faSKyle Evans 	}
72*096c39faSKyle Evans 
73*096c39faSKyle Evans 	if (bufsz == 0) {
74*096c39faSKyle Evans 		if (mode == MODE_BYTES)
75*096c39faSKyle Evans 			bufsz = reps;
76*096c39faSKyle Evans 		else
77*096c39faSKyle Evans 			bufsz = LINE_MAX;
78*096c39faSKyle Evans 	}
79*096c39faSKyle Evans 
80*096c39faSKyle Evans 	buf = malloc(bufsz);
81*096c39faSKyle Evans 	if (buf == NULL)
82*096c39faSKyle Evans 		err(1, "malloc");
83*096c39faSKyle Evans 
84*096c39faSKyle Evans 	for (;;) {
85*096c39faSKyle Evans 		size_t readsz;
86*096c39faSKyle Evans 
87*096c39faSKyle Evans 		/*
88*096c39faSKyle Evans 		 * Be careful not to over-read if we're in byte-mode.  In every other
89*096c39faSKyle Evans 		 * mode, we'll read as much as we can.
90*096c39faSKyle Evans 		 */
91*096c39faSKyle Evans 		if (mode == MODE_BYTES)
92*096c39faSKyle Evans 			readsz = MIN(bufsz, reps);
93*096c39faSKyle Evans 		else
94*096c39faSKyle Evans 			readsz = bufsz;
95*096c39faSKyle Evans 
96*096c39faSKyle Evans 		ret = read(STDIN_FILENO, buf, readsz);
97*096c39faSKyle Evans 		if (ret == -1 && errno == EINTR)
98*096c39faSKyle Evans 			continue;
99*096c39faSKyle Evans 		if (ret == -1)
100*096c39faSKyle Evans 			err(1, "read");
101*096c39faSKyle Evans 		if (ret == 0) {
102*096c39faSKyle Evans 			if (mode == MODE_EOF)
103*096c39faSKyle Evans 				return (0);
104*096c39faSKyle Evans 			errx(1, "premature EOF");
105*096c39faSKyle Evans 		}
106*096c39faSKyle Evans 
107*096c39faSKyle Evans 		/* Write out what we've got */
108*096c39faSKyle Evans 		write(STDOUT_FILENO, buf, ret);
109*096c39faSKyle Evans 
110*096c39faSKyle Evans 		/*
111*096c39faSKyle Evans 		 * Bail out if we've hit our metric (byte mode / count mode).
112*096c39faSKyle Evans 		 */
113*096c39faSKyle Evans 		switch (mode) {
114*096c39faSKyle Evans 		case MODE_BYTES:
115*096c39faSKyle Evans 			reps -= ret;
116*096c39faSKyle Evans 			if (reps == 0)
117*096c39faSKyle Evans 				return (0);
118*096c39faSKyle Evans 			break;
119*096c39faSKyle Evans 		case MODE_COUNT:
120*096c39faSKyle Evans 			reps--;
121*096c39faSKyle Evans 			if (reps == 0)
122*096c39faSKyle Evans 				return (0);
123*096c39faSKyle Evans 			break;
124*096c39faSKyle Evans 		default:
125*096c39faSKyle Evans 			break;
126*096c39faSKyle Evans 		}
127*096c39faSKyle Evans 	}
128*096c39faSKyle Evans 
129*096c39faSKyle Evans 	return (0);
130*096c39faSKyle Evans }
131