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;
- }