strings.c (4095B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 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_MAX */
- #include <stdio.h> /* fprintf(), BUFSIZ */
- #include <stdlib.h> /* strtol() */
- #include <string.h> /* strerror(), strncmp() */
- #include <unistd.h> /* read(), write(), close(), getopt(), optarg, optind */
- size_t opt_min_strlen = 4;
- const char *opt_offset_format = NULL;
- int delim = '\n';
- static int
- print_string(char *buffer, size_t offset)
- {
- int ret = 0;
- if(opt_offset_format == NULL)
- {
- ret = printf("%s%c", buffer, delim);
- }
- else
- {
- ret = printf(opt_offset_format, offset, buffer, delim);
- }
- if(ret < 0)
- {
- fprintf(stderr, "strings: error: Failed writing: %s\n", strerror(errno));
- errno = 0;
- return 1;
- }
- else
- {
- return 0;
- }
- }
- static int
- concat(int fd, const char *fdname)
- {
- ssize_t c;
- static char read_buf[4096];
- static char write_buf[4096];
- size_t write_pos = 0;
- size_t offset = 0;
- 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;
- }
- }
- }
- if(c < 0)
- {
- fprintf(stderr, "strings: error: Failed reading ‘%s’: %s\n", fdname, strerror(errno));
- errno = 0;
- return 1;
- }
- return 0;
- }
- static void
- usage(void)
- {
- fprintf(stderr, "strings: [-az] [-t format] [-n number] [file...]\n");
- }
- int
- main(int argc, char *argv[])
- {
- int c;
- while((c = getopt(argc, argv, ":an:t:z")) != -1)
- {
- char *endptr = NULL;
- switch(c)
- {
- case 'a':
- /* Structure is always ignored */
- break;
- case 'n':
- opt_min_strlen = strtol(optarg, &endptr, 10);
- if(endptr && *endptr != 0)
- {
- // extraneous characters is invalid
- errno = EINVAL;
- }
- if(errno != 0)
- {
- fprintf(stderr, "strings: error: Option `-n %s`: %s\n", optarg, strerror(errno));
- usage();
- return 1;
- }
- if(opt_min_strlen < 1)
- {
- fprintf(stderr, "strings: error: Option `-n %s` is too small\n", optarg);
- usage();
- return 1;
- }
- if(opt_min_strlen == LONG_MAX || opt_min_strlen > 4096)
- {
- fprintf(stderr, "strings: error: 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%c";
- break;
- case 'x':
- opt_offset_format = "%zx %s%c";
- break;
- case 'd':
- opt_offset_format = "%zd %s%c";
- break;
- default:
- fprintf(stderr, "strings: error: Unknown format: %s\n", optarg);
- usage();
- return 1;
- }
- break;
- case 'z':
- delim = '\0';
- break;
- case ':':
- fprintf(stderr, "strings: error: Missing operand for option: '-%c'\n", optopt);
- usage();
- return 1;
- case '?':
- fprintf(stderr, "strings: error: Unrecognised option: '-%c'\n", optopt);
- usage();
- return 1;
- default:
- abort();
- }
- }
- 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: Failed 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: Failed closing ‘%s’: %s\n", argv[argi], strerror(errno));
- return 1;
- }
- }
- }
- return 0;
- }