/* * */ #include "cumsum.h" /* * Global configuration variables. These are set based on command-line * arguments, and read-only beyond that point. */ static bool ARG_FP_INPUT = false; static bool ARG_UINT_INPUT = false; static bool ARG_HELP = false; static bool ARG_RUNNING_SUM = false; static int64_t ARG_WINDOW_SIZE = 0; /* * Circular buffer for windowed sums. I made this global for * convenience--it's a simple enough program that this'll work * fine. It could be refactored to have a more restricted scope. */ static Number *window_buffer = nullptr; static size_t window_idx = 0; static int parse_options(int argc, char *const *argv) { int arg_index = 0; int arg; bool error = false; while ((arg = getopt(argc, argv, "fhurw:")) != -1 && !error) { switch (arg) { case 'f': ARG_FP_INPUT = true; break; case 'u': ARG_UINT_INPUT = true; break; case 'h': ARG_HELP = true; break; case 'r': ARG_RUNNING_SUM = true; break; case 'w': ARG_WINDOW_SIZE = atol(optarg); if (ARG_WINDOW_SIZE <= 0) { fprintf(stderr, "Error: invalid window size: %lld. Must be > 0.\n", ARG_WINDOW_SIZE); error = true; break; } window_buffer = calloc(ARG_WINDOW_SIZE, sizeof(window_buffer[0])); if (!window_buffer) { fprintf(stderr, "Error: memory allocation failed\n"); error = true; } break; case '?': if (isprint(optopt)) { fprintf(stderr, "Error: unknown option `-%c`.\n", optopt); } else { fprintf(stderr, "Error: unknown option character `\\x%x`.\n", optopt); } error = true; break; default: error = true; break; } } if (ARG_UINT_INPUT && ARG_FP_INPUT) { fprintf(stderr, "Error: the -u and -f flags are mutually exclusive.\n"); error = true; } if (ARG_RUNNING_SUM && ARG_WINDOW_SIZE) { fprintf(stderr, "Error: the -w and -r flags are mutually exclusive.\n"); error = true; } if (!error) { arg_index = optind; } return arg_index; } static void help() { fprintf(stderr, "Usage:\ncumsum [-f|-u] [-r|-w window_size] [filename]\n"); } void print_sum(Number sum) { if (ARG_FP_INPUT) { fprintf(stdout, "%lf\n", sum.d); } else if (ARG_UINT_INPUT) { fprintf(stdout, "%lld\n", sum.u); } else { fprintf(stdout, "%lld\n", sum.i); } } static int get_next_number(FILE *file, Number *num) { /* read the next white-space seperated token */ char buffer[512]; int rc = fscanf(file, "%511s", buffer); if (rc == EOF) { return 0; } else if (rc != -1) { return -1; } errno = 0; char *endptr; if (ARG_FP_INPUT) { double val = strtod(buffer, &endptr); if (*endptr != 0 || endptr == buffer || errno == ERANGE) { fprintf(stderr, "Skipping invalid input token: %s\n", buffer); return -1; } num->d = val; } else if (ARG_UINT_INPUT) { uint64_t val = strtoull(buffer, &endptr, 10); if (*endptr != 0 || endptr == buffer || errno == ERANGE) { fprintf(stderr, "Skipping invalid input token: %s\n", buffer); return -1; } num->u = val; } else { int64_t val = strtoll(buffer, &endptr, 10); if (*endptr != 0 || endptr == buffer || errno == ERANGE) { fprintf(stderr, "Skipping invalid input token: %s\n", buffer); return -1; } num->i = val; } return 1; } void accumulate_number(Number *sum, Number num) { if (ARG_FP_INPUT) { sum->d += num.d; } else if (ARG_UINT_INPUT) { sum->u += num.u; } else { sum->i += num.i; } } void print_buffer_sum() { Number sum = {}; for (size_t i = 0; i < ARG_WINDOW_SIZE; i++) { accumulate_number(&sum, window_buffer[i]); } print_sum(sum); } static int read_data(FILE *file, Number *sum) { Number rec = {}; int rc; while ((rc = get_next_number(file, &rec))) { if (rc == -1) { continue; } if (ARG_WINDOW_SIZE > 1) { window_buffer[window_idx] = rec; window_idx = (window_idx + 1) % ARG_WINDOW_SIZE; print_buffer_sum(); } else { accumulate_number(sum, rec); if (ARG_RUNNING_SUM) { print_sum(*sum); } } } return 1; } static int process_data(FILE *file) { int rc = 1; Number sum = {}; rc = read_data(file, &sum); if (rc && !ARG_RUNNING_SUM) { print_sum(sum); } process_data_end: return rc; } int main(int argc, char **argv) { int rc = EXIT_SUCCESS; int file_index = 0; if (!(file_index = parse_options(argc, argv))) { help(); rc = EXIT_FAILURE; goto program_exit; } /* if the -h argument is supplied, print usage and exit successfully */ if (ARG_HELP) { help(); goto program_exit; } /* open the input file, if one is specified, otherwise default to stdin */ FILE *input_file; if (file_index < argc && strcmp(argv[file_index], "-") != 0) { if (!(input_file = fopen(argv[file_index], "r"))) { fprintf(stderr, "Error: Unable to open input file %s\n", argv[file_index]); rc = EXIT_FAILURE; goto program_exit; } } else { input_file = stdin; } if (!process_data(input_file)) { rc = EXIT_FAILURE; } close_file: if (input_file != stdin) { fclose(input_file); } free(window_buffer); program_exit: exit(rc); }