logo

utils-std

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

strings.c (4675B)


  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 <ctype.h> /* isprint() */
  8. #include <errno.h> /* errno */
  9. #include <fcntl.h> /* open(), O_RDONLY */
  10. #include <limits.h> /* LONG_MAX */
  11. #include <stdio.h> /* fprintf(), BUFSIZ */
  12. #include <stdlib.h> /* strtol() */
  13. #include <string.h> /* strerror(), strncmp() */
  14. #include <unistd.h> /* read(), write(), close() */
  15. #ifdef HAS_GETOPT_LONG
  16. #include <getopt.h>
  17. #endif
  18. const char *argv0 = "strings";
  19. size_t opt_min_strlen = 4;
  20. const char *opt_offset_format = NULL;
  21. char delim = '\n';
  22. static int
  23. print_string(char *buffer, size_t buflen, size_t offset)
  24. {
  25. int ret = 0;
  26. int wrote = 0;
  27. if(opt_offset_format)
  28. {
  29. ret = printf(opt_offset_format, offset);
  30. if(ret < 0) return ret;
  31. wrote += ret;
  32. }
  33. ret = fwrite(buffer, buflen, 1, stdout);
  34. if(ret < 0) return ret;
  35. wrote += ret;
  36. return wrote;
  37. }
  38. static int
  39. concat(int fd, const char *fdname)
  40. {
  41. ssize_t c;
  42. static char read_buf[4096];
  43. static char write_buf[4096];
  44. size_t write_pos = 0;
  45. size_t offset = 0;
  46. while((c = read(fd, read_buf, sizeof(read_buf))) > 0)
  47. {
  48. int read_pos = 0;
  49. for(; read_pos < c; read_pos++)
  50. {
  51. char b = read_buf[read_pos];
  52. if(isprint(b))
  53. {
  54. write_buf[write_pos++] = b;
  55. if(write_pos < (sizeof(write_buf) - 1)) continue;
  56. }
  57. if(write_pos >= opt_min_strlen)
  58. {
  59. write_buf[write_pos] = delim;
  60. if(print_string(write_buf, write_pos + 1, offset) < 0)
  61. {
  62. fprintf(stderr, "strings: error: Failed writing resulting string: %s\n", strerror(errno));
  63. return 1;
  64. }
  65. }
  66. offset += write_pos;
  67. offset++;
  68. write_pos = 0;
  69. }
  70. }
  71. if(c < 0)
  72. {
  73. fprintf(stderr, "strings: error: Failed reading ‘%s’: %s\n", fdname, strerror(errno));
  74. errno = 0;
  75. return 1;
  76. }
  77. return 0;
  78. }
  79. static void
  80. usage(void)
  81. {
  82. fprintf(stderr, "strings: [-az] [-t format] [-n number] [file...]\n");
  83. }
  84. int
  85. main(int argc, char *argv[])
  86. {
  87. #ifdef HAS_GETOPT_LONG
  88. // Strictly for GNUisms compatibility so no long-only options
  89. // clang-format off
  90. static struct option opts[] = {
  91. {"all", no_argument, NULL, 'a'},
  92. {"bytes", required_argument, NULL, 'n'},
  93. {"radix", required_argument, NULL, 't'},
  94. {0, 0, 0, 0},
  95. };
  96. // clang-format on
  97. // Need + as first character to get POSIX-style option parsing
  98. for(int c = -1; (c = getopt_long(argc, argv, "+:an:t:z", opts, NULL)) != -1;)
  99. #else
  100. for(int c = -1; (c = getopt_nolong(argc, argv, ":an:t:z")) != -1;)
  101. #endif
  102. {
  103. char *endptr = NULL;
  104. switch(c)
  105. {
  106. case 'a':
  107. /* Structure is always ignored */
  108. break;
  109. case 'n':
  110. opt_min_strlen = strtol(optarg, &endptr, 10);
  111. if(endptr && *endptr != 0)
  112. {
  113. // extraneous characters is invalid
  114. errno = EINVAL;
  115. }
  116. if(errno != 0)
  117. {
  118. fprintf(stderr, "strings: error: Option `-n %s`: %s\n", optarg, strerror(errno));
  119. usage();
  120. return 1;
  121. }
  122. if(opt_min_strlen < 1)
  123. {
  124. fprintf(stderr, "strings: error: Option `-n %s` is too small\n", optarg);
  125. usage();
  126. return 1;
  127. }
  128. if(opt_min_strlen == LONG_MAX || opt_min_strlen > 4096)
  129. {
  130. fprintf(stderr, "strings: error: Option `-n %s` is too large\n", optarg);
  131. usage();
  132. return 1;
  133. }
  134. break;
  135. case 't':
  136. if(strnlen(optarg, 2) > 1)
  137. {
  138. usage();
  139. return 1;
  140. }
  141. switch(optarg[0])
  142. {
  143. case 'o':
  144. opt_offset_format = "%zo ";
  145. break;
  146. case 'x':
  147. opt_offset_format = "%zx ";
  148. break;
  149. case 'd':
  150. opt_offset_format = "%zd ";
  151. break;
  152. default:
  153. fprintf(stderr, "strings: error: Unknown format: %s\n", optarg);
  154. usage();
  155. return 1;
  156. }
  157. break;
  158. case 'z':
  159. delim = '\0';
  160. break;
  161. case ':':
  162. fprintf(stderr, "strings: error: Missing operand for option: '-%c'\n", optopt);
  163. usage();
  164. return 1;
  165. case '?':
  166. GETOPT_UNKNOWN_OPT
  167. usage();
  168. return 1;
  169. default:
  170. abort();
  171. }
  172. }
  173. argc -= optind;
  174. argv += optind;
  175. if(argc < 1)
  176. {
  177. return concat(0, "<stdin>");
  178. }
  179. for(int argi = 0; argi < argc; argi++)
  180. {
  181. if(strncmp(argv[argi], "-", 2) == 0)
  182. {
  183. if(concat(0, "<stdin>") != 0)
  184. {
  185. return 1;
  186. }
  187. }
  188. else
  189. {
  190. int fd = open(argv[argi], O_RDONLY);
  191. if(fd < 0)
  192. {
  193. fprintf(stderr, "strings: error: Failed opening ‘%s’: %s\n", argv[argi], strerror(errno));
  194. return 1;
  195. }
  196. if(concat(fd, argv[argi]) != 0)
  197. {
  198. return 1;
  199. }
  200. if(close(fd) < 0)
  201. {
  202. fprintf(stderr, "strings: error: Failed closing ‘%s’: %s\n", argv[argi], strerror(errno));
  203. return 1;
  204. }
  205. }
  206. }
  207. return 0;
  208. }