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