287 lines
7.6 KiB
C
287 lines
7.6 KiB
C
/* ************************************************************************** */
|
|
/* */
|
|
/* ::: :::::::: */
|
|
/* parse_command.c :+: :+: :+: */
|
|
/* +:+ +:+ +:+ */
|
|
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
|
/* +#+#+#+#+#+ +#+ */
|
|
/* 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);
|
|
}
|