tee.c (2360B)
- // 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
- #include <assert.h> /* assert() */
- #include <errno.h> /* errno */
- #include <signal.h> /* signal() */
- #include <stdio.h> /* fprintf(), fgetc(), fputc(), fclose(), fopen() */
- #include <stdlib.h> /* calloc(), free(), abort() */
- #include <string.h> /* strerror() */
- #include <unistd.h> /* getopt(), opt… */
- static void
- cleanup(FILE **fds)
- {
- if(fds != NULL)
- {
- free(fds);
- }
- }
- int
- main(int argc, char *argv[])
- {
- const char *mode = "w";
- FILE **fds = {NULL}; // Shut up GCC
- int c;
- while((c = getopt(argc, argv, ":ai")) != -1)
- {
- switch(c)
- {
- case 'a':
- mode = "a";
- break;
- case 'i': /* ignore SIGINT */;
- signal(SIGINT, SIG_IGN);
- break;
- case ':':
- fprintf(stderr, "tee: error: Missing operand for option: '-%c'\n", optopt);
- return 1;
- case '?':
- fprintf(stderr, "tee: error: Unrecognised option: '-%c'\n", optopt);
- return 1;
- default:
- abort();
- }
- }
- argc -= optind;
- argv += optind;
- if(argc > 0)
- {
- fds = calloc(argc, sizeof(*fds));
- if(!fds)
- {
- fprintf(stderr, "tee: error: Failed to allocate fd array: %s\n", strerror(errno));
- return 1;
- }
- }
- for(int argi = 0; argi < argc; argi++)
- {
- assert(argv[argi]);
- // POSIX: implementations shouldn't treat '-' as stdin
- fds[argi] = fopen(argv[argi], mode);
- if(fds[argi] == NULL)
- {
- fprintf(stderr, "tee: error: Failed opening file ‘%s’: %s\n", argv[argi], strerror(errno));
- cleanup(fds);
- return 1;
- }
- }
- // main loop, note that failed writes shouldn't make tee exit
- int err = 0;
- while((c = fgetc(stdin)) != EOF)
- {
- if(fputc(c, stdout) == EOF)
- {
- fprintf(stderr, "tee: error: Failed writing to stdout: %s\n", strerror(errno));
- err = 1;
- errno = 0;
- }
- for(int argi = 0; argi < argc; argi++)
- {
- if(fputc(c, fds[argi]) == EOF)
- {
- fprintf(stderr, "tee: error: Failed writing to argument %d: %s\n", argi, strerror(errno));
- err = 1;
- errno = 0;
- }
- }
- }
- // cleanup
- for(int argi = 0; argi < argc; argi++)
- {
- if(fclose(fds[argi]) != 0)
- {
- fprintf(stderr, "tee: error: Failed closing file '%s': %s\n", argv[argi], strerror(errno));
- err++;
- }
- }
- cleanup(fds);
- return err;
- }