logo

utils-std

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

strings.c (4308B)


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