LCOV - code coverage report
Current view: top level - src - parser.c (source / functions) Coverage Total Hit
Test: report.info Lines: 77.5 % 289 224
Test Date: 2025-02-11 09:58:02 Functions: 100.0 % 12 12

            Line data    Source code
       1              : #include "parser.h"
       2              : 
       3          207 : command_node_t *create_command_node()
       4              : {
       5          207 :     command_node_t *node = malloc(sizeof(command_node_t));
       6          207 :     if (node == NULL)
       7              :     {
       8            0 :         errno = ENOMEM;
       9            0 :         print_error("[ERROR] Failed to allocate memory for command node");
      10            0 :         return NULL;
      11              :     }
      12              : 
      13          207 :     node->args = malloc(MAX_ARGS * sizeof(char *));
      14          207 :     if (node->args == NULL)
      15              :     {
      16            0 :         errno = ENOMEM;
      17            0 :         print_error("[ERROR] Failed to allocate memory for command node arguments");
      18            0 :         free_if_needed(node);
      19            0 :         return NULL;
      20              :     }
      21              : 
      22        53199 :     for (int i = 0; i < MAX_ARGS; i++)
      23              :     {
      24        52992 :         node->args[i] = NULL;
      25              :     }
      26          207 :     node->op_type = OP_NONE;
      27          207 :     node->left = NULL;
      28          207 :     node->right = NULL;
      29              : 
      30          207 :     return node;
      31              : }
      32              : 
      33           46 : command_tree_t *create_command_tree()
      34              : {
      35           46 :     command_tree_t *tree = malloc(sizeof(command_tree_t));
      36           46 :     if (tree == NULL)
      37              :     {
      38            0 :         errno = ENOMEM;
      39            0 :         print_error("[ERROR] Failed to allocate memory for command tree");
      40            0 :         return NULL;
      41              :     }
      42              : 
      43           46 :     tree->root = create_command_node();
      44           46 :     if (tree->root == NULL)
      45              :     {
      46            0 :         free_if_needed(tree);
      47            0 :         return NULL;
      48              :     }
      49              : 
      50           46 :     return tree;
      51              : }
      52              : 
      53           46 : command_node_t *handle_operator(command_tree_t *tree, operator_t op)
      54              : {
      55           46 :     command_node_t *new_node = create_command_node();
      56           46 :     if (new_node == NULL)
      57              :     {
      58            0 :         errno = ENOMEM;
      59            0 :         print_error("[ERROR] Failed to create new command node");
      60            0 :         return NULL;
      61              :     }
      62              : 
      63           46 :     new_node->left = tree->root;
      64           46 :     new_node->op_type = op;
      65           46 :     new_node->right = create_command_node();
      66           46 :     if (new_node->right == NULL)
      67              :     {
      68            0 :         errno = ENOMEM;
      69            0 :         print_error("[ERROR] Failed to create right command node");
      70            0 :         free_command_node(new_node);
      71            0 :         return NULL;
      72              :     }
      73              : 
      74           46 :     return new_node;
      75              : }
      76              : 
      77          157 : int handle_argument(command_node_t *current, const char *token, size_t index)
      78              : {
      79          157 :     if (index >= MAX_ARGS - 1)
      80              :     {
      81            1 :         errno = E2BIG;
      82            1 :         print_error("[ERROR] Too many arguments");
      83            1 :         current->args[MAX_ARGS - 1] = NULL;
      84            1 :         return EXIT_FAILURE;
      85              :     }
      86              : 
      87          156 :     char *stored_value = NULL;
      88              : 
      89          156 :     if (token[0] == DOLLAR_SIGN)
      90              :     {
      91            6 :         char *env_value = get_env_var(token);
      92            6 :         if (env_value != NULL)
      93            6 :             stored_value = env_value;
      94              :         else
      95            0 :             stored_value = strdup("");
      96              :     }
      97              :     else
      98              :     {
      99          150 :         stored_value = strdup(token);
     100              :     }
     101              : 
     102          156 :     if (stored_value == NULL)
     103              :     {
     104            0 :         errno = ENOMEM;
     105            0 :         print_error("[ERROR] Failed to duplicate argument string");
     106            0 :         free_if_needed(stored_value);
     107            0 :         return EXIT_FAILURE;
     108              :     }
     109              : 
     110          156 :     if (current->args[index] != NULL)
     111            0 :         free_if_needed(current->args[index]); // free_if_needed any existing argument
     112              : 
     113          156 :     current->args[index] = stored_value;
     114              : 
     115          156 :     return EXIT_SUCCESS;
     116              : }
     117              : 
     118              : /**
     119              :  * @brief Check if the tree contains a specific operator
     120              :  * @param node Command node to check
     121              :  * @param op Operator to search for
     122              :  * @return int 1 if the operator is found, 0 otherwise
     123              :  */
     124          141 : static int tree_contains_operator(command_node_t *node, operator_t op)
     125              : {
     126          141 :     if (node == NULL)
     127           80 :         return 0;
     128              : 
     129           61 :     if (node->op_type == op)
     130            1 :         return 1;
     131              : 
     132           60 :     return tree_contains_operator(node->left, op) || tree_contains_operator(node->right, op);
     133              : }
     134              : 
     135              : /**
     136              :  * @brief Insert a pipe node into the tree
     137              :  * @param tree Command tree to insert into
     138              :  * @param new_node New node to insert
     139              :  * @return void
     140              :  */
     141            1 : static void insert_pipe_node(command_tree_t *tree, command_node_t *new_node)
     142              : {
     143            1 :     command_node_t *current = tree->root;
     144            1 :     command_node_t *parent = NULL;
     145              : 
     146            2 :     while (current != NULL && (current->op_type == OP_AND || current->op_type == OP_OR))
     147              :     {
     148            1 :         parent = current;
     149            1 :         current = current->right;
     150              :     }
     151              : 
     152            1 :     if (parent == NULL)
     153            0 :         tree->root = new_node;
     154              :     else
     155            1 :         parent->right = new_node;
     156              : 
     157            1 :     new_node->left = current;
     158            1 : }
     159              : 
     160           45 : command_tree_t *parse_command(const char *input)
     161              : {
     162           45 :     if (input == NULL)
     163            1 :         return NULL;
     164              : 
     165           44 :     char *input_copy = strdup(input);
     166           44 :     if (input_copy == NULL)
     167              :     {
     168            0 :         print_error("[ERROR] Failed to duplicate input string");
     169            0 :         return NULL;
     170              :     }
     171              : 
     172           44 :     command_tree_t *tree = create_command_tree();
     173           44 :     if (tree == NULL)
     174              :     {
     175            0 :         free_if_needed(input_copy);
     176            0 :         print_error("[ERROR] Failed to create command tree");
     177            0 :         return NULL;
     178              :     }
     179              : 
     180           44 :     command_node_t *current = tree->root;
     181           44 :     size_t arg_index = 0;
     182           44 :     char *next_token = NULL;
     183           44 :     char *previous_token = NULL;
     184           44 :     char *token = enhanced_strtok(input_copy, CMD_DELIMITER, &next_token);
     185              : 
     186          261 :     while (token != NULL)
     187              :     {
     188              :         // Handle alias expansion safely
     189          217 :         int index = get_alias_index(token);
     190          217 :         if (index != -1 && !(previous_token != NULL && strcmp(previous_token, "unalias") == 0))
     191            2 :         {
     192            2 :             char *alias_command = get_alias_command(token);
     193            2 :             if (alias_command == NULL)
     194              :             {
     195            0 :                 print_error("[ERROR] Failed to retrieve alias command");
     196            0 :                 free_command_node(tree->root);
     197            0 :                 free_if_needed(tree);
     198            0 :                 free_if_needed(input_copy);
     199            0 :                 return NULL;
     200              :             }
     201              : 
     202            2 :             size_t expanded_size = strlen(alias_command) + (next_token ? strlen(next_token) : 0) + 2;
     203            2 :             char *expanded_command = malloc(expanded_size);
     204            2 :             if (!expanded_command)
     205              :             {
     206            0 :                 print_error("[ERROR] Memory allocation failed for alias expansion");
     207            0 :                 free_if_needed(alias_command);
     208            0 :                 free_command_node(tree->root);
     209            0 :                 free_if_needed(tree);
     210            0 :                 free_if_needed(input_copy);
     211            0 :                 return NULL;
     212              :             }
     213              : 
     214            2 :             snprintf(expanded_command, expanded_size, "%s %s", alias_command, next_token ? next_token : "");
     215            2 :             free_if_needed(alias_command);
     216            2 :             free_if_needed(input_copy);
     217            2 :             input_copy = expanded_command;
     218            2 :             next_token = NULL;
     219            2 :             token = enhanced_strtok(input_copy, CMD_DELIMITER, &next_token);
     220            2 :             continue;
     221              :         }
     222              : 
     223              :         // Handle environment variable assignment safely
     224          215 :         if (strchr(token, EQUAL_SIGN) != NULL)
     225              :         {
     226           24 :             if (!(previous_token != NULL && strcmp(previous_token, "alias") == 0))
     227              :             {
     228           16 :                 if (set_env_var(token) != 0)
     229              :                 {
     230            0 :                     print_error("[ERROR] Failed to set environment variable");
     231            0 :                     free_command_node(tree->root);
     232            0 :                     free_if_needed(tree);
     233            0 :                     free_if_needed(input_copy);
     234            0 :                     return NULL;
     235              :                 }
     236           16 :                 free_if_needed(previous_token);
     237           16 :                 previous_token = strdup(token);
     238           16 :                 token = enhanced_strtok(NULL, CMD_DELIMITER, &next_token);
     239           16 :                 continue;
     240              :             }
     241              :         }
     242              : 
     243              :         // Handle operators
     244          199 :         operator_t op = get_operator_type(token);
     245          199 :         if (op != OP_NONE)
     246              :         {
     247           45 :             command_node_t *new_node = handle_operator(tree, op);
     248           45 :             if (new_node == NULL)
     249              :             {
     250            0 :                 print_error("[ERROR] Failed to handle operator");
     251            0 :                 free_command_node(tree->root);
     252            0 :                 free_if_needed(tree);
     253            0 :                 free_if_needed(input_copy);
     254            0 :                 return NULL;
     255              :             }
     256              : 
     257           45 :             if (op == OP_PIPE && (tree_contains_operator(tree->root, OP_AND) || tree_contains_operator(tree->root, OP_OR)))
     258            1 :                 insert_pipe_node(tree, new_node);
     259              :             else
     260           44 :                 tree->root = new_node;
     261              : 
     262           45 :             current = new_node->right;
     263           45 :             arg_index = 0;
     264              :         }
     265              :         // Handle arguments
     266          154 :         else if (handle_argument(current, token, arg_index++) == EXIT_FAILURE)
     267              :         {
     268            0 :             print_error("[ERROR] Failed to handle argument");
     269            0 :             free_command_node(tree->root);
     270            0 :             free_if_needed(tree);
     271            0 :             free_if_needed(input_copy);
     272            0 :             return NULL;
     273              :         }
     274              : 
     275          199 :         free_if_needed(previous_token);
     276          199 :         previous_token = strdup(token);
     277          199 :         token = enhanced_strtok(NULL, CMD_DELIMITER, &next_token);
     278              :     }
     279              : 
     280           44 :     current->args[arg_index] = NULL;
     281           44 :     free_if_needed(input_copy);
     282           44 :     free_if_needed(previous_token);
     283           44 :     return tree;
     284              : }
     285              : 
     286          210 : operator_t get_operator_type(const char *operator_str)
     287              : {
     288          210 :     if (operator_str == NULL)
     289            1 :         return OP_NONE;
     290              : 
     291          209 :     if (strcmp(operator_str, "|") == 0)
     292           12 :         return OP_PIPE;
     293          197 :     else if (strcmp(operator_str, ";") == 0)
     294           23 :         return OP_SEQ;
     295          174 :     else if (strcmp(operator_str, "&&") == 0)
     296            7 :         return OP_AND;
     297          167 :     else if (strcmp(operator_str, "||") == 0)
     298            4 :         return OP_OR;
     299          163 :     else if (strcmp(operator_str, "&") == 0)
     300            2 :         return OP_BG;
     301          161 :     else if (strcmp(operator_str, ">") == 0)
     302            2 :         return OP_REDIR_OUT;
     303          159 :     else if (strcmp(operator_str, "<") == 0)
     304            2 :         return OP_REDIR_IN;
     305          157 :     else if (strcmp(operator_str, ">>") == 0)
     306            1 :         return OP_APPEND;
     307          156 :     else if (strcmp(operator_str, "<<") == 0)
     308            1 :         return OP_HEREDOC;
     309              :     else
     310          155 :         return OP_NONE;
     311              : }
     312              : 
     313          217 : char *handle_env_assignment(char *str, const char *delim, char **next_token)
     314              : {
     315              :     static char *last_allocated = NULL;
     316              : 
     317          217 :     if (last_allocated != NULL)
     318              :     {
     319           20 :         free_if_needed(last_allocated);
     320           20 :         last_allocated = NULL;
     321              :     }
     322              : 
     323          217 :     char *equals = strchr(str, EQUAL_SIGN);
     324          217 :     if (!equals || equals <= str || equals >= str + strcspn(str, delim))
     325          193 :         return NULL;
     326              : 
     327           24 :     char *value_start = equals + 1;
     328           24 :     while (*value_start && isspace(*value_start))
     329            0 :         value_start++;
     330              : 
     331           24 :     if (*value_start != DOUBLE_QUOTES && *value_start != SINGLE_QUOTE)
     332            3 :         return NULL;
     333              : 
     334           21 :     char *quote_end = handle_quoted_string(value_start, *value_start, next_token);
     335              : 
     336           21 :     if (quote_end)
     337              :     {
     338           21 :         last_allocated = quote_end;
     339           21 :         return str;
     340              :     }
     341              : 
     342            0 :     free_if_needed(quote_end);
     343              : 
     344            0 :     return NULL;
     345              : }
     346              : 
     347           35 : char *expand_env_vars(const char *str)
     348              : {
     349           35 :     if (!str || !strchr(str, DOLLAR_SIGN))
     350           30 :         return strdup(str);
     351              : 
     352            5 :     size_t total_size = 0;
     353            5 :     const char *read_pos = str;
     354              : 
     355           64 :     while (*read_pos)
     356              :     {
     357           59 :         if (*read_pos == DOLLAR_SIGN && *(read_pos + 1))
     358           10 :         {
     359           10 :             char var_name[256] = {0};
     360           10 :             var_name[0] = DOLLAR_SIGN;
     361           10 :             int i = 1;
     362           18 :             while (isalnum(read_pos[i]) || read_pos[i] == '_')
     363              :             {
     364            8 :                 var_name[i] = read_pos[i];
     365            8 :                 i++;
     366              :             }
     367              : 
     368           10 :             char *value = get_env_var(var_name);
     369           10 :             if (value)
     370              :             {
     371           10 :                 total_size += strlen(value);
     372           10 :                 free_if_needed(value);
     373              :             }
     374           10 :             read_pos += i;
     375              :         }
     376              :         else
     377              :         {
     378           49 :             total_size++;
     379           49 :             read_pos++;
     380              :         }
     381              :     }
     382              : 
     383            5 :     char *result = malloc(total_size + 1);
     384            5 :     if (!result)
     385              :     {
     386            0 :         errno = ENOMEM;
     387            0 :         print_error("[ERROR] Failed to allocate memory");
     388            0 :         return NULL;
     389              :     }
     390              : 
     391            5 :     read_pos = str;
     392            5 :     char *write_pos = result;
     393              : 
     394           64 :     while (*read_pos)
     395              :     {
     396           59 :         if (*read_pos == DOLLAR_SIGN && *(read_pos + 1))
     397           10 :         {
     398           10 :             char var_name[256] = {0};
     399           10 :             var_name[0] = DOLLAR_SIGN;
     400           10 :             int i = 1;
     401           18 :             while (isalnum(read_pos[i]) || read_pos[i] == '_')
     402              :             {
     403            8 :                 var_name[i] = read_pos[i];
     404            8 :                 i++;
     405              :             }
     406              : 
     407           10 :             char *value = get_env_var(var_name);
     408           10 :             if (value)
     409              :             {
     410           10 :                 size_t len = strlen(value);
     411           10 :                 memcpy(write_pos, value, len);
     412           10 :                 write_pos += len;
     413           10 :                 free_if_needed(value);
     414              :             }
     415           10 :             read_pos += i;
     416              :         }
     417              :         else
     418              :         {
     419           49 :             *write_pos++ = *read_pos++;
     420              :         }
     421              :     }
     422            5 :     *write_pos = NULL_CHAR;
     423            5 :     return result;
     424              : }
     425              : 
     426           35 : char *handle_quoted_string(char *str, char quote, char **next_token)
     427              : {
     428           35 :     str++;
     429           35 :     char *end = str;
     430           35 :     char *result = NULL;
     431              : 
     432          485 :     while (*end && *end != quote)
     433          450 :         end++;
     434              : 
     435           35 :     if (*end == quote)
     436              :     {
     437           35 :         *end = NULL_CHAR;
     438           35 :         *next_token = end + 1;
     439              : 
     440           35 :         if (quote == DOUBLE_QUOTES || quote == SINGLE_QUOTE)
     441           35 :             result = expand_env_vars(str);
     442              :         else
     443            0 :             result = strdup(str);
     444              : 
     445           35 :         return result; // enhanced_strtok will free_if_needed this
     446              :     }
     447            0 :     return NULL;
     448              : }
     449              : 
     450          261 : char *enhanced_strtok(char *str, const char *delim, char **next_token)
     451              : {
     452              :     static char *last_allocated = NULL;
     453              : 
     454          261 :     if (last_allocated != NULL)
     455              :     {
     456           14 :         free_if_needed(last_allocated); // free_if_needed any previously result
     457           14 :         last_allocated = NULL;
     458              :     }
     459              : 
     460          261 :     if (str == NULL)
     461          215 :         str = *next_token;
     462              : 
     463          261 :     str += strspn(str, delim);
     464          261 :     if (*str == NULL_CHAR)
     465           44 :         return NULL;
     466              : 
     467          217 :     char *token = str;
     468          217 :     char *env_result = handle_env_assignment(str, delim, next_token);
     469          217 :     if (env_result)
     470           21 :         return env_result;
     471              : 
     472          196 :     if (*str == DOUBLE_QUOTES || *str == SINGLE_QUOTE)
     473              :     {
     474           14 :         char *result = handle_quoted_string(str, *str, next_token);
     475           14 :         last_allocated = result;
     476           14 :         return result;
     477              :     }
     478              : 
     479          182 :     str += strcspn(str, delim);
     480              : 
     481          182 :     if (*str)
     482          148 :         *str++ = NULL_CHAR;
     483              : 
     484          182 :     *next_token = str;
     485          182 :     return token;
     486              : }
        

Generated by: LCOV version 2.0-1