diff options
author | Joel Kronqvist <joelkronqvist@proton.me> | 2024-03-23 17:56:49 +0200 |
---|---|---|
committer | Joel Kronqvist <joelkronqvist@proton.me> | 2024-03-23 18:00:51 +0200 |
commit | e26279107edf7012e6b9cd558aaa09a8f0ca4764 (patch) | |
tree | 2bcd2eb3c6c2ade2e85c9f52ca1fc41d3c720811 /stdu.c | |
download | stdu-e26279107edf7012e6b9cd558aaa09a8f0ca4764.tar.gz stdu-e26279107edf7012e6b9cd558aaa09a8f0ca4764.zip |
Initial commit
Diffstat (limited to 'stdu.c')
-rw-r--r-- | stdu.c | 237 |
1 files changed, 237 insertions, 0 deletions
@@ -0,0 +1,237 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdbool.h> + +#include "intmath.h" + +int tests_run = 0; +int tests_failed = 0; + +struct Config { + int precision; + bool human_readable; +}; +typedef struct Config Config; +struct Result { + bool success; + void* result; +}; +typedef struct Result Result; + +Result parse_config(int argc, char* argv[]) { + Result res; + res.success = false; + Config tmp; + tmp.precision = 0; + tmp.human_readable = false; + + size_t i = 1; + char* argument; + char* precision = NULL; + while (i < argc) { + argument = argv[i]; + int comp1 = strcmp(argument, "--precision") == 0; + if ( + comp1 || strncmp(argument, "-p", 2) == 0 + ) { + if (precision != NULL) { + char* error_msg = malloc(44); + snprintf( + error_msg, + 44, + "precision can't be specified multiple times" + ); + res.result = error_msg; + return res; + } + i++; + if (strlen(argument) != 2 && !comp1) { + precision = argument + 2; + continue; + } + if (i == argc) { + char* error_msg = malloc(57); + snprintf( + error_msg, + 57, + "`%s` should be followed by an integer argument", + argument + ); + res.result = error_msg; + return res; + } + precision = argv[i]; + } else if ( + strcmp(argument, "--human-readable") == 0 + || strcmp(argument, "-h") == 0 + ) { + if (tmp.human_readable != false) { + char* error_msg = malloc(52); + snprintf( + error_msg, + 52, + "human readability can't be specified multiple times" + ); + res.result = error_msg; + return res; + } + tmp.human_readable = true; + } else { + size_t msg_size = 21 + strlen(argument); + char* error_msg = malloc(msg_size); + snprintf( + error_msg, + msg_size, + "unknown argument: `%s`", + argument + ); + res.result = error_msg; + return res; + } + + i++; + } + + if (precision != NULL) { + char* endptr; + errno = 0; + tmp.precision = (int) strtol(precision, &endptr, 10); + if (errno != 0) { + char* error_msg = malloc(64); + char* errno_str = strerror(errno); + snprintf( + error_msg, + 64, + "invalid precision: %s", + errno_str + ); + free(errno_str); + res.result = error_msg; + return res; + } + if (*endptr != '\0') { + char* error_msg = malloc(53); + snprintf( + error_msg, + 53, + "the precision may not contain non-numeric characters", + precision + ); + res.result = error_msg; + return res; + } + if (tmp.precision <= 0) { + char* error_msg = malloc(50); + snprintf( + error_msg, + 50, + "the precision can't be less than or equal to zero" + ); + res.result = error_msg; + return res; + } + } + + Config* conf = malloc(sizeof(int) + sizeof(bool)); + conf->precision = tmp.precision; + conf->human_readable = tmp.human_readable; + + res.success = true; + res.result = conf; + return res; +} + +int main (int argc, char* argv[]) { + Result res = parse_config(argc, argv); + if (res.success == false) { + char* err_msg = (char*) res.result; + fprintf( + stderr, + "Error parsing command line: %s\n", + err_msg + ); + return 1; + } + + Config* conf = (Config*) res.result; + int precision = conf->precision; + bool human_readable = conf->human_readable; + printf("precision: %d\n", precision); + if (human_readable) printf("human readable\n"); + + unsigned int base = 10; + if (human_readable && precision == 0) { + precision = 1; + base = 1024; + } + + int blocksize = 1; + size_t bufsize = 1; + unsigned int bytes_read = 0; + size_t new_read_bytes = 0; + if (precision != 0) { + blocksize = exp_notated_to_int(int_ceiled_exponent_notation_base( + bytes_read + 1, + precision, + base)) + - bytes_read; + bufsize = 1024; + } + size_t max_bufsize = 1048576; + char* buf = malloc(bufsize); + char* tmpbuf; + + while (1) { + /* output */ + printf("\r%u", bytes_read); + if (fflush(stdin) == EOF) { + printf("\n"); + perror("error during fflush"); + return 1; + } + + /* reading */ + new_read_bytes = fread( + buf, + 1, + int_min(blocksize, bufsize)/* + (blocksize == 0)*/, + stdin + ); + if (new_read_bytes == 0) { + int err = ferror(stdin); + if (err != 0) { + printf("\n"); + fprintf(stderr, "error reading stdin"); + return err; + } + break; + } + bytes_read += new_read_bytes; + + /* resizing buffer and blocksize to read as much as possible + * at once + */ + if (precision == 0) continue; + blocksize = exp_notated_to_int(int_ceiled_exponent_notation_base( + bytes_read + 1, + precision, + base)) + - bytes_read; + if (blocksize > bufsize) { + tmpbuf = malloc(bufsize * 2); + if (tmpbuf == NULL) { + free(tmpbuf); + } else { + free(buf); + buf = tmpbuf; + bufsize *= 2; + } + } + } + printf("\n"); + + return 0; +} |