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