logo

utils

~/.local/bin tools and git-hooks git clone https://hacktivis.me/git/utils.git

tee.c (1961B)


  1. // Collection of Unix tools, comparable to coreutils
  2. // SPDX-FileCopyrightText: 2017-2022 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 <stdio.h> /* fprintf(), fgetc(), fputc(), fclose(), fopen() */
  8. #include <stdlib.h> /* malloc(), free(), abort() */
  9. #include <string.h> /* strerror() */
  10. #include <unistd.h> /* getopt(), opt… */
  11. void
  12. cleanup(FILE **fds)
  13. {
  14. if(fds != NULL)
  15. {
  16. free(fds);
  17. }
  18. }
  19. int
  20. main(int argc, char *argv[])
  21. {
  22. const char *mode = "w";
  23. FILE **fds = {NULL}; // Shut up GCC
  24. int c;
  25. while((c = getopt(argc, argv, ":ai")) != -1)
  26. {
  27. switch(c)
  28. {
  29. case 'a':
  30. mode = "a";
  31. break;
  32. case 'i': /* ignore SIGINT */;
  33. break;
  34. }
  35. }
  36. argc -= optind;
  37. argv += optind;
  38. if(argc > 0)
  39. {
  40. fds = malloc(sizeof(FILE *) * (size_t)argc);
  41. if(!fds)
  42. {
  43. fprintf(stderr, "tee: Cannot allocate fd array: %s\n", strerror(errno));
  44. return 1;
  45. }
  46. }
  47. for(int argi = 0; argi < argc; argi++)
  48. {
  49. assert(argv[argi]);
  50. // POSIX: implementations shouldn't treat '-' as stdin
  51. fds[argi] = fopen(argv[argi], mode);
  52. if(fds[argi] == NULL)
  53. {
  54. fprintf(stderr, "tee: Error opening ‘%s’: %s\n", argv[argi], strerror(errno));
  55. cleanup(fds);
  56. return 1;
  57. }
  58. }
  59. // main loop, note that failed writes shouldn't make tee exit
  60. int err = 0;
  61. while((c = fgetc(stdin)) != EOF)
  62. {
  63. if(fputc(c, stdout) == EOF)
  64. {
  65. fprintf(stderr, "tee: Error writing ‘<stdout>’: %s\n", strerror(errno));
  66. err = 1;
  67. errno = 0;
  68. }
  69. for(int argi = 0; argi < argc; argi++)
  70. {
  71. if(fputc(c, fds[argi]) == EOF)
  72. {
  73. fprintf(stderr, "tee: Error writing to argument %d: %s\n", argi, strerror(errno));
  74. err = 1;
  75. errno = 0;
  76. }
  77. }
  78. }
  79. // cleanup
  80. for(int argi = 0; argi < argc; argi++)
  81. {
  82. if(fclose(fds[argi]) != 0)
  83. {
  84. abort();
  85. }
  86. }
  87. cleanup(fds);
  88. return err;
  89. }