logo

utils-std

Collection of commonly available Unix tools

cat.c (2788B)


  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. #define _GNU_SOURCE // splice
  6. #include <assert.h>
  7. #include <errno.h>
  8. #include <fcntl.h> // open, O_RDONLY, splice
  9. #include <stdint.h> // SIZE_MAX
  10. #include <stdio.h> // fprintf, BUFSIZ
  11. #include <stdlib.h> // abort
  12. #include <string.h> // strerror, strncmp
  13. #include <unistd.h> // read, write, close, getopt
  14. static int
  15. concat(int fd, const char *fdname)
  16. {
  17. ssize_t c;
  18. char buf[BUFSIZ];
  19. assert(errno == 0);
  20. while((c = read(fd, buf, sizeof(buf))) > 0)
  21. {
  22. assert(errno == 0);
  23. if(write(1, buf, (size_t)c) < 0)
  24. {
  25. fprintf(stderr, "cat: Error writing: %s\n", strerror(errno));
  26. errno = 0;
  27. return 1;
  28. }
  29. }
  30. if(c < 0)
  31. {
  32. fprintf(stderr, "cat: Error reading ‘%s’: %s\n", fdname, strerror(errno));
  33. errno = 0;
  34. return 1;
  35. }
  36. return 0;
  37. }
  38. #ifdef HAS_SPLICE
  39. static int
  40. fd_copy(int fd, const char *fdname)
  41. {
  42. while(1)
  43. {
  44. assert(errno == 0);
  45. ssize_t ret =
  46. splice(fd, NULL, 1, NULL, SIZE_MAX, SPLICE_F_MOVE | SPLICE_F_NONBLOCK | SPLICE_F_MORE);
  47. if(ret == 0) break;
  48. if(ret < 0)
  49. {
  50. switch(errno)
  51. {
  52. case EINVAL:
  53. errno = 0;
  54. return concat(fd, fdname);
  55. case EAGAIN:
  56. errno = 0;
  57. continue;
  58. default:
  59. fprintf(stderr, "cat: Error copying ‘%s’: %s\n", fdname, strerror(errno));
  60. errno = 0;
  61. return 1;
  62. }
  63. }
  64. }
  65. return 0;
  66. }
  67. #else // HAS_SPLICE
  68. #define fd_copy concat
  69. #endif // HAS_SPLICE
  70. static void
  71. usage()
  72. {
  73. fprintf(stderr, "Usage: cat [-u] [files ...]\n");
  74. }
  75. int
  76. main(int argc, char *argv[])
  77. {
  78. int c = -1;
  79. while((c = getopt(argc, argv, ":u")) != -1)
  80. {
  81. switch(c)
  82. {
  83. case 'u':
  84. // POSIX: Ignored, buffered streams aren't used
  85. break;
  86. case ':':
  87. fprintf(stderr, "cat: Error: Missing operand for option: '-%c'\n", optopt);
  88. usage();
  89. return 1;
  90. case '?':
  91. fprintf(stderr, "cat: Error: Unrecognised option: '-%c'\n", optopt);
  92. usage();
  93. return 1;
  94. default:
  95. abort();
  96. }
  97. }
  98. argc -= optind;
  99. argv += optind;
  100. if(argc < 1)
  101. {
  102. return fd_copy(0, "<stdin>");
  103. }
  104. for(int argi = 0; argi < argc; argi++)
  105. {
  106. if(strncmp(argv[argi], "-", 2) == 0)
  107. {
  108. if(fd_copy(0, "<stdin>") != 0)
  109. {
  110. return 1;
  111. }
  112. }
  113. else
  114. {
  115. assert(errno == 0);
  116. int fd = open(argv[argi], O_RDONLY);
  117. if(fd < 0)
  118. {
  119. fprintf(stderr, "cat: Error opening ‘%s’: %s\n", argv[argi], strerror(errno));
  120. errno = 0;
  121. return 1;
  122. }
  123. if(fd_copy(fd, argv[argi]) != 0)
  124. {
  125. return 1;
  126. }
  127. assert(errno == 0);
  128. if(close(fd) < 0)
  129. {
  130. fprintf(stderr, "cat: Error closing ‘%s’: %s\n", argv[argi], strerror(errno));
  131. errno = 0;
  132. return 1;
  133. }
  134. }
  135. }
  136. return 0;
  137. }