logo

utils

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

strings.c (3481B)


  1. // Collection of Unix tools, comparable to coreutils
  2. // SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
  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 <stdio.h> /* fprintf(), BUFSIZ */
  9. #include <string.h> /* strerror(), strncmp(), memset() */
  10. #include <unistd.h> /* read(), write(), close(), getopt(), optarg, optind */
  11. #define _OPENBSD_SOURCE
  12. #include <stdlib.h> /* (BSD) strtonum() */
  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, "\nError 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, "\nError 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. opt_min_strlen = (size_t)strtonum(optarg, 1, 4096, &errstr);
  102. if(errstr)
  103. {
  104. fprintf(stderr, "Minimal string length is %s: %s\n", errstr, optarg);
  105. usage();
  106. return 1;
  107. }
  108. break;
  109. case 't':
  110. if(strnlen(optarg, 2) > 1)
  111. {
  112. usage();
  113. return 1;
  114. }
  115. switch(optarg[0])
  116. {
  117. case 'o':
  118. opt_offset_format = "%zo %s\n";
  119. break;
  120. case 'x':
  121. opt_offset_format = "%zx %s\n";
  122. break;
  123. case 'd':
  124. opt_offset_format = "%zd %s\n";
  125. break;
  126. default:
  127. fprintf(stderr, "Unknown format: %s\n", optarg);
  128. usage();
  129. return 1;
  130. }
  131. break;
  132. }
  133. }
  134. argc -= optind;
  135. argv += optind;
  136. if(argc < 1)
  137. {
  138. return concat(0, "<stdin>");
  139. }
  140. for(int argi = 0; argi < argc; argi++)
  141. {
  142. if(strncmp(argv[argi], "-", 2) == 0)
  143. {
  144. if(concat(0, "<stdin>") != 0)
  145. {
  146. return 1;
  147. }
  148. }
  149. else
  150. {
  151. int fd = open(argv[argi], O_RDONLY);
  152. if(fd <= 0)
  153. {
  154. fprintf(stderr, "\nError opening ‘%s’: %s\n", argv[argi], strerror(errno));
  155. return 1;
  156. }
  157. if(concat(fd, argv[argi]) != 0)
  158. {
  159. return 1;
  160. }
  161. if(close(fd) < 0)
  162. {
  163. fprintf(stderr, "\nError closing ‘%s’: %s\n", argv[argi], strerror(errno));
  164. return 1;
  165. }
  166. }
  167. }
  168. return 0;
  169. }