logo

utils

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

strings.c (3973B)


  1. // Collection of Unix tools, comparable to coreutils
  2. // SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include <ctype.h> /* isprint() */
  6. #include <errno.h> /* errno */
  7. #include <fcntl.h> /* open(), O_RDONLY */
  8. #include <limits.h> /* LONG_MIN, LONG_MAX */
  9. #include <stdio.h> /* fprintf(), BUFSIZ */
  10. #include <stdlib.h> /* strtol() */
  11. #include <string.h> /* strerror(), strncmp(), memset() */
  12. #include <unistd.h> /* read(), write(), close(), getopt(), optarg, optind */
  13. size_t opt_min_strlen = 4;
  14. char *opt_offset_format = NULL;
  15. int
  16. print_string(char *buffer, size_t offset)
  17. {
  18. int ret = 0;
  19. if(opt_offset_format == NULL)
  20. {
  21. ret = printf("%s\n", buffer);
  22. }
  23. else
  24. {
  25. /* flawfinder: ignore. opt_offset_format isn't user-provided */
  26. ret = printf(opt_offset_format, offset, buffer);
  27. }
  28. if(ret < 0)
  29. {
  30. fprintf(stderr, "strings: Error writing: %s\n", strerror(errno));
  31. return 1;
  32. }
  33. else
  34. {
  35. return 0;
  36. }
  37. }
  38. int
  39. concat(int fd, const char *fdname)
  40. {
  41. ssize_t c;
  42. char read_buf[4096];
  43. char write_buf[4096];
  44. size_t write_pos = 0;
  45. size_t offset = 0;
  46. memset(write_buf, 0, sizeof(write_buf));
  47. while((c = read(fd, read_buf, sizeof(read_buf))) > 0)
  48. {
  49. int read_pos = 0;
  50. char b = 0;
  51. for(; read_pos < c; read_pos++)
  52. {
  53. b = read_buf[read_pos];
  54. if(isprint(b) && write_pos < 4096)
  55. {
  56. write_buf[write_pos++] = b;
  57. }
  58. else
  59. {
  60. if(write_pos >= opt_min_strlen)
  61. {
  62. write_buf[write_pos + 1] = 0;
  63. if(print_string(write_buf, offset) != 0)
  64. {
  65. return 1;
  66. }
  67. }
  68. offset += write_pos;
  69. offset++;
  70. write_pos = 0;
  71. memset(write_buf, 0, sizeof(write_buf));
  72. }
  73. }
  74. }
  75. if(c < 0)
  76. {
  77. fprintf(stderr, "strings: Error reading ‘%s’: %s\n", fdname, strerror(errno));
  78. return 1;
  79. }
  80. return 0;
  81. }
  82. void
  83. usage()
  84. {
  85. fprintf(stderr, "strings: [-a] [-t format] [-n number] [file...]\n");
  86. }
  87. int
  88. main(int argc, char *argv[])
  89. {
  90. int c;
  91. const char *errstr = NULL;
  92. /* flawfinder: ignore. Old implementations of getopt should fix themselves */
  93. while((c = getopt(argc, argv, ":an:t:")) != -1)
  94. {
  95. switch(c)
  96. {
  97. case 'a':
  98. /* Structure is always ignored */
  99. break;
  100. case 'n':
  101. errno = 0;
  102. char *endptr = "";
  103. opt_min_strlen = strtol(optarg, &endptr, 10);
  104. if(*endptr != 0)
  105. {
  106. // extraneous characters is invalid
  107. errno = EINVAL;
  108. }
  109. if(errno != 0)
  110. {
  111. fprintf(stderr, "strings: Option `-n %s`: %s\n", optarg, strerror(errno));
  112. usage();
  113. return 1;
  114. }
  115. if(opt_min_strlen == LLONG_MIN || opt_min_strlen < 1)
  116. {
  117. fprintf(stderr, "strings: Option `-n %s` is too small\n", optarg);
  118. usage();
  119. return 1;
  120. }
  121. if(opt_min_strlen == LLONG_MAX || opt_min_strlen > 4096)
  122. {
  123. fprintf(stderr, "strings: Option `-n %s` is too large\n", optarg);
  124. usage();
  125. return 1;
  126. }
  127. break;
  128. case 't':
  129. if(strnlen(optarg, 2) > 1)
  130. {
  131. usage();
  132. return 1;
  133. }
  134. switch(optarg[0])
  135. {
  136. case 'o':
  137. opt_offset_format = "%zo %s\n";
  138. break;
  139. case 'x':
  140. opt_offset_format = "%zx %s\n";
  141. break;
  142. case 'd':
  143. opt_offset_format = "%zd %s\n";
  144. break;
  145. default:
  146. fprintf(stderr, "strings: Unknown format: %s\n", optarg);
  147. usage();
  148. return 1;
  149. }
  150. break;
  151. }
  152. }
  153. argc -= optind;
  154. argv += optind;
  155. if(argc < 1)
  156. {
  157. return concat(0, "<stdin>");
  158. }
  159. for(int argi = 0; argi < argc; argi++)
  160. {
  161. if(strncmp(argv[argi], "-", 2) == 0)
  162. {
  163. if(concat(0, "<stdin>") != 0)
  164. {
  165. return 1;
  166. }
  167. }
  168. else
  169. {
  170. int fd = open(argv[argi], O_RDONLY);
  171. if(fd <= 0)
  172. {
  173. fprintf(stderr, "strings: Error opening ‘%s’: %s\n", argv[argi], strerror(errno));
  174. return 1;
  175. }
  176. if(concat(fd, argv[argi]) != 0)
  177. {
  178. return 1;
  179. }
  180. if(close(fd) < 0)
  181. {
  182. fprintf(stderr, "strings: Error closing ‘%s’: %s\n", argv[argi], strerror(errno));
  183. return 1;
  184. }
  185. }
  186. }
  187. return 0;
  188. }