norminette: split parse_command.c
This commit is contained in:
parent
f144aa5b6a
commit
6b2360ab6b
17 changed files with 622 additions and 411 deletions
|
|
@ -6,19 +6,12 @@
|
|||
/* By: jschaft <cecile.schaft@orange.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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)
|
||||
{
|
||||
|
|
|
|||
26
src/exec_command_utils.h
Normal file
26
src/exec_command_utils.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* exec_command_utils.h :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: jschaft <cecile.schaft@orange.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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
|
||||
|
|
@ -6,67 +6,14 @@
|
|||
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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.
|
||||
|
|
|
|||
153
src/parse_command_read_element.c
Normal file
153
src/parse_command_read_element.c
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* parse_command_read_element.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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);
|
||||
}
|
||||
23
src/parse_command_read_element.h
Normal file
23
src/parse_command_read_element.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* parse_command_read_element.h :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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
|
||||
146
src/parse_command_read_string.c
Normal file
146
src/parse_command_read_string.c
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* parse_command_read_string.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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);
|
||||
}
|
||||
27
src/parse_command_read_string.h
Normal file
27
src/parse_command_read_string.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* parse_command_read_string.h :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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
|
||||
56
src/parse_command_utils.c
Normal file
56
src/parse_command_utils.c
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* parse_command_utils.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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));
|
||||
}
|
||||
49
src/parse_command_utils.h
Normal file
49
src/parse_command_utils.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* parse_command_utils.h :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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
|
||||
65
src/variables.c
Normal file
65
src/variables.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* variables.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue