/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* parse_command.c :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: mcolonna +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/04/24 13:47:40 by mcolonna #+# #+# */ /* Updated: 2024/05/07 13:59:30 by mcolonna ### ########.fr */ /* */ /* ************************************************************************** */ #include "include.h" // Ask the user for data and set readfd to the fd which will receive this // data. // Returns 'errno' on error. static int heredoc(t_memclass mc, int *readfd, const char *eof) { const t_memclass mc_in = mem_subclass(fatal_error, mc); int outpipe[2]; t_const_string line; const t_const_string eof_line = str_join(fatal_error, mc_in, eof, "\n"); if (pipe(outpipe) == -1) minishell_error("errno"); *readfd = outpipe[0]; while (true) { print_str(fatal_error, 1, "\e[38;5;33m( 'o')> \e[0m"); line = NULL; while (!line) line = read_line(fatal_error, mc_in, 0); if (str_eq(line, eof) || str_eq(line, eof_line)) break ; print_str(err_remember, outpipe[1], line); mem_free((void *)line); if (err_get()) return (minishell_error("errno"), errno); } mem_freeall(mc_in); if (close(outpipe[1]) == -1) return (minishell_error("errno"), errno); return (0); } // To call when a parse error occurs. // Always returns 1 (parse error's status). static int parse_error(const char *msg) { const t_memclass mc = mem_subclass(fatal_error, g_mc); minishell_error(str_join(fatal_error, mc, "parse error: ", msg)); mem_freeall(mc); return (1); } // Global variables for all the parsing functions typedef struct s_parsing_args { t_memclass mc; t_command r; t_stream stream; t_list calls; bool got_first_call; // got at least the first program call? const char *heredoc; } 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_err err, t_memclass mc, const char *str, char c) { char *s; char *r; s = str_dup(fatal_error, mc, "-"); s[0] = c; r = str_join(err, mc, str, s); mem_free(s); mem_free((void *)str); return (r); } // Read the string, stop if the char is in stop_charset // TODO variables if using " static const char *get_string(t_parsing_args *args, const char *stop_charset) { char quote; const t_memclass mc = mem_subclass(fatal_error, args->mc); const char *stop_charset_2; char *str; quote = '\0'; if (char_isin(stream_read(&args->stream), "\"'")) quote = stream_pop(&args->stream); if (!quote) stop_charset_2 = str_join(fatal_error, mc, stop_charset, " \n"); else stop_charset_2 = str_addchar(fatal_error, mc, str_dup(fatal_error, mc, ""), quote); str = str_dup(fatal_error, mc, ""); while (stream_read(&args->stream) && !char_isin(stream_read(&args->stream), stop_charset_2)) str = str_addchar(fatal_error, mc, str, stream_pop(&args->stream)); if (quote) if (!stream_pop(&args->stream)) return (NULL); str = str_dup(fatal_error, args->mc, str); mem_freeall(mc); return (str); } // Get a program call (program names & its arguments) until stop_charset. static int get_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 = get_string(args, stop_charset); 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); } static int get_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 = get_string(args, stop_charset); 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); } static int get_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 = get_string(args, stop_charset); 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); } static void get_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("'|', '>' or '<' expected"); *error = get_call(args, "<>|"); } else if (c == '>') *error = get_outputfile(args, "<>|"); else if (c == '<') *error = get_inputfile(args, "<>|"); else fatal_error_msg("internal error u.u"); } // Read an element (a call to a program, a '< FILE' or a '> FILE') // On success, return 0. On error, return the error status. static int get_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, "|><")) get_element2(args, &error, c); else if (!args->got_first_call) error = get_call(args, "<>|"); else return (parse_error("'|', '>' or '<'' expected")); skip_blank(&args->stream); } return (error); } t_parsing_args init_parsing_args(const t_memclass mc) { const t_parsing_args r = { .mc = mc, .r = { .error = 0, .empty = false, .input_fd = 0, .output_fd = 1, }, .calls = list_createempty(mc), .got_first_call = false, .heredoc = NULL, }; return (r); } t_command parse_command(const t_memclass mc, const char *command) { t_parsing_args args; int error; error = 0; args = init_parsing_args(mc); streamstr_init(&args.stream, command); skip_blank(&args.stream); if (!stream_read(&args.stream)) { args.r.empty = true; return (args.r); } while (!error && stream_read(&args.stream)) { error = get_element(&args); skip_blank(&args.stream); } if (!error) args.r.calls = (t_call *)list_convert_type( fatal_error, args.mc, &args.calls, sizeof(t_call)); if (error) args.r.error = error; if (args.heredoc) heredoc(mc, &args.r.input_fd, args.heredoc); return (args.r); }