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 : }
|