diff options
| author | Charles Cabergs <me@cacharle.xyz> | 2020-08-22 19:28:39 +0200 |
|---|---|---|
| committer | Charles Cabergs <me@cacharle.xyz> | 2020-08-22 19:28:39 +0200 |
| commit | f204fe59bd5fa537bf84cb522339c92b16f5a909 (patch) | |
| tree | 0fca812a8d40cbfb19c6c1a7db6d2e7f022a4f64 | |
| parent | 2f4cc13cdde6bd79356513085fc85d97ebd7c76a (diff) | |
| download | coreutils-f204fe59bd5fa537bf84cb522339c92b16f5a909.tar.gz coreutils-f204fe59bd5fa537bf84cb522339c92b16f5a909.tar.bz2 coreutils-f204fe59bd5fa537bf84cb522339c92b16f5a909.zip | |
Added shuf
| -rw-r--r-- | README.md | 18 | ||||
| -rw-r--r-- | shuf.c | 237 |
2 files changed, 246 insertions, 9 deletions
@@ -2,17 +2,18 @@ Rewrite of some core utilities for educational purposes. -| Name | Description | -|------------|-----------------------------------------------------------------| -| `mkdir` | make directories | -| `basename` | strip directory and suffix from filenames | -| `rm` | remove files or directories | -| `seq` | print a sequence of numbers | -| `tee` | read from standard input and write to standard output and files | +| Name | Description | +|------------|----------------------------------------------------------------- | +| `mkdir` | make directories | +| `basename` | strip directory and suffix from filenames | +| `rm` | remove files or directories | +| `seq` | print a sequence of numbers | +| `tee` | read from standard input and write to standard output and files | +| `shuf` | generate random permutations | # TODO -| Name | Description | +| Name | Description | |------------|-----------------------------------------------------------------| | `cut` | remove sections from each line of files | | `cp` | | @@ -25,7 +26,6 @@ Rewrite of some core utilities for educational purposes. | `tail` | | | `tr` | | | `test` | | -| `shuf` | | | `sort` | | | `uniq` | | | `dirname` | | @@ -0,0 +1,237 @@ +#define _POSIX_C_SOURCE 200809L +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <limits.h> + +#define NUMBER_LEN(x) strlen(#x); + +static char *g_name = "shuf"; + +void fatal(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s: ", g_name); + vfprintf(stderr, format, ap); + fputc('\n', stderr); + va_end(ap); + exit(EXIT_FAILURE); +} + +long parse_ulong(char *str, char *error_msg) +{ + char *end; + long ret; + + errno = 0; + ret = strtol(str, &end, 10); + if (errno != 0 || ret < 0 || *end != '\0') + fatal("%s '%s'", error_msg, optarg); + return ret; +} + +typedef struct +{ + char **data; + size_t len; + size_t cap; +} t_lines; + +#define INITIAL_CAPACITY 64 + +void lines_init(t_lines *lines) +{ + lines->len = 0; + lines->cap = INITIAL_CAPACITY; + lines->data = malloc(lines->cap * sizeof(char*)); + if (lines->data == NULL) + fatal("%s", strerror(errno)); +} + +void lines_destroy(t_lines *lines) +{ + for (size_t i = 0; i < lines->len; i++) + free(lines->data[i]); + free(lines->data); +} + +#define GROWTH_FACTOR 2 + +void lines_push(t_lines *lines, char *line) +{ + if (lines->len >= lines->cap) + { + lines->cap *= GROWTH_FACTOR; + lines->data = realloc(lines->data, lines->cap * sizeof(char*)); + if (lines->data == NULL) + fatal("%s", strerror(errno)); + } + lines->data[lines->len] = line; + lines->len++; +} + +#define RANDOM_FILEPATH "/dev/random" + +int main(int argc, char **argv) +{ + int option; + long max_output = -1; + char *output_filepath = NULL; + bool repeat = false; + bool echo = false; + char delimiter = '\n'; + long range_start = -1; + long range_stop = -1; + + while ((option = getopt(argc, argv, "ei:n:o:rz")) != -1) + { + + switch (option) + { + case 'e': echo = true; break; + case 'r': repeat = true; break; + case 'o': output_filepath = optarg; break; + case 'z': delimiter = '\0'; break; + + case 'i': + { + char *hyphen = strchr(optarg, '-'); + if (hyphen == NULL) + fatal("invalid input range '%s'", optarg); + *hyphen = '\0'; + range_start = parse_ulong(optarg, "invalid input range"); + *hyphen = '-'; + range_stop = parse_ulong(hyphen + 1, "invalid input range"); + if (range_start > range_stop) + fatal("invalid input range '%s'", optarg); + break; + } + + case 'n': + max_output = parse_ulong(optarg, "invalid line count"); + break; + } + + } + if (range_start != -1 && echo) + fatal("cannot combine -e and -i options"); + if (range_start != -1 && optind != argc) + fatal("extra operand '%s'", argv[optind]); + if (!echo && argc - optind > 1) + fatal("extra operand '%s'", argv[optind]); + + unsigned int seed; + FILE *seed_file = fopen(RANDOM_FILEPATH, "r"); + if (seed_file == NULL) + fatal("%s: %s", RANDOM_FILEPATH, strerror(errno)); + fread(&seed, sizeof(seed), 1, seed_file); + srand(seed); + + t_lines lines; + lines_init(&lines); + + if (range_start != -1) + { + size_t buf_len = NUMBER_LEN(LONG_MAX); + for (; range_start <= range_stop; range_start++) + { + char *line = malloc(buf_len * sizeof(char)); + if (line == NULL) + fatal("%s", strerror(errno)); + sprintf(line, "%ld", range_start); + lines_push(&lines, line); + } + } + else if (echo) + { + for (; optind < argc; optind++) + { + char *line = strdup(argv[optind]); + if (line == NULL) + fatal("%s", strerror(errno)); + lines_push(&lines, line); + } + } + else + { + FILE *input = stdin; + if (optind != argc) + { + input = fopen(argv[optind], "r"); + if (input == NULL) + fatal("%s: %s", argv[optind], strerror(errno)); + } + + char *line = NULL; + size_t line_size = 0; + errno = 0; + while (getdelim(&line, &line_size, delimiter, input) != -1) + { + lines_push(&lines, line); + line = NULL; + line_size = 0; + } + if (errno != 0) + { + fclose(input); + fatal("%s: %s", argv[optind], strerror(errno)); + } + fclose(input); + } + + if (lines.len == 0) + { + lines_destroy(&lines); + return EXIT_SUCCESS; + } + for (size_t i = lines.len - 1; i > 0; i--) + { + size_t j = rand() % i; + char *tmp = lines.data[i]; + lines.data[i] = lines.data[j]; + lines.data[j] = tmp; + } + + FILE *output = stdout; + if (output_filepath != NULL) + { + output = fopen(output_filepath, "w"); + if (output == NULL) + fatal("%s: %s", output_filepath, strerror(errno)); + } + + if (repeat) + { + for (size_t i = 0; true; i = (i + 1) % lines.len) + { + if (fputs(lines.data[i], output) == EOF) + break; + if (echo || range_start != -1) + if (fputc('\n', output) == EOF) + break; + } + } + else + { + for (size_t i = 0; + max_output == -1 ? (i < lines.len) : (i < (unsigned long)max_output); + i++) + { + if (fputs(lines.data[i], output) == EOF) + break; + if (echo || range_start != -1) + if (fputc('\n', output) == EOF) + break; + } + } + fclose(output); + + lines_destroy(&lines); + return EXIT_SUCCESS; +} |
