min_list (C)

min_list.c

#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_INT 100

typedef enum _result { ok, err_invalid_size, err_is_null } result;

const char *usage_msg =
    "Error wrong number of arguments.\n"
    "Usage: ./min_list <num1> <num2> ...\n"
    "       where <num1>, <num2>, ... must be valid integers.\n";

// Copies the value of the smallest value in the tab array in the min variable.
// Returns the pointer to a newly allocated value of the minimum value of the
// array if everything went fine. Returns NULL otherwise
int *list_find_min(int32_t *tab, int size);

// Prints all the element in the tab array.
// Returns err_invalid_size if size <= 0
// Returns err_is_null if tab is NULL
// Returns ok if everything went fine
result list_print(int32_t *tab, int size);

// Exits if the error code shows invalidity
void error_handling(result error_code, int32_t **tab);

// Parses a string to an integer.
// Returns a pointer to newly allocated data.
// Returns NULL if conversion failed.
int *parse_int32(char *arg_to_transform);

// Reads the command line inputs and stores them
// in a newly allocated array.
// Returns the array with the parsed numbers or NULL if allocation failed or an
// invalid number was parsed.
int *read_input(int size, char *char_num[]) {
    int32_t *tab = malloc(size * sizeof(*tab));
    if (NULL == tab) {
        fprintf(stderr, "Memory allocation failed\n");
        return NULL;
    }
    for (int i = 0; i < size; ++i) {
        int *num = parse_int32(char_num[i]);
        if (NULL == num) {
            free(tab);
            fprintf(stderr, "Tried to parse %s which is not a valid integer.\n",
                    char_num[i]);
            return NULL;
        }
        tab[i] = *num;
        free(num);
    }
    return tab;
}

int main(int argc, char *argv[]) {
    if (argc == 1) {
        fprintf(stderr, "%s", usage_msg);
        return EXIT_FAILURE;
    }

    int size = argc - 1;
    int32_t *tab = read_input(size, &argv[1]);
    if (NULL == tab) {
        fprintf(stderr, "Failure during argument parsing.\n");
        return EXIT_FAILURE;
    }

    printf("Among the numbers in the list:\n");
    error_handling(list_print(tab, size), &tab);
    int *min = list_find_min(tab, size);
    if (NULL == min) {
        fprintf(stderr, "Could not find the minimum of the array.\n");
        free(tab);
        tab = NULL;
        return EXIT_FAILURE;
    }
    printf("The value of the minimum of the numbers is: %d\n", *min);

    free(min);
    free(tab);
    tab = NULL;

    return EXIT_SUCCESS;
}

int min_i32(int32_t lhs, int32_t rhs) {
    if (lhs < rhs) {
        return lhs;
    } else {
        return rhs;
    }
}

// Checks if size is valid and tab is not NULL
result list_is_valid(int32_t *tab, int size) {
    if (size <= 0) {
        return err_invalid_size;
    }
    if (NULL == tab) {
        return err_is_null;
    }
    return ok;
}

result list_print(int32_t *tab, int size) {
    result code = list_is_valid(tab, size);
    if (code != ok) {
        return code;
    }

    for (int i = 0; i < size; ++i) {
        printf("%d ", tab[i]);
    }
    printf("\n");
    return code;
}

int32_t *list_find_min(int32_t *tab, int size) {
    result code = list_is_valid(tab, size);
    if (code != ok) {
        return NULL;
    }

    int32_t *min = malloc(sizeof(*min));
    *min = tab[0];
    for (int i = 1; i < size; ++i) {
        *min = min_i32(*min, tab[i]);
    }
    return min;
}

void error_handling(result error_code, int32_t **tab) {
    switch (error_code) {
        case err_invalid_size:
            fprintf(stderr, "Return value, %d. Size is <= 0.\n", error_code);
            free(*tab);
            *tab = NULL;
            exit(EXIT_FAILURE);
            break;
        case err_is_null:
            fprintf(stderr, "Tab is NULL.\n");
            free(*tab);
            *tab = NULL;
            exit(EXIT_FAILURE);
            break;
        case ok:
            break;
    }
}

int32_t *parse_int32(char *arg_to_transform) {
    if (strlen(arg_to_transform) == 0) {
        return NULL;  // empty string to parse
    }
    char *remaining;
    errno = 0;  // errno == 0 (defined in errno.h) means everything went fine
    long arg = strtol(arg_to_transform, &remaining,
                      10);  // number is parsed in base 10
    if (*remaining != '\0' || errno != 0) {
        return NULL;  // Empty string parsed or an error occurred
    }

    if (arg < INT_MIN || arg > INT_MAX) {
        return NULL;  // Not within the limits of an int
    }
    int32_t *num = malloc(sizeof(*num));
    *num = (int32_t)arg;
    return num;
}