diff --git a/Makefile b/Makefile index 73339b5..fd2d6ee 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,8 @@ SRCS = src/ INCLUDES = include/ libtf/ libft/ # .c files in src/ without the extension CODE = main ask_command error path parse_command exec_command builtin \ - signals cool_readline + signals cool_readline variables parse_command_utils \ + parse_command_read_string parse_command_read_element # directories to 'make' LIBRARIES = libtf libft # .a files to include diff --git a/include/include.h b/include/include.h index c952ba5..e46c7bd 100644 --- a/include/include.h +++ b/include/include.h @@ -6,7 +6,7 @@ /* By: jschaft +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/04/23 14:15:12 by mcolonna #+# #+# */ -/* Updated: 2024/06/18 13:53:15 by mcolonna ### ########.fr */ +/* Updated: 2024/06/27 13:48:28 by mcolonna ### ########.fr */ /* */ /* ************************************************************************** */ @@ -32,6 +32,7 @@ # include "libtf.h" # include "main.h" +# include "variables.h" # include "cool_readline.h" # include "ask_command.h" # include "parse_command.h" diff --git a/include/parse_command.h b/include/parse_command.h index 1b36349..ded1531 100644 --- a/include/parse_command.h +++ b/include/parse_command.h @@ -6,7 +6,7 @@ /* By: jschaft +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/04/23 14:15:12 by mcolonna #+# #+# */ -/* Updated: 2024/06/25 15:43:15 by mcolonna ### ########.fr */ +/* Updated: 2024/06/27 13:47:56 by mcolonna ### ########.fr */ /* */ /* ************************************************************************** */ @@ -15,12 +15,6 @@ # include "include.h" -typedef struct s_variable -{ - const char *name; - const char *value; -} t_variable; - // Represents a call to a program (the program name and its arguments) typedef struct s_call { diff --git a/include/variables.h b/include/variables.h new file mode 100644 index 0000000..b993695 --- /dev/null +++ b/include/variables.h @@ -0,0 +1,28 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* variables.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/06/27 13:43:21 by mcolonna #+# #+# */ +/* Updated: 2024/06/27 14:43:36 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef VARIABLES_H +# define VARIABLES_H + +typedef struct s_variable +{ + const char *name; + const char *value; +} t_variable; + +// Set a variable to a new value. +void variables_set(t_list *variables, const t_variable var); + +// Get the value of a variable from its name. +const char *variables_get(t_list *variables, const char *name); + +#endif diff --git a/minishell.srctrlbm b/minishell.srctrlbm new file mode 100644 index 0000000..04c97e4 Binary files /dev/null and b/minishell.srctrlbm differ diff --git a/minishell.srctrldb b/minishell.srctrldb new file mode 100644 index 0000000..999b93a Binary files /dev/null and b/minishell.srctrldb differ diff --git a/minishell.srctrlprj b/minishell.srctrlprj new file mode 100644 index 0000000..3b0680b --- /dev/null +++ b/minishell.srctrlprj @@ -0,0 +1,36 @@ + + + + + gnu17 + + + unknown + x86_64 + unknown + unknown + + 0 + + + include + libtf + libft + + minishell + + 1 + + + .c + .h + + + src/parse_command.c + + enabled + C Source Group + + + 8 + diff --git a/src/exec_command.c b/src/exec_command.c index b15bcdb..5ebfacc 100644 --- a/src/exec_command.c +++ b/src/exec_command.c @@ -6,19 +6,12 @@ /* By: jschaft +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/04/24 13:48:00 by jschaft #+# #+# */ -/* Updated: 2024/06/25 15:45:58 by mcolonna ### ########.fr */ +/* Updated: 2024/06/27 14:45:31 by mcolonna ### ########.fr */ /* */ /* ************************************************************************** */ #include "include.h" - -typedef struct s_exec_command_global -{ - t_memclass mc; - int nb_calls; - t_pipes *pipes; - t_env *env; -} t_exec_command_global; +#include "exec_command_utils.h" static void close_all_pipes(t_exec_command_global *global) { diff --git a/src/exec_command_utils.h b/src/exec_command_utils.h new file mode 100644 index 0000000..25d4e9d --- /dev/null +++ b/src/exec_command_utils.h @@ -0,0 +1,26 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* exec_command_utils.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: jschaft +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:48:00 by jschaft #+# #+# */ +/* Updated: 2024/06/27 14:45:14 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef EXEC_COMMAND_UTILS_H +# define EXEC_COMMAND_UTILS_H + +# include "include.h" + +typedef struct s_exec_command_global +{ + t_memclass mc; + int nb_calls; + t_pipes *pipes; + t_env *env; +} t_exec_command_global; + +#endif diff --git a/src/parse_command.c b/src/parse_command.c index 64ff68f..4516db6 100644 --- a/src/parse_command.c +++ b/src/parse_command.c @@ -6,67 +6,14 @@ /* By: mcolonna +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/04/24 13:47:40 by mcolonna #+# #+# */ -/* Updated: 2024/06/27 13:30:12 by mcolonna ### ########.fr */ +/* Updated: 2024/06/27 14:52:32 by mcolonna ### ########.fr */ /* */ /* ************************************************************************** */ #include "include.h" - -#define SYMBOL_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" - -static void variables_free(t_list *variables, t_list_element *el) -{ - if (el->previous) - el->previous->next = el->next; - else - variables->first = el->next; - if (el->next) - el->next->previous = el->previous; - else - variables->last = el->previous; - variables->size--; - mem_free(el->value); - mem_free(el); -} - -// Return a pointer to the variable 'name'. -// (An undefined variable is considered of an empty value) -static t_variable *variables_find(t_list *variables, const char *name) -{ - t_list_element *el; - t_list_element *next; - t_variable *r; - - el = variables->first; - while (el) - { - next = el->next; - r = (t_variable *)el->value; - if (str_eq(r->name, name)) - return (r); - if (str_eq(r->value, "")) - variables_free(variables, el); - el = next; - } - r = mem_alloc(fatal_error, variables->mc, sizeof(t_variable)); - r->name = str_dup(fatal_error, variables->mc, name); - r->value = ""; - list_add(fatal_error, variables, r); - return (r); -} - -// Set a variable to a new value. -void variables_set(t_list *variables, const t_variable var) -{ - variables_find(variables, var.name)->value = ( - str_dup(fatal_error, variables->mc, var.value)); -} - -// Get the value of a variable from its name. -const char *variables_get(t_list *variables, const char *name) -{ - return (variables_find(variables, name)->value); -} +#include "parse_command_utils.h" +#include "parse_command_read_string.h" +#include "parse_command_read_element.h" // Ask the user for data and set readfd to the fd which will receive this // data. @@ -76,7 +23,6 @@ static int heredoc(t_memclass mc, int *readfd, const char *eof) const t_memclass mc_in = mem_subclass(fatal_error, mc); int outpipe[2]; char *line; - char *to_free; const t_const_string eof_line = str_join(fatal_error, mc_in, eof, "\n"); if (pipe(outpipe) == -1) @@ -85,14 +31,13 @@ static int heredoc(t_memclass mc, int *readfd, const char *eof) while (true) { line = cool_readline("\e[38;5;33m( 'o')> \e[0m"); - to_free = line; if (!line) - line = ""; + continue ; if (str_eq(line, eof) || str_eq(line, eof_line)) break ; print_str(err_remember, outpipe[1], line); print_str(err_remember, outpipe[1], "\n"); - free(to_free); + free(line); if (err_get()) return (minishell_error("errno"), errno); } @@ -102,338 +47,6 @@ static int heredoc(t_memclass mc, int *readfd, const char *eof) return (0); } -// To call when a parse error occurs. -// Always returns 1 (parse error's status). -static int parse_error(const char *msg) -{ - print_str(fatal_error, 2, "parse error: "); - print_line(fatal_error, 2, msg); - return (1); -} - -// Global variables for all the parsing functions -typedef struct s_parsing_args -{ - t_memclass mc; // mc freed given to parse_command - t_command r; // t_command that parse_command will return - t_stream stream; // stream reading the command string - t_list calls; // list of calls - bool got_first_call; // already got at least the first program call? - const char *heredoc; // EOF line for heredoc. NULL if no heredoc - t_list *variables; // list of current variables - t_env *env; -} t_parsing_args; - -// Skip blank characters -static void skip_blank(t_stream *stream) -{ - stream_skip(stream, " \r\n\t"); -} - -// Add 'c' at the end of 'str' and return the result. -// Also mem_free() 'str'. -static char *str_addchar(t_memclass mc, const char *str, char c) -{ - char *s; - char *r; - - s = str_dup(fatal_error, mc, "-"); - s[0] = c; - r = str_join(fatal_error, mc, str, s); - mem_free(s); - mem_free((void *)str); - return (r); -} - -// Read until a character is in the charset or is '\0' -// and append the string to dest. -static void read_until(t_parsing_args *args, const char **dest, - const char *stop_charset) -{ - while (stream_read(&args->stream) - && !char_isin(stream_read(&args->stream), stop_charset) - ) - *dest = str_addchar(args->mc, *dest, stream_pop(&args->stream)); -} - -// Read until a character isn't in the charset or is '\0' -// and append the string to dest. -void read_only(t_parsing_args *args, const char **dest, const char *charset) -{ - while (char_isin(stream_read(&args->stream), charset)) - *dest = str_addchar(args->mc, *dest, stream_pop(&args->stream)); -} - -// Read the value of the variable and append it to dest. -// The stream must point to the first char of the variable name. -// Write a parse error and change args->r.error if no variable name. -static void read_variable_value(t_parsing_args *args, const char **dest) -{ - const char *name; - const char *value; - const char *tmp; - - name = NULL; - if (stream_read(&args->stream) == '?') - { - stream_pop(&args->stream); - value = str_inttostr(fatal_error, args->mc, args->env->errorstatus); - } - else - { - name = str_dup(fatal_error, args->mc, ""); - read_only(args, &name, SYMBOL_CHARS); - if (str_eq(name, "")) - { - args->r.error = parse_error("variable name expected"); - return ; - } - value = variables_get(args->variables, name); - } - tmp = *dest; - *dest = str_join(fatal_error, args->mc, *dest, value); - mem_free((char *)name); - mem_free((char *)tmp); -} - -// Read a string without quotes. -// Append it to dest. -// Change args->r.error if error. -static void read_string_noquote(t_parsing_args *args, const char **dest, - const char *stop_charset) -{ - const char *real_stop_charset - = str_join(fatal_error, args->mc, stop_charset, "$"); - - while (stream_read(&args->stream) - && !char_isin(stream_read(&args->stream), stop_charset)) - { - read_until(args, dest, real_stop_charset); - if (stream_read(&args->stream) == '$') - { - stream_pop(&args->stream); - read_variable_value(args, dest); - if (args->r.error) - return ; - } - } -} - -// Read a string with ' quotes. -// Append it to dest. -// Return false if it is not a ' quoted string. -// If parse error, return true and change args.r.error accordingly. -static bool read_string_quote(t_parsing_args *args, const char **dest) -{ - if (stream_read(&args->stream) != '\'') - return (false); - stream_pop(&args->stream); - read_until(args, dest, "'"); - if (!stream_read(&args->stream)) - { - args->r.error = parse_error("EOF unexpected"); - return (true); - } - stream_pop(&args->stream); - return (true); -} - -// Read a string with " quotes. -// Append it to dest. -// Return false if it is not a " quoted string. -// If parse error, return true and change args.r.error accordingly. -static bool read_string_doublequote(t_parsing_args *args, const char **dest) -{ - if (stream_read(&args->stream) != '"') - return (false); - stream_pop(&args->stream); - while (stream_read(&args->stream) && stream_read(&args->stream) != '"') - { - read_until(args, dest, "\"$"); - if (stream_read(&args->stream) == '$') - { - stream_pop(&args->stream); - read_variable_value(args, dest); - if (args->r.error) - return (true); - } - } - if (!stream_read(&args->stream)) - { - args->r.error = parse_error("EOF unexpected"); - return (true); - } - stream_pop(&args->stream); - return (true); -} - -// Read the string, stop if the char is in stop_charset. -// Possible syntaxes: -// - /[^(stop_charset)]+/ -// - /'.*'/ -// - /".*"/ -// Change args->r.error and return NULL if error. -static const char *read_string(t_parsing_args *args, const char *stop_charset) -{ - const char *str; - char *const real_stop_charset - = str_join(fatal_error, args->mc, stop_charset, " \r\n\t"); - - if (!stream_read(&args->stream)) - return (NULL); - str = str_dup(fatal_error, args->mc, ""); - while (!args->r.error - && stream_read(&args->stream) - && !char_isin(stream_read(&args->stream), real_stop_charset)) - { - if (!read_string_quote(args, &str) - && !read_string_doublequote(args, &str)) - read_string_noquote(args, &str, real_stop_charset); - } - mem_free(real_stop_charset); - if (args->r.error) - return (NULL); - return (str); -} - -// Get a program call (program names & its arguments) until stop_charset. -// Change args accordingly. -// On success, return 0. On error, return the error status. -static int read_call(t_parsing_args *args, const char *stop_charset) -{ - t_call *r; - t_list arguments; - const char *str; - - arguments = list_createempty(args->mc); - while (stream_read(&args->stream) - && !char_isin(stream_read(&args->stream), stop_charset)) - { - str = read_string(args, stop_charset); - if (args->r.error) - return (args->r.error); - if (!str) - return (parse_error("EOF unexpected")); - list_add(fatal_error, &arguments, (char *)str); - skip_blank(&args->stream); - } - r = mem_alloc(fatal_error, args->mc, sizeof(t_call)); - r->program = (char *)list_get(err_remember, &arguments, 0); - if (err_get()) - return (parse_error("program name expected")); - r->argc = list_getsize(&arguments); - r->argv = (char *const *)list_convert(fatal_error, args->mc, &arguments); - list_add(fatal_error, &args->calls, (t_call *)r); - args->got_first_call = true; - return (0); -} - -// Read a '<' or '<<' redirection from the file. -// Change args accordingly. -// On success, return 0. On error, return the error status. -static int read_inputfile(t_parsing_args *args, const char *stop_charset) -{ - const char *str; - bool heredoc; - - if (args->r.input_fd != 0) - return (parse_error("several input files")); - if (!stream_read(&args->stream)) - return (parse_error("EOF unexpected")); - heredoc = stream_read(&args->stream) == '<'; - if (heredoc) - stream_pop(&args->stream); - skip_blank(&args->stream); - str = read_string(args, stop_charset); - if (args->r.error) - return (args->r.error); - if (!str) - return (parse_error("EOF unexpected")); - if (heredoc) - args->heredoc = str; - else - { - args->r.input_fd = open(str, O_RDONLY); - if (args->r.input_fd == -1) - return (perror(str), errno); - } - return (0); -} - -// Read a '>' or '>>' redirection from the file. -// Change args accordingly. -// On success, return 0. On error, return the error status. -static int read_outputfile(t_parsing_args *args, const char *stop_charset) -{ - const char *str; - int flag; - - if (args->r.output_fd != 1) - return (parse_error("several output files")); - if (!stream_read(&args->stream)) - return (parse_error("EOF unexpected")); - flag = O_TRUNC; - if (stream_read(&args->stream) == '>') - { - stream_pop(&args->stream); - flag = O_APPEND; - } - skip_blank(&args->stream); - str = read_string(args, stop_charset); - if (args->r.error) - return (args->r.error); - if (!str) - return (parse_error("EOF unexpected")); - args->r.output_fd = open( - str, O_WRONLY | O_CREAT | flag, 0666); - if (args->r.output_fd == -1) - return (perror(str), errno); - return (0); -} - -// (extension of read_element) -static void read_element2(t_parsing_args *args, int *error, char c) -{ - stream_pop(&args->stream); - skip_blank(&args->stream); - if (c == '|') - { - if (!args->got_first_call) - *error = parse_error("pipe before any call to a program"); - else - *error = read_call(args, "<>|"); - } - else if (c == '>') - *error = read_outputfile(args, "<>|"); - else if (c == '<') - *error = read_inputfile(args, "<>|"); - else - fatal_error_msg("internal error u.u"); -} - -// Read an element from the stream (a call to a program or a redirection) -// Change args accordingly. -// On success, return 0. On error, return the error status. -static int read_element(t_parsing_args *args) -{ - char c; - int error; - - error = 0; - while (!error && stream_read(&args->stream)) - { - c = stream_read(&args->stream); - if (char_isin(c, "|><")) - read_element2(args, &error, c); - else if (!args->got_first_call) - error = read_call(args, "<>|"); - else - return (parse_error("'|', '>' or '<'' expected")); - skip_blank(&args->stream); - } - return (error); -} - // Check if the command is a variable definition. // If possible, change 'variables' accordingly and return true. // If not, return false. diff --git a/src/parse_command_read_element.c b/src/parse_command_read_element.c new file mode 100644 index 0000000..9e93da7 --- /dev/null +++ b/src/parse_command_read_element.c @@ -0,0 +1,153 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse_command_read_element.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:47:40 by mcolonna #+# #+# */ +/* Updated: 2024/06/27 14:42:34 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "include.h" +#include "parse_command_read_string.h" +#include "parse_command_read_element.h" + +// Get a program call (program names & its arguments) until stop_charset. +// Change args accordingly. +// On success, return 0. On error, return the error status. +static int read_call(t_parsing_args *args, const char *stop_charset) +{ + t_call *r; + t_list arguments; + const char *str; + + arguments = list_createempty(args->mc); + while (stream_read(&args->stream) + && !char_isin(stream_read(&args->stream), stop_charset)) + { + str = read_string(args, stop_charset); + if (args->r.error) + return (args->r.error); + if (!str) + return (parse_error("EOF unexpected")); + list_add(fatal_error, &arguments, (char *)str); + skip_blank(&args->stream); + } + r = mem_alloc(fatal_error, args->mc, sizeof(t_call)); + r->program = (char *)list_get(err_remember, &arguments, 0); + if (err_get()) + return (parse_error("program name expected")); + r->argc = list_getsize(&arguments); + r->argv = (char *const *)list_convert(fatal_error, args->mc, &arguments); + list_add(fatal_error, &args->calls, (t_call *)r); + args->got_first_call = true; + return (0); +} + +// Read a '<' or '<<' redirection from the file. +// Change args accordingly. +// On success, return 0. On error, return the error status. +static int read_inputfile(t_parsing_args *args, const char *stop_charset) +{ + const char *str; + bool heredoc; + + if (args->r.input_fd != 0) + return (parse_error("several input files")); + if (!stream_read(&args->stream)) + return (parse_error("EOF unexpected")); + heredoc = stream_read(&args->stream) == '<'; + if (heredoc) + stream_pop(&args->stream); + skip_blank(&args->stream); + str = read_string(args, stop_charset); + if (args->r.error) + return (args->r.error); + if (!str) + return (parse_error("EOF unexpected")); + if (heredoc) + args->heredoc = str; + else + { + args->r.input_fd = open(str, O_RDONLY); + if (args->r.input_fd == -1) + return (perror(str), errno); + } + return (0); +} + +// Read a '>' or '>>' redirection from the file. +// Change args accordingly. +// On success, return 0. On error, return the error status. +static int read_outputfile(t_parsing_args *args, const char *stop_charset) +{ + const char *str; + int flag; + + if (args->r.output_fd != 1) + return (parse_error("several output files")); + if (!stream_read(&args->stream)) + return (parse_error("EOF unexpected")); + flag = O_TRUNC; + if (stream_read(&args->stream) == '>') + { + stream_pop(&args->stream); + flag = O_APPEND; + } + skip_blank(&args->stream); + str = read_string(args, stop_charset); + if (args->r.error) + return (args->r.error); + if (!str) + return (parse_error("EOF unexpected")); + args->r.output_fd = open( + str, O_WRONLY | O_CREAT | flag, 0666); + if (args->r.output_fd == -1) + return (perror(str), errno); + return (0); +} + +// (extension of read_element) +static void read_element2(t_parsing_args *args, int *error, char c) +{ + stream_pop(&args->stream); + skip_blank(&args->stream); + if (c == '|') + { + if (!args->got_first_call) + *error = parse_error("pipe before any call to a program"); + else + *error = read_call(args, "<>|"); + } + else if (c == '>') + *error = read_outputfile(args, "<>|"); + else if (c == '<') + *error = read_inputfile(args, "<>|"); + else + fatal_error_msg("internal error u.u"); +} + +// Read an element from the stream (a call to a program or a redirection) +// Change args accordingly. +// On success, return 0. On error, return the error status. +int read_element(t_parsing_args *args) +{ + char c; + int error; + + error = 0; + while (!error && stream_read(&args->stream)) + { + c = stream_read(&args->stream); + if (char_isin(c, "|><")) + read_element2(args, &error, c); + else if (!args->got_first_call) + error = read_call(args, "<>|"); + else + return (parse_error("'|', '>' or '<'' expected")); + skip_blank(&args->stream); + } + return (error); +} diff --git a/src/parse_command_read_element.h b/src/parse_command_read_element.h new file mode 100644 index 0000000..05f3da5 --- /dev/null +++ b/src/parse_command_read_element.h @@ -0,0 +1,23 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse_command_read_element.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:47:40 by mcolonna #+# #+# */ +/* Updated: 2024/06/27 14:45:51 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef PARSE_COMMAND_READ_ELEMENT_H +# define PARSE_COMMAND_READ_ELEMENT_H + +# include "include.h" + +// Read an element from the stream (a call to a program or a redirection) +// Change args accordingly. +// On success, return 0. On error, return the error status. +int read_element(t_parsing_args *args); + +#endif diff --git a/src/parse_command_read_string.c b/src/parse_command_read_string.c new file mode 100644 index 0000000..8f95dc4 --- /dev/null +++ b/src/parse_command_read_string.c @@ -0,0 +1,146 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse_command_read_string.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:47:40 by mcolonna #+# #+# */ +/* Updated: 2024/06/27 14:32:37 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "include.h" +#include "parse_command_utils.h" + +// Read the value of the variable and append it to dest. +// The stream must point to the first char of the variable name. +// Write a parse error and change args->r.error if no variable name. +static void read_variable_value(t_parsing_args *args, const char **dest) +{ + const char *name; + const char *value; + const char *tmp; + + name = NULL; + if (stream_read(&args->stream) == '?') + { + stream_pop(&args->stream); + value = str_inttostr(fatal_error, args->mc, args->env->errorstatus); + } + else + { + name = str_dup(fatal_error, args->mc, ""); + read_only(args, &name, SYMBOL_CHARS); + if (str_eq(name, "")) + { + args->r.error = parse_error("variable name expected"); + return ; + } + value = variables_get(args->variables, name); + } + tmp = *dest; + *dest = str_join(fatal_error, args->mc, *dest, value); + mem_free((char *)name); + mem_free((char *)tmp); +} + +// Read a string without quotes. +// Append it to dest. +// Change args->r.error if error. +static void read_string_noquote(t_parsing_args *args, const char **dest, + const char *stop_charset) +{ + const char *real_stop_charset + = str_join(fatal_error, args->mc, stop_charset, "$"); + + while (stream_read(&args->stream) + && !char_isin(stream_read(&args->stream), stop_charset)) + { + read_until(args, dest, real_stop_charset); + if (stream_read(&args->stream) == '$') + { + stream_pop(&args->stream); + read_variable_value(args, dest); + if (args->r.error) + return ; + } + } +} + +// Read a string with ' quotes. +// Append it to dest. +// Return false if it is not a ' quoted string. +// If parse error, return true and change args.r.error accordingly. +static bool read_string_quote(t_parsing_args *args, const char **dest) +{ + if (stream_read(&args->stream) != '\'') + return (false); + stream_pop(&args->stream); + read_until(args, dest, "'"); + if (!stream_read(&args->stream)) + { + args->r.error = parse_error("EOF unexpected"); + return (true); + } + stream_pop(&args->stream); + return (true); +} + +// Read a string with " quotes. +// Append it to dest. +// Return false if it is not a " quoted string. +// If parse error, return true and change args.r.error accordingly. +static bool read_string_doublequote(t_parsing_args *args, const char **dest) +{ + if (stream_read(&args->stream) != '"') + return (false); + stream_pop(&args->stream); + while (stream_read(&args->stream) && stream_read(&args->stream) != '"') + { + read_until(args, dest, "\"$"); + if (stream_read(&args->stream) == '$') + { + stream_pop(&args->stream); + read_variable_value(args, dest); + if (args->r.error) + return (true); + } + } + if (!stream_read(&args->stream)) + { + args->r.error = parse_error("EOF unexpected"); + return (true); + } + stream_pop(&args->stream); + return (true); +} + +// Read the string, stop if the char is in stop_charset. +// Possible syntaxes: +// - /[^(stop_charset)]+/ +// - /'.*'/ +// - /".*"/ +// Change args->r.error and return NULL if error. +const char *read_string(t_parsing_args *args, const char *stop_charset) +{ + const char *str; + char *const real_stop_charset + = str_join(fatal_error, args->mc, stop_charset, " \r\n\t"); + + if (!stream_read(&args->stream)) + return (NULL); + str = str_dup(fatal_error, args->mc, ""); + while (!args->r.error + && stream_read(&args->stream) + && !char_isin(stream_read(&args->stream), real_stop_charset)) + { + if (!read_string_quote(args, &str) + && !read_string_doublequote(args, &str)) + read_string_noquote(args, &str, real_stop_charset); + } + mem_free(real_stop_charset); + if (args->r.error) + return (NULL); + return (str); +} diff --git a/src/parse_command_read_string.h b/src/parse_command_read_string.h new file mode 100644 index 0000000..356fb94 --- /dev/null +++ b/src/parse_command_read_string.h @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse_command_read_string.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:47:40 by mcolonna #+# #+# */ +/* Updated: 2024/06/27 14:46:02 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef PARSE_COMMAND_READ_STRING_H +# define PARSE_COMMAND_READ_STRING_H + +# include "include.h" +# include "parse_command_utils.h" + +// Read the string, stop if the char is in stop_charset. +// Possible syntaxes: +// - /[^(stop_charset)]+/ +// - /'.*'/ +// - /".*"/ +// Change args->r.error and return NULL if error. +const char *read_string(t_parsing_args *args, const char *stop_charset); + +#endif diff --git a/src/parse_command_utils.c b/src/parse_command_utils.c new file mode 100644 index 0000000..686413a --- /dev/null +++ b/src/parse_command_utils.c @@ -0,0 +1,56 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse_command_utils.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:47:40 by mcolonna #+# #+# */ +/* Updated: 2024/06/27 14:21:20 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "include.h" +#include "parse_command_utils.h" + +int parse_error(const char *msg) +{ + print_str(fatal_error, 2, "parse error: "); + print_line(fatal_error, 2, msg); + return (1); +} + +void skip_blank(t_stream *stream) +{ + stream_skip(stream, " \r\n\t"); +} + +// Add 'c' at the end of 'str' and return the result. +// Also mem_free() 'str'. +static char *str_addchar(t_memclass mc, const char *str, char c) +{ + char *s; + char *r; + + s = str_dup(fatal_error, mc, "-"); + s[0] = c; + r = str_join(fatal_error, mc, str, s); + mem_free(s); + mem_free((void *)str); + return (r); +} + +void read_until(t_parsing_args *args, const char **dest, + const char *stop_charset) +{ + while (stream_read(&args->stream) + && !char_isin(stream_read(&args->stream), stop_charset) + ) + *dest = str_addchar(args->mc, *dest, stream_pop(&args->stream)); +} + +void read_only(t_parsing_args *args, const char **dest, const char *charset) +{ + while (char_isin(stream_read(&args->stream), charset)) + *dest = str_addchar(args->mc, *dest, stream_pop(&args->stream)); +} diff --git a/src/parse_command_utils.h b/src/parse_command_utils.h new file mode 100644 index 0000000..e622f0b --- /dev/null +++ b/src/parse_command_utils.h @@ -0,0 +1,49 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parse_command_utils.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:47:40 by mcolonna #+# #+# */ +/* Updated: 2024/06/27 14:47:14 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef PARSE_COMMAND_UTILS_H +# define PARSE_COMMAND_UTILS_H + +# include "include.h" + +# define SYMBOL_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" + +// Global variables for all the parsing functions +typedef struct s_parsing_args +{ + t_memclass mc; // mc freed given to parse_command + t_command r; // t_command that parse_command will return + t_stream stream; // stream reading the command string + t_list calls; // list of calls + bool got_first_call; // already got at least the first program call? + const char *heredoc; // EOF line for heredoc. NULL if no heredoc + t_list *variables; // list of current variables + t_env *env; +} t_parsing_args; + +// To call when a parse error occurs. +// Always returns 1 (parse error's status). +int parse_error(const char *msg); + +// Skip blank characters +void skip_blank(t_stream *stream); + +// Read until a character is in the charset or is '\0' +// and append the string to dest. +void read_until(t_parsing_args *args, const char **dest, + const char *stop_charset); + +// Read until a character isn't in the charset or is '\0' +// and append the string to dest. +void read_only(t_parsing_args *args, const char **dest, const char *charset); + +#endif diff --git a/src/variables.c b/src/variables.c new file mode 100644 index 0000000..31d2f70 --- /dev/null +++ b/src/variables.c @@ -0,0 +1,65 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* variables.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:47:40 by mcolonna #+# #+# */ +/* Updated: 2024/06/27 13:47:39 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "include.h" + +static void variables_free(t_list *variables, t_list_element *el) +{ + if (el->previous) + el->previous->next = el->next; + else + variables->first = el->next; + if (el->next) + el->next->previous = el->previous; + else + variables->last = el->previous; + variables->size--; + mem_free(el->value); + mem_free(el); +} + +// Return a pointer to the variable 'name'. +// (An undefined variable is considered of an empty value) +static t_variable *variables_find(t_list *variables, const char *name) +{ + t_list_element *el; + t_list_element *next; + t_variable *r; + + el = variables->first; + while (el) + { + next = el->next; + r = (t_variable *)el->value; + if (str_eq(r->name, name)) + return (r); + if (str_eq(r->value, "")) + variables_free(variables, el); + el = next; + } + r = mem_alloc(fatal_error, variables->mc, sizeof(t_variable)); + r->name = str_dup(fatal_error, variables->mc, name); + r->value = ""; + list_add(fatal_error, variables, r); + return (r); +} + +void variables_set(t_list *variables, const t_variable var) +{ + variables_find(variables, var.name)->value = ( + str_dup(fatal_error, variables->mc, var.value)); +} + +const char *variables_get(t_list *variables, const char *name) +{ + return (variables_find(variables, name)->value); +}