strings.c (3973B)
- // Collection of Unix tools, comparable to coreutils
 - // SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
 - // SPDX-License-Identifier: MPL-2.0
 - #define _POSIX_C_SOURCE 200809L
 - #include <ctype.h> /* isprint() */
 - #include <errno.h> /* errno */
 - #include <fcntl.h> /* open(), O_RDONLY */
 - #include <limits.h> /* LONG_MIN, LONG_MAX */
 - #include <stdio.h> /* fprintf(), BUFSIZ */
 - #include <stdlib.h> /* strtol() */
 - #include <string.h> /* strerror(), strncmp(), memset() */
 - #include <unistd.h> /* read(), write(), close(), getopt(), optarg, optind */
 - size_t opt_min_strlen = 4;
 - char *opt_offset_format = NULL;
 - int
 - print_string(char *buffer, size_t offset)
 - {
 - int ret = 0;
 - if(opt_offset_format == NULL)
 - {
 - ret = printf("%s\n", buffer);
 - }
 - else
 - {
 - /* flawfinder: ignore. opt_offset_format isn't user-provided */
 - ret = printf(opt_offset_format, offset, buffer);
 - }
 - if(ret < 0)
 - {
 - fprintf(stderr, "strings: Error writing: %s\n", strerror(errno));
 - return 1;
 - }
 - else
 - {
 - return 0;
 - }
 - }
 - int
 - concat(int fd, const char *fdname)
 - {
 - ssize_t c;
 - char read_buf[4096];
 - char write_buf[4096];
 - size_t write_pos = 0;
 - size_t offset = 0;
 - memset(write_buf, 0, sizeof(write_buf));
 - while((c = read(fd, read_buf, sizeof(read_buf))) > 0)
 - {
 - int read_pos = 0;
 - char b = 0;
 - for(; read_pos < c; read_pos++)
 - {
 - b = read_buf[read_pos];
 - if(isprint(b) && write_pos < 4096)
 - {
 - write_buf[write_pos++] = b;
 - }
 - else
 - {
 - if(write_pos >= opt_min_strlen)
 - {
 - write_buf[write_pos + 1] = 0;
 - if(print_string(write_buf, offset) != 0)
 - {
 - return 1;
 - }
 - }
 - offset += write_pos;
 - offset++;
 - write_pos = 0;
 - memset(write_buf, 0, sizeof(write_buf));
 - }
 - }
 - }
 - if(c < 0)
 - {
 - fprintf(stderr, "strings: Error reading ‘%s’: %s\n", fdname, strerror(errno));
 - return 1;
 - }
 - return 0;
 - }
 - void
 - usage()
 - {
 - fprintf(stderr, "strings: [-a] [-t format] [-n number] [file...]\n");
 - }
 - int
 - main(int argc, char *argv[])
 - {
 - int c;
 - const char *errstr = NULL;
 - /* flawfinder: ignore. Old implementations of getopt should fix themselves */
 - while((c = getopt(argc, argv, ":an:t:")) != -1)
 - {
 - switch(c)
 - {
 - case 'a':
 - /* Structure is always ignored */
 - break;
 - case 'n':
 - errno = 0;
 - char *endptr = "";
 - opt_min_strlen = strtol(optarg, &endptr, 10);
 - if(*endptr != 0)
 - {
 - // extraneous characters is invalid
 - errno = EINVAL;
 - }
 - if(errno != 0)
 - {
 - fprintf(stderr, "strings: Option `-n %s`: %s\n", optarg, strerror(errno));
 - usage();
 - return 1;
 - }
 - if(opt_min_strlen == LLONG_MIN || opt_min_strlen < 1)
 - {
 - fprintf(stderr, "strings: Option `-n %s` is too small\n", optarg);
 - usage();
 - return 1;
 - }
 - if(opt_min_strlen == LLONG_MAX || opt_min_strlen > 4096)
 - {
 - fprintf(stderr, "strings: Option `-n %s` is too large\n", optarg);
 - usage();
 - return 1;
 - }
 - break;
 - case 't':
 - if(strnlen(optarg, 2) > 1)
 - {
 - usage();
 - return 1;
 - }
 - switch(optarg[0])
 - {
 - case 'o':
 - opt_offset_format = "%zo %s\n";
 - break;
 - case 'x':
 - opt_offset_format = "%zx %s\n";
 - break;
 - case 'd':
 - opt_offset_format = "%zd %s\n";
 - break;
 - default:
 - fprintf(stderr, "strings: Unknown format: %s\n", optarg);
 - usage();
 - return 1;
 - }
 - break;
 - }
 - }
 - argc -= optind;
 - argv += optind;
 - if(argc < 1)
 - {
 - return concat(0, "<stdin>");
 - }
 - for(int argi = 0; argi < argc; argi++)
 - {
 - if(strncmp(argv[argi], "-", 2) == 0)
 - {
 - if(concat(0, "<stdin>") != 0)
 - {
 - return 1;
 - }
 - }
 - else
 - {
 - int fd = open(argv[argi], O_RDONLY);
 - if(fd <= 0)
 - {
 - fprintf(stderr, "strings: Error opening ‘%s’: %s\n", argv[argi], strerror(errno));
 - return 1;
 - }
 - if(concat(fd, argv[argi]) != 0)
 - {
 - return 1;
 - }
 - if(close(fd) < 0)
 - {
 - fprintf(stderr, "strings: Error closing ‘%s’: %s\n", argv[argi], strerror(errno));
 - return 1;
 - }
 - }
 - }
 - return 0;
 - }