#include #include #include #include #include #include /* -A, --show-all equivalent to -vET -b, --number-nonblank number nonempty output lines, overrides -n -e equivalent to -vE -E, --show-ends display $ at end of each line -n, --number number all output lines -s, --squeeze-blank suppress repeated empty output lines -t equivalent to -vT -T, --show-tabs display TAB characters as ^I -u (ignored) -v, --show-nonprinting use ^ and M- notation, except for LFD and TAB */ static unsigned long long delay_nsecs = 0; static unsigned chunk_size = 4096; static void calc_timespec(struct timespec *ts, unsigned long long nsecs) { ts->tv_sec = nsecs / 1000000000; ts->tv_nsec = nsecs % 1000000000; } static void do_it(int fd, const char *fn, unsigned chunk_size) { char inbuf[chunk_size]; const size_t max = sizeof(inbuf); size_t len = 0; ssize_t n; int done = 0; while (!done) { // fill buffer assert(max > len); n = read(fd, inbuf + len, max - len); if (n < 0) { perror(fn); exit(EXIT_FAILURE); } if (!n) { // closed done = 1; continue; } len += n; // empty buffer size_t i = 0; while (i < len) { struct timespec ts; size_t n = len - i; calc_timespec(&ts, delay_nsecs * n); nanosleep(&ts, NULL); n = write(STDOUT_FILENO, inbuf + i, n); assert(n != 0); if (n < 0) { perror("slowcat"); exit(EXIT_FAILURE); } i += n; } assert(len == i); len -= i; } } int main(int argc, char *argv[]) { int i; int opt; while ((opt = getopt(argc, argv, "AbeEnstTuvd:c:")) != -1) { switch (opt) { case 'd': assert(optarg != NULL); delay_nsecs = strtoull(optarg, NULL, 0); break; case 'c': assert(optarg != NULL); chunk_size = strtoul(optarg, NULL, 0); break; default: fprintf(stderr, "Usage: %s [-d delay_nsecs] [-c chunk_size] [files ... ]\n", argv[0]); exit(EXIT_FAILURE); } } if (optind >= argc) { do_it(STDIN_FILENO, "", chunk_size); } else for (;optind < argc; optind++) { const char *fn = argv[optind]; int fd = open(fn, O_RDONLY); if (fd < 0) { perror(fn); exit(EXIT_FAILURE); } do_it(fd, fn, chunk_size); } return 0; }