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 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 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