Skip to content

Documentation

Maciej Domnik edited this page Aug 27, 2024 · 12 revisions

COMMAND-LINE ARGUMENT PARSING

BRIEF DESCRIPTION

This section of this project is dedicated to handling arguments given by the user from the command line. The users calls the program using "./minirt" followed by an arbitrary number of non repeating flags with possible arguments as well as one scene file in the ".rt" format, which describes the contents of the scene. All arguments can be called in any given order.

FLAG PARSING

Flag parsing is done in a highly modular way. A Flag is created in the header by defining it's FULL and SHORT string as well as their data type (e.g. --save, -s, "STRING"). A data type can also have an '*', which signifies an optional argument. A data type can be either "NULL"(takes no arguments), "STRING", "FLOAT", "VECTOR" (all with optional argument setting).


FLAG_FORMATTING.C

Option Preferences Function

This function begins the process of checking the flag format as well as their arguments. The function checks the full and short options flags and updates the options structure accordingly.

Parameters

  • argv: The command line arguments.
  • options: The options structure to be updated.

Return Value

  • Returns 0 if the file format is successfully checked and updated, -1 otherwise.
int	option_preferences(char **argv, t_options *options)
{
	if (check_file_format(argv, options, ft_strdup(OPTS_FULL), 0) == -1)
		return (-1);
	if (check_file_format(argv, options, ft_strdup(OPTS_SHORT), 1) == -1)
		return (-1);
	return (0);
}

Check File Format Function

This function checks each command line argument for a match with a list of flags. It uses the args_to_opts function to compare each argument against a list of flags. If an argument does not match any flags, the function returns -1. Otherwise, it updates the options structure using binary assignment with opt_binary_assignment and processes the argument values with value_containers. If any step fails, the function returns -1.

Parameters

  • argv: The command line arguments.
  • options: The options structure to store the converted options.
  • flag: The flag to be used for conversion.
  • type: The type of conversion to be performed.

Return Value

  • Returns 0 if the file format is valid, -1 otherwise.
int check_file_format(char **argv, t_options *options, char *flag, int type)
{
    int i;
    int ato_ret;

    i = 1;
    while (argv[i])
    {
        ato_ret = args_to_opts(argv[i], ft_strdup(flag), type);
        if (ato_ret == -2)
            return (-1);
        else
        {
            if (opt_binary_assignment(ato_ret, options) == -1)
                return (-1);
            if (value_containers(ato_ret, options, argv[i + 1], argv[i]) == -1)
                return (-1);
        }
        i++;
    }
    return (0);
}

Args to Opts Function

This function converts a string to an option index based on a given flag and type. It splits the flag into options using a comma as a delimiter and compares each option with the given string. If a match is found, the function returns the index of the option. If the string is not a number and starts with a (-), an error message is printed and -2 is returned. If no match is found, -1 is returned.

Parameters

  • str: The string to be converted to an option index.
  • flag: The flag used to split into options.
  • type: The type of the flag.

Return Value

  • Returns the index of the option if a match is found.
  • Returns -2 if the string is a non-valid flag.
  • Returns -1 if no match is found.
int args_to_opts(char *str, char *flag, int type)
{
    char    **opts;
    int     i;

    opts = ft_split(flag, ',');
    i = 0;
    while (opts[i])
    {
        if (ft_strcmp(opts[i], str) == 0)
            return (i);
        i++;
    }
    if (is_number(str) == 1)
    {
        if ((type == 0 && str[0] == '-' && str[1] == '-' )
            || (type == 1 && str[0] == '-' && str[1] != '-'))
        {
            ft_dprintf(2, "%s '%s'\n", ERR_INVALID_OPT, str);
            return (-2);
        }
    }
    return (-1);
}

Opt Binary Assignment Function

This function assigns a binary value to an option flag in the provided t_options structure. It checks if the value is less than 0 and returns 0 if true. It also checks if the flag is already set and returns -1 if true, printing an error message. If the flag is not set, it sets the flag and returns 0.

Parameters

  • value: The binary position index to assign (1) to the flag.
  • options: Pointer to the t_options structure.

Return Value

  • Returns 0 if the flag was successfully set.
  • Returns -1 if the flag is already set.
int opt_binary_assignment(int value, t_options *options)
{
    if (value < 0)
        return (0);
    if (options->opts_flags & (1 << value))
    {
        ft_dprintf(2, "%s\n", ERR_DOUBLE_OPT);
        return (-1);
    }
    options->opts_flags |= (1 << value);
    return (0);
}

CREATE_FLAG_NODES.C

Value Containers Function

This function is a starting point for populating the options->values linked list with flag arguments. It takes the value of the flag, a pointer to a t_options struct, an argument string, and a flag. It groups the flag, flag type, and argument string into a single array of strings, flag_join. Then, it calls the process_flag function with the appropriate arguments to create a new value node and append it to the options->values linked list. It returns 0 if successful and -1 if an error occurs.

Parameters

  • value: The value for the flag.
  • options: A pointer to a t_options struct.
  • str: A string of the flag's argument.
  • flag: The flag string.

Return Value

  • Returns 0 if successful.
  • Returns -1 if an error occurs.
int value_containers(int value, t_options *options, char *str, char *flag)
{
    char    **f_types;
    char    **flag_join;
    char    *opts_value;

    opts_value = ft_strdup(OPTS_VALUE);
    if (value < 0)
        return (0);
    f_types = ft_split(opts_value, ',');
    flag_join = gc_malloc(sizeof(char *) * 4);
    flag_join[0] = ft_strdup(flag);
    flag_join[1] = ft_strdup(f_types[value]);
    if (str)
        flag_join[2] = ft_strdup(str);
    else
        flag_join[2] = NULL;
    flag_join[3] = NULL;
    if (process_flag(flag_join[1], options, value, flag_join) == -1)
        return (-1);
    return (0);
}

Process Flag Function

This function compares the flag's corresponding input type against a preset type list and creates a new option node. It processes the flag's arguments based on the flag's type. If the flag type matches a given type, it calls the create_option function. If the flag type is "NULL", it calls the handle_null_value function. If the argument for a flag is invalid, it prints an error message to stderr and returns -1.

Parameters

  • f_type: The type of the flag.
  • options: The options structure to update.
  • value: The value of the flag.
  • flag_join: An array of strings that holds relevant flag information.

Return Value

  • Returns 0 if the flag was processed successfully.
  • Returns -1 otherwise.
int process_flag(char *f_type, t_options *options, int value, char **flag_join)
{
    if (ft_strnstr(f_type, "STRING", ft_strlen(f_type))
        || ft_strnstr(f_type, "FLOAT", ft_strlen(f_type))
        || ft_strnstr(f_type, "VECTOR", ft_strlen(f_type)))
    {
        if (create_option(options, value, flag_join) == -1)
            return (-1);
    }
    if (ft_strnstr(f_type, "NULL", ft_strlen(f_type)))
    {
        if (handle_null_value(flag_join[2]) == 1)
        {
            ft_dprintf(2, "Error: Option '%s' %s\n", flag_join[0], ERR_NO_ARG);
            return (-1);
        }
    }
    return (0);
}

Create Option Function

This function creates a t_value node and appends it to the options->values linked list if the flag's argument is valid. It checks if the flag's argument is valid against its relevant flag type. If the flag's argument is valid, it creates a new t_value node and appends it to the back of the options->values linked list. If the flag's argument is invalid, it prints an error message to stderr and returns -1.

Parameters

  • options: The options list to add the new node to.
  • value: The value of the option.
  • flag_join: The array of relevant flag information.

Return Value

  • Returns 0 if the option node was successfully created and added.
  • Returns -1 otherwise.
int create_option(t_options *options, int value, char **flag_join)
{
    t_value *new_value_node;
    char    *arg;

    arg = check_if_option(flag_join[2]);
    if (flag_join[1][0] == '*' && arg == NULL)
        new_value_node = create_value_node(value, flag_join[1], NULL);
    else if (arg == NULL)
    {
        ft_dprintf(2, "Error: Option '%s' %s\n", flag_join[0], ERR_REQ_ARG);
        return (-1);
    }
    else
        new_value_node = create_value_node(value, flag_join[1], arg);
    if (new_value_node == NULL)
        return (-1);
    append_value_node(options, new_value_node);
    return (0);
}

Create Value Node Function

This function creates a new t_value node with the specified type, value type, and data. It compares the value type against a list of known types and runs the appropriate function to check and handle the flag's argument. If the flag's argument is valid, it creates a new value node and returns a pointer to it. If the flag's argument is invalid, it prints an error message to stderr and returns NULL.

Parameters

  • type: Enum of the flag's type.
  • value_type: String holding the value type.
  • data: Argument string.

Return Value

  • Returns a pointer to the newly created value node if successful.
  • Returns NULL if an error occurred.
t_value *create_value_node(t_opts_type type, char *value_type, char *data)
{
    t_value *new_value_node;

    new_value_node = (t_value *)gc_malloc(sizeof(t_value));
    new_value_node->type = type;
    if (ft_strnstr(value_type, "STRING", ft_strlen(value_type)))
    {
        if (handle_string_value(new_value_node, data) == -1)
            return (NULL);
    }
    else if (ft_strnstr(value_type, "FLOAT", ft_strlen(value_type)))
    {
        if (handle_float_value(new_value_node, data) == -1)
            return (NULL);
    }
    else if (ft_strnstr(value_type, "VECTOR", ft_strlen(value_type)))
    {
        if (handle_vector_value(new_value_node, data) == -1)
            return (NULL);
    }
    new_value_node->next = (NULL);
    return (new_value_node);
}

Append Value Node Function

This function appends a new value node to the options->values linked list. If the options->values list is empty, it initializes it with the new value node. Otherwise, it traverses the list to find the last node and appends the new value node to the end.

Parameters

  • options: The options structure to append the value node to.
  • new_value_node: The new value node to append.
void append_value_node(t_options *options, t_value *new_value_node)
{
    t_value *last;

    if (options->values == NULL)
    {
        options->values = new_value_node;
        return ;
    }
    last = options->values;
    while (last->next != NULL)
        last = last->next;
    last->next = new_value_node;
}

HANDLE_VALUES.C

Handle Null Value Function

This function checks if a flag of type NULL holds no arguments. It evaluates whether the flag's argument is null or invalid (e.g., another flag or a number with a negative sign). It returns 0 if the argument is considered invalid; otherwise, it returns 1. The outer function will handle the error message.

Parameters

  • str: The input string to be checked.

Return Value

  • Returns 0 if the string is null or is not a number and has a negative sign.
  • Returns 1 otherwise.
int handle_null_value(char *str)
{
    if (!str)
        return (0);
    if ((is_number(str) == 1) && str[0] == '-')
        return (0);
    return (1);
}

Handle String Value Function

This function is responsible for assigning a string value to the specified t_value node of type string. If the data is NULL, the string value of the t_value node will be set to NULL. Otherwise, it will first check the format of the string using the check_string_format function. If the format check fails, the function returns -1. Otherwise, it allocates memory for the string value using ft_strdup and returns 0.

Parameters

  • new_value_node: Pointer to the t_value node to assign the string value to.
  • data: The string value to assign.

Return Value

  • Returns 0 on success.
  • Returns -1 if the format check fails.
int handle_string_value(t_value *new_value_node, char *data)
{
    if (data == NULL)
    {
        new_value_node->value.string.string_value = NULL;
    }
    else
    {
        if (check_string_format(data) == -1)
            return (-1);
        else
            new_value_node->value.string.string_value = ft_strdup(data);
    }
    return (0);
}

Handle Float Value Function

This function is responsible for assigning a float value to the specified t_value node of type float. If the data is NULL, the float value of the t_value node will be set to 0. Otherwise, it will first check the format of the float using the check_float_format function. If the format check fails, the function returns -1. Otherwise, it converts the string into a double and returns 0.

Parameters

  • new_value_node: Pointer to the t_value node to assign the float value to.
  • data: The float value to assign.

Return Value

  • Returns 0 on success.
  • Returns -1 if the format check fails.
int handle_float_value(t_value *new_value_node, char *data)
{
    if (data == NULL)
    {
        new_value_node->value.float_value.float_value = 0;
    }
    else
    {
        if (check_float_format(data) == -1)
            return (-1);
        else
            new_value_node->value.float_value.float_value = ft_atob(data);
    }
    return (0);
}

Handle Vector Value Function

This function is responsible for assigning values to vector components (x, y, z) in a t_value node. If the data is NULL, the vector values of the t_value node will be set to 0 by default. Otherwise, it first checks the format of the data value using the check_vector_format function. If the format check fails, the function returns -1. If the format check is successful, the function converts the string into a set of three doubles and returns 0.

Parameters

  • new_value_node: Pointer to the t_value node to assign the vector values to.
  • data: The string containing vector values to assign.

Return Value

  • Returns 0 on success.
  • Returns -1 if the format check fails or if there is an error with the vector argument.
int handle_vector_value(t_value *new_value_node, char *data)
{
    char    **vector;
    int     i;

    i = 0;
    if (data == NULL)
        set_vector_values(new_value_node, NULL);
    else
    {
        if (check_vector_format(data) == -1)
            return (-1);
        else
        {
            vector = ft_split(data, ',');
            while (vector[i] != NULL)
                i++;
            if (i == 3)
                set_vector_values(new_value_node, vector);
            else
            {
                ft_dprintf(1, "%s\n", ERR_VEC_ARG);
                return (-1);
            }
        }
    }
    return (0);
}

Set Vector Values Function

This function sets the values of a vector (x, y, z) in a given value node. It is used as a helper function for handle_vector_value to assign vector components to the appropriate fields in a t_value node.

Parameters

  • new_value_node: The value node to set the vector values in.
  • vector: The vector values to set. If NULL, the vector components are set to 0.

Return Value

  • This function does not return a value.
void set_vector_values(t_value *new_value_node, char **vector)
{
    if (vector == NULL)
    {
        new_value_node->value.vector.x = 0;
        new_value_node->value.vector.y = 0;
        new_value_node->value.vector.z = 0;
    }
    else
    {
        new_value_node->value.vector.x = ft_atob(vector[0]);
        new_value_node->value.vector.y = ft_atob(vector[1]);
        new_value_node->value.vector.z = ft_atob(vector[2]);
    }
}

CHECK_VALUE_FORMAT.C

Check String Format Function

This function checks if a given flag's string has a valid format. The valid format consists of uppercase letters (A-Z), lowercase letters (a-z), and underscores (_). If the string contains any other characters, an error message is printed to the standard error stream, and the function returns -1. Otherwise, the function returns 0.

Parameters

  • str: The flag's string argument to be checked.

Return Value

  • Returns 0 if the string has a valid format.
  • Returns -1 if the string contains invalid characters.
int check_string_format(char *str)
{
    int i;

    i = 0;
    while (str[i])
    {
        if ((str[i] < 65 || str[i] > 90)
            && (str[i] < 97 || str[i] > 122) && str[i] != '_')
        {
            if (str[i] != '_')
                ft_dprintf(2, "%s '%c'\n", ERR_INVALID_CHAR, str[i]);
            return (-1);
        }
        i++;
    }
    return (0);
}

Check Float Format Function

This function checks if the given string represents a valid float value. The valid format consists of digits (0-9), a single decimal point (.), and an optional negative sign (-) at the beginning of the string. If the string contains any other characters or multiple decimal points, an error message is printed to the standard error stream, and the function returns -1. Otherwise, it returns 0.

Parameters

  • str: The flag's float value as a string to be checked.

Return Value

  • Returns 0 if the format is valid.
  • Returns -1 if the format is invalid.
int check_float_format(char *str)
{
    int i;
    int dot_count;

    i = 0;
    dot_count = 0;
    while (str[i])
    {
        if ((str[i] < 48 || str[i] > 57) && str[i] != '.' && str[i] != '-')
        {
            ft_dprintf(2, "%s '%c'\n", ERR_INVALID_CHAR, str[i]);
            return (-1);
        }
        if (str[i] == '.')
            dot_count++;
        i++;
    }
    if (dot_count > 1)
    {
        ft_dprintf(2, "%s\n", ERR_DOT_COUNT);
        return (-1);
    }
    return (0);
}

Check Vector Format Function

This function checks if the given string represents a valid vector value. The valid format consists of digits (0-9), a single decimal point (.), and an optional negative sign (-) at the beginning of the value. The vector values (x, y, and z) must be separated by commas (,). If the string contains any other characters or multiple decimal points before a comma, an error message is printed to the standard error stream, and the function returns -1. Otherwise, it returns 0.

Parameters

  • str: The flag's vector value as a string to be checked.

Return Value

  • Returns 0 if the format is valid.
  • Returns -1 if the format is invalid.
int check_vector_format(char *str)
{
    int i;
    int dot_count;

    i = 0;
    dot_count = 0;
    while (str[i])
    {
        if ((str[i] < 48 || str[i] > 57) && str[i] != '.'
            && str[i] != '-' && str[i] != ',')
        {
            ft_dprintf(2, "%s '%c'\n", ERR_INVALID_CHAR, str[i]);
            return (-1);
        }
        else if (str[i] == '.')
            dot_count++;
        if (dot_count > 1)
        {
            ft_dprintf(2, "%s\n", ERR_DOT_COUNT);
            return (-1);
        }
        else if (str[i] == ',')
            dot_count = 0;
        i++;
    }
    return (0);
}

FLAG_UTILS.C

Check If Option Function

This function checks if the given string has a valid format for a flag. It also considers negative numbers with the (-) sign to be invalid and ignores them. If the string is a valid option, it is duplicated and returned. Otherwise, NULL is returned.

Parameters

  • str: The string to be checked.

Return Value

  • Returns a pointer to the duplicated string if it is a valid option.
  • Returns NULL if the string is not a valid option.
char	*check_if_option(char *str)
{
	char	*ret;

	if (!str)
		return (NULL);
	if (check_scene_file(str) == 0)
		return (NULL);
	if ((is_number(str) == 1) && str[0] == '-')
		return (NULL);
	ret = ft_strdup(str);
	return (ret);
}

Is Number Function

This function checks if a given string is a valid number. It iterates through each character of the string and verifies if it falls within the range of valid characters for a number. Valid characters include digits (0-9), decimal point (.), minus sign (-), and comma (,).

Parameters

  • str: The string to be checked.

Return Value

  • Returns 0 if the string is a valid number.
  • Returns 1 otherwise.
int is_number(char *str)
{
    int i;

    i = 0;
    while (str[i])
    {
        if ((str[i] < 48 || str[i] > 57) && str[i] != '.'
            && str[i] != '-' && str[i] != ',')
            return (1);
        i++;
    }
    return (0);
}

Ft Atob Function

This function converts a string to a double value. It processes the string to handle optional negative signs, integer parts, and fractional parts. The conversion is done by using the parse_num and parse_frac functions to handle the respective parts of the number.

Parameters

  • str: The string to be converted.

Return Value

  • Returns the converted double value.
double ft_atob(char *str)
{
    int     i;
    double  result;
    bool    is_negative;

    i = 0;
    is_negative = false;
    if (str[i] == '-')
    {
        is_negative = true;
        i++;
    }
    else if (str[i] == '+')
        i++;
    result = parse_num(str, &i, false, 10.0);
    if (str[i] == '.')
    {
        i++;
        result += parse_frac(str, &i, 10.0);
    }
    if (is_negative)
        result = -result;
    return (result);
}

Parse Num Function

This function parses a number from a string, handling both integer and floating-point formats. It iterates through the string, converting characters to a double value. If an invalid character is encountered, an error message is printed to the standard error stream, and -1 is returned.

Parameters

  • str: The string to parse the number from.
  • i: A pointer to the current index in the string.
  • isf: A boolean flag indicating if the number is a floating-point number.
  • frac_div: The divisor used to calculate the fractional part of the number.

Return Value

  • Returns the parsed number as a double value, or -1 if an error occurred.
static double parse_num(const char *str, int *i, bool isf, double frac_div)
{
    double result;

    result = 0.0;
    while (str[*i] != '\0' && (ft_isdigit(str[*i]) || (str[*i] == '.' && !isf)))
    {
        if (ft_isdigit(str[*i]))
        {
            if (isf)
            {
                result += (str[*i] - '0') / frac_div;
                frac_div *= 10;
            }
            else
                result = result * 10 + (str[*i] - '0');
        }
        else if (str[*i] == '.')
            isf = true;
        else
        {
            ft_dprintf(2, "%s '%c'\n", ERR_INVALID_ATOB, str[*i]);
            return (-1);
        }
        (*i)++;
    }
    return (result);
}

Parse Frac Function

This function parses the fractional part of a number from a string and returns the corresponding double value. It processes characters following the decimal point and calculates the fractional value based on a given divisor.

Parameters

  • str: The string to parse the fraction from.
  • i: A pointer to the current index in the string.
  • frac_div: The divisor used to calculate the fraction.

Return Value

  • Returns the parsed fraction as a double value.
static double parse_frac(const char *str, int *i, double frac_div)
{
    double frac;

    frac = 0.0;
    while (str[*i] != '\0' && ft_isdigit(str[*i]))
    {
        frac += (str[*i] - '0') / frac_div;
        frac_div *= 10;
        (*i)++;
    }
    return (frac);
}