cat.c (2788B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #define _GNU_SOURCE // splice
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h> // open, O_RDONLY, splice
- #include <stdint.h> // SIZE_MAX
- #include <stdio.h> // fprintf, BUFSIZ
- #include <stdlib.h> // abort
- #include <string.h> // strerror, strncmp
- #include <unistd.h> // read, write, close, getopt
- static int
- concat(int fd, const char *fdname)
- {
- ssize_t c;
- char buf[BUFSIZ];
- assert(errno == 0);
- while((c = read(fd, buf, sizeof(buf))) > 0)
- {
- assert(errno == 0);
- if(write(1, buf, (size_t)c) < 0)
- {
- fprintf(stderr, "cat: Error writing: %s\n", strerror(errno));
- errno = 0;
- return 1;
- }
- }
- if(c < 0)
- {
- fprintf(stderr, "cat: Error reading ‘%s’: %s\n", fdname, strerror(errno));
- errno = 0;
- return 1;
- }
- return 0;
- }
- #ifdef HAS_SPLICE
- static int
- fd_copy(int fd, const char *fdname)
- {
- while(1)
- {
- assert(errno == 0);
- ssize_t ret =
- splice(fd, NULL, 1, NULL, SIZE_MAX, SPLICE_F_MOVE | SPLICE_F_NONBLOCK | SPLICE_F_MORE);
- if(ret == 0) break;
- if(ret < 0)
- {
- switch(errno)
- {
- case EINVAL:
- errno = 0;
- return concat(fd, fdname);
- case EAGAIN:
- errno = 0;
- continue;
- default:
- fprintf(stderr, "cat: Error copying ‘%s’: %s\n", fdname, strerror(errno));
- errno = 0;
- return 1;
- }
- }
- }
- return 0;
- }
- #else // HAS_SPLICE
- #define fd_copy concat
- #endif // HAS_SPLICE
- static void
- usage()
- {
- fprintf(stderr, "Usage: cat [-u] [files ...]\n");
- }
- int
- main(int argc, char *argv[])
- {
- int c = -1;
- while((c = getopt(argc, argv, ":u")) != -1)
- {
- switch(c)
- {
- case 'u':
- // POSIX: Ignored, buffered streams aren't used
- break;
- case ':':
- fprintf(stderr, "cat: Error: Missing operand for option: '-%c'\n", optopt);
- usage();
- return 1;
- case '?':
- fprintf(stderr, "cat: Error: Unrecognised option: '-%c'\n", optopt);
- usage();
- return 1;
- default:
- abort();
- }
- }
- argc -= optind;
- argv += optind;
- if(argc < 1)
- {
- return fd_copy(0, "<stdin>");
- }
- for(int argi = 0; argi < argc; argi++)
- {
- if(strncmp(argv[argi], "-", 2) == 0)
- {
- if(fd_copy(0, "<stdin>") != 0)
- {
- return 1;
- }
- }
- else
- {
- assert(errno == 0);
- int fd = open(argv[argi], O_RDONLY);
- if(fd < 0)
- {
- fprintf(stderr, "cat: Error opening ‘%s’: %s\n", argv[argi], strerror(errno));
- errno = 0;
- return 1;
- }
- if(fd_copy(fd, argv[argi]) != 0)
- {
- return 1;
- }
- assert(errno == 0);
- if(close(fd) < 0)
- {
- fprintf(stderr, "cat: Error closing ‘%s’: %s\n", argv[argi], strerror(errno));
- errno = 0;
- return 1;
- }
- }
- }
- return 0;
- }