logo

utils-std

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

tee.c (2901B)


  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 "../config.h"
  6. #include "../libutils/getopt_nolong.h"
  7. #include <assert.h> /* assert() */
  8. #include <errno.h> /* errno */
  9. #include <signal.h> /* signal() */
  10. #include <stdio.h> /* fprintf(), fgetc(), fputc(), fclose(), fopen() */
  11. #include <stdlib.h> /* calloc(), free(), abort() */
  12. #include <string.h> /* strerror() */
  13. #include <unistd.h> /* getopt(), opt… */
  14. #ifdef HAS_GETOPT_LONG
  15. #include <getopt.h>
  16. #endif
  17. const char *argv0 = "tee";
  18. static void
  19. cleanup(FILE **fds)
  20. {
  21. if(fds != NULL)
  22. {
  23. free(fds);
  24. }
  25. }
  26. int
  27. main(int argc, char *argv[])
  28. {
  29. const char *mode = "w";
  30. FILE **fds = {NULL}; // Shut up GCC
  31. int c;
  32. #ifdef HAS_GETOPT_LONG
  33. // Strictly for GNUisms compatibility so no long-only options
  34. // clang-format off
  35. static struct option opts[] = {
  36. {"append", required_argument, NULL, 'a'},
  37. {"ignore-interrupts", required_argument, NULL, 'i'},
  38. {0, 0, 0, 0},
  39. };
  40. // clang-format on
  41. // Need + as first character to get POSIX-style option parsing
  42. for(int c = -1; (c = getopt_long(argc, argv, "+:ai", opts, NULL)) != -1;)
  43. #else
  44. for(int c = -1; (c = getopt_nolong(argc, argv, ":ai")) != -1;)
  45. #endif
  46. {
  47. switch(c)
  48. {
  49. case 'a':
  50. mode = "a";
  51. break;
  52. case 'i': /* ignore SIGINT */;
  53. signal(SIGINT, SIG_IGN);
  54. break;
  55. case ':':
  56. fprintf(stderr, "tee: error: Missing operand for option: '-%c'\n", optopt);
  57. return 1;
  58. case '?':
  59. GETOPT_UNKNOWN_OPT
  60. return 1;
  61. default:
  62. abort();
  63. }
  64. }
  65. argc -= optind;
  66. argv += optind;
  67. if(argc > 0)
  68. {
  69. fds = calloc(argc, sizeof(*fds));
  70. if(!fds)
  71. {
  72. fprintf(stderr, "tee: error: Failed to allocate fd array: %s\n", strerror(errno));
  73. return 1;
  74. }
  75. }
  76. for(int argi = 0; argi < argc; argi++)
  77. {
  78. assert(argv[argi]);
  79. // POSIX: implementations shouldn't treat '-' as stdin
  80. fds[argi] = fopen(argv[argi], mode);
  81. if(fds[argi] == NULL)
  82. {
  83. fprintf(stderr, "tee: error: Failed opening file ‘%s’: %s\n", argv[argi], strerror(errno));
  84. cleanup(fds);
  85. return 1;
  86. }
  87. }
  88. // main loop, note that failed writes shouldn't make tee exit
  89. int err = 0;
  90. while((c = fgetc(stdin)) != EOF)
  91. {
  92. if(fputc(c, stdout) == EOF)
  93. {
  94. fprintf(stderr, "tee: error: Failed writing to stdout: %s\n", strerror(errno));
  95. err = 1;
  96. errno = 0;
  97. }
  98. for(int argi = 0; argi < argc; argi++)
  99. {
  100. if(fputc(c, fds[argi]) == EOF)
  101. {
  102. fprintf(stderr, "tee: error: Failed writing to argument %d: %s\n", argi, strerror(errno));
  103. err = 1;
  104. errno = 0;
  105. }
  106. }
  107. }
  108. // cleanup
  109. for(int argi = 0; argi < argc; argi++)
  110. {
  111. if(fclose(fds[argi]) != 0)
  112. {
  113. fprintf(stderr, "tee: error: Failed closing file '%s': %s\n", argv[argi], strerror(errno));
  114. err++;
  115. }
  116. }
  117. cleanup(fds);
  118. return err;
  119. }