logo

utils-std

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

strings.c (4144B)


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