split.c (6869B)
- // 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 "../lib/fs.h" // auto_file_copy
- #include "../lib/truncation.h" // apply_size_suffix
- #include <errno.h>
- #include <fcntl.h> // open
- #include <limits.h> // NAME_MAX
- #include <stdio.h> // fprintf
- #include <stdlib.h> // strtoul
- #include <string.h> // strerror
- #include <sys/stat.h> // fstat
- #include <unistd.h> // getopt
- const char *argv0 = "split";
- const char *name = "x";
- size_t name_len = 1;
- size_t suffix_len = 2, bytes = 0, lines = 0;
- char *name_in = NULL;
- static int
- base26(int id, char *str)
- {
- memcpy(str, name, name_len);
- memset(str + name_len, 'a', suffix_len);
- size_t id_p = name_len + suffix_len;
- do
- {
- str[id_p--] = 'a' + (id % 26);
- id /= 26;
- } while(id > 0 && id_p > name_len);
- if(id_p <= name_len)
- {
- fprintf(stderr,
- "%s: error: Failed representing %d into suffix of length %zu\n",
- argv0,
- id,
- suffix_len);
- return -1;
- }
- return 0;
- }
- static int
- split_bytes(void)
- {
- int fd_in = STDIN_FILENO;
- if(name_in != NULL)
- {
- fd_in = open(name_in, O_RDONLY | O_NOCTTY);
- if(fd_in < 0)
- {
- fprintf(stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_in, strerror(errno));
- return 1;
- }
- }
- struct stat fd_in_stat;
- if(fstat(fd_in, &fd_in_stat) != 0)
- {
- fprintf(stderr,
- "%s: error: Failed getting status from file '%s': %s",
- argv0,
- name_in,
- strerror(errno));
- close(fd_in);
- return 1;
- }
- int err = 0;
- off_t wrote = 0;
- int split_id = 0;
- while(wrote < fd_in_stat.st_size)
- {
- char name_out[NAME_MAX] = "";
- if(base26(split_id++, name_out) < 0) return 1;
- int fd_out = open(name_out, O_WRONLY | O_NOCTTY | O_CREAT, 0644);
- if(fd_out < 0)
- {
- fprintf(
- stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_out, strerror(errno));
- err = 1;
- break;
- }
- int ret = auto_file_copy(fd_in, fd_out, bytes, 0);
- if(ret < 0)
- {
- fprintf(stderr,
- "%s: error: Failed copying from file '%s' to file '%s': %s\n",
- argv0,
- name_in ? name_in : "<stdin>",
- name_out,
- strerror(errno));
- close(fd_out);
- err = 1;
- break;
- }
- wrote += ret;
- if(close(fd_out) < 0)
- {
- fprintf(
- stderr, "%s: error: Failed closing file '%s': %s\n", argv0, name_out, strerror(errno));
- err = 1;
- break;
- }
- }
- if(name_in != NULL) close(fd_in);
- return err;
- }
- static int
- split_lines(void)
- {
- FILE *in = stdin;
- if(name_in != NULL)
- {
- in = fopen(name_in, "r");
- if(in == NULL)
- {
- fprintf(stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_in, strerror(errno));
- return 1;
- }
- }
- int err = 0;
- char *line = NULL;
- size_t line_len = 0;
- int split_id = 0;
- while(true)
- {
- if(feof(in)) break;
- if(ferror(in))
- {
- fprintf(stderr,
- "%s: error: Failed reading line from file '%s': %s\n",
- argv0,
- name_in,
- strerror(errno));
- err = 1;
- break;
- }
- char name_out[NAME_MAX] = "";
- if(base26(split_id++, name_out) < 0)
- {
- err = 1;
- break;
- }
- FILE *out = NULL;
- for(size_t i = 0; i < lines; i++)
- {
- ssize_t nread = getline(&line, &line_len, in);
- if(nread < 0)
- {
- if(errno != 0)
- {
- fprintf(stderr,
- "%s: error: Failed reading line from file '%s': %s\n",
- argv0,
- name_in,
- strerror(errno));
- err = 1;
- }
- break;
- }
- if(out == NULL)
- {
- out = fopen(name_out, "w");
- if(out == NULL)
- {
- fprintf(stderr,
- "%s: error: Failed opening '%s' file: %s\n",
- argv0,
- name_out,
- strerror(errno));
- err = 1;
- break;
- }
- }
- if(fwrite(line, nread, 1, out) < 0)
- {
- fprintf(stderr,
- "%s: error: Failed writing line to file '%s': %s\n",
- argv0,
- name_out,
- strerror(errno));
- err = 1;
- break;
- }
- }
- if(out != NULL)
- {
- if(fclose(out) < 0)
- {
- fprintf(
- stderr, "%s: error: Failed closing file '%s': %s\n", argv0, name_out, strerror(errno));
- err = 1;
- break;
- }
- }
- if(err != 0) break;
- }
- if(line_len > 0) free(line);
- if(name_in != NULL) fclose(in);
- return err;
- }
- static const char *error_opt_b_l = "%s: error: Options -b and -l are mutually exclusive\n";
- int
- main(int argc, char *argv[])
- {
- int c = -1;
- while((c = getopt(argc, argv, ":a:b:l:")) != -1)
- {
- char *endptr = NULL;
- switch(c)
- {
- case 'a':
- suffix_len = strtoul(optarg, &endptr, 0);
- if(suffix_len == 0)
- {
- fprintf(stderr, "%s: error: Failed parsing '-a %s': %s\n", argv0, optarg, strerror(errno));
- return 1;
- }
- if(endptr != NULL && *endptr != '\0')
- {
- fprintf(stderr,
- "%s: error: Invalid trailing characters in '-a %s': %s\n",
- argv0,
- optarg,
- endptr);
- return 1;
- }
- break;
- case 'b':
- {
- if(lines != 0)
- {
- fprintf(stderr, error_opt_b_l, argv0);
- return 1;
- }
- unsigned long opt_b = strtoul(optarg, &endptr, 0);
- if(opt_b == 0)
- {
- fprintf(stderr, "%s: error: Failed parsing '-b %s': %s\n", argv0, optarg, strerror(errno));
- return 1;
- }
- if(endptr != NULL && *endptr != 0)
- if(apply_size_suffix(&opt_b, endptr) != 0) return 1;
- bytes = opt_b;
- lines = 0;
- break;
- }
- case 'l':
- if(bytes != 0)
- {
- fprintf(stderr, error_opt_b_l, argv0);
- return 1;
- }
- lines = strtoul(optarg, &endptr, 0);
- if(lines == 0)
- {
- fprintf(stderr, "%s: error: Failed parsing '-l %s': %s\n", argv0, optarg, strerror(errno));
- return 1;
- }
- if(endptr != NULL && *endptr != '\0')
- {
- fprintf(stderr,
- "%s: error: Invalid trailing characters in '-l %s': %s\n",
- argv0,
- optarg,
- endptr);
- return 1;
- }
- break;
- case ':':
- fprintf(stderr, "%s: error: Option '-%c' requires an operand\n", argv0, optopt);
- return 1;
- default:
- fprintf(stderr, "%s: error: Unhandled option '-%c'\n", argv0, optopt);
- return 1;
- }
- }
- argc -= optind;
- argv += optind;
- if(lines == 0 && bytes == 0) lines = 1000;
- if(argc > 2 || argc < 0)
- {
- fprintf(stderr, "%s: error: Expected 0, 1, or 2 arguments, got %d\n", argv0, argc);
- return 1;
- }
- else if(argc >= 1)
- {
- name_in = argv[0];
- if(argc == 2) name = argv[1];
- }
- name_len = strlen(name);
- if(name_len + suffix_len > NAME_MAX)
- {
- fprintf(stderr,
- "%s: error: name(%zd bytes) + suffix_length(%zd bytes) > NAME_MAX(%d bytes)\n",
- argv0,
- name_len,
- suffix_len,
- NAME_MAX);
- return 1;
- }
- if(bytes != 0) return split_bytes();
- return split_lines();
- }