aboutsummaryrefslogtreecommitdiff
path: root/stdu.c
diff options
context:
space:
mode:
Diffstat (limited to 'stdu.c')
-rw-r--r--stdu.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/stdu.c b/stdu.c
new file mode 100644
index 0000000..43038a8
--- /dev/null
+++ b/stdu.c
@@ -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;
+}