logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git

tee.c (2360B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include <assert.h> /* assert() */
  6. #include <errno.h> /* errno */
  7. #include <signal.h> /* signal() */
  8. #include <stdio.h> /* fprintf(), fgetc(), fputc(), fclose(), fopen() */
  9. #include <stdlib.h> /* calloc(), free(), abort() */
  10. #include <string.h> /* strerror() */
  11. #include <unistd.h> /* getopt(), opt… */
  12. static void
  13. cleanup(FILE **fds)
  14. {
  15. if(fds != NULL)
  16. {
  17. free(fds);
  18. }
  19. }
  20. int
  21. main(int argc, char *argv[])
  22. {
  23. const char *mode = "w";
  24. FILE **fds = {NULL}; // Shut up GCC
  25. int c;
  26. while((c = getopt(argc, argv, ":ai")) != -1)
  27. {
  28. switch(c)
  29. {
  30. case 'a':
  31. mode = "a";
  32. break;
  33. case 'i': /* ignore SIGINT */;
  34. signal(SIGINT, SIG_IGN);
  35. break;
  36. case ':':
  37. fprintf(stderr, "tee: error: Missing operand for option: '-%c'\n", optopt);
  38. return 1;
  39. case '?':
  40. fprintf(stderr, "tee: error: Unrecognised option: '-%c'\n", optopt);
  41. return 1;
  42. default:
  43. abort();
  44. }
  45. }
  46. argc -= optind;
  47. argv += optind;
  48. if(argc > 0)
  49. {
  50. fds = calloc(argc, sizeof(*fds));
  51. if(!fds)
  52. {
  53. fprintf(stderr, "tee: error: Failed to allocate fd array: %s\n", strerror(errno));
  54. return 1;
  55. }
  56. }
  57. for(int argi = 0; argi < argc; argi++)
  58. {
  59. assert(argv[argi]);
  60. // POSIX: implementations shouldn't treat '-' as stdin
  61. fds[argi] = fopen(argv[argi], mode);
  62. if(fds[argi] == NULL)
  63. {
  64. fprintf(stderr, "tee: error: Failed opening file ‘%s’: %s\n", argv[argi], strerror(errno));
  65. cleanup(fds);
  66. return 1;
  67. }
  68. }
  69. // main loop, note that failed writes shouldn't make tee exit
  70. int err = 0;
  71. while((c = fgetc(stdin)) != EOF)
  72. {
  73. if(fputc(c, stdout) == EOF)
  74. {
  75. fprintf(stderr, "tee: error: Failed writing to stdout: %s\n", strerror(errno));
  76. err = 1;
  77. errno = 0;
  78. }
  79. for(int argi = 0; argi < argc; argi++)
  80. {
  81. if(fputc(c, fds[argi]) == EOF)
  82. {
  83. fprintf(stderr, "tee: error: Failed writing to argument %d: %s\n", argi, strerror(errno));
  84. err = 1;
  85. errno = 0;
  86. }
  87. }
  88. }
  89. // cleanup
  90. for(int argi = 0; argi < argc; argi++)
  91. {
  92. if(fclose(fds[argi]) != 0)
  93. {
  94. fprintf(stderr, "tee: error: Failed closing file '%s': %s\n", argv[argi], strerror(errno));
  95. err++;
  96. }
  97. }
  98. cleanup(fds);
  99. return err;
  100. }