norminette: split parse_command.c
This commit is contained in:
parent
f144aa5b6a
commit
6b2360ab6b
17 changed files with 622 additions and 411 deletions
3
Makefile
3
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
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
/* By: jschaft <cecile.schaft@orange.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
/* By: jschaft <cecile.schaft@orange.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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
|
||||
{
|
||||
|
|
28
include/variables.h
Normal file
28
include/variables.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* variables.h :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: mcolonna <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* 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
|
BIN
minishell.srctrlbm
Normal file
BIN
minishell.srctrlbm
Normal file
Binary file not shown.
BIN
minishell.srctrldb
Normal file
BIN
minishell.srctrldb
Normal file
Binary file not shown.
36
minishell.srctrlprj
Normal file
36
minishell.srctrlprj
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<config>
|
||||
<source_groups>
|
||||
<source_group_9caac7da-7e15-4f68-926b-f7361e8a277d>
|
||||
<c_standard>gnu17</c_standard>
|
||||
<cross_compilation>
|
||||
<target>
|
||||
<abi>unknown</abi>
|
||||
<arch>x86_64</arch>
|
||||
<sys>unknown</sys>
|
||||
<vendor>unknown</vendor>
|
||||
</target>
|
||||
<target_options_enabled>0</target_options_enabled>
|
||||
</cross_compilation>
|
||||
<header_search_paths>
|
||||
<header_search_path>include</header_search_path>
|
||||
<header_search_path>libtf</header_search_path>
|
||||
<header_search_path>libft</header_search_path>
|
||||
</header_search_paths>
|
||||
<name>minishell</name>
|
||||
<pch_flags>
|
||||
<use_compiler_flags>1</use_compiler_flags>
|
||||
</pch_flags>
|
||||
<source_extensions>
|
||||
<source_extension>.c</source_extension>
|
||||
<source_extension>.h</source_extension>
|
||||
</source_extensions>
|
||||
<source_paths>
|
||||
<source_path>src/parse_command.c</source_path>
|
||||
</source_paths>
|
||||
<status>enabled</status>
|
||||
<type>C Source Group</type>
|
||||
</source_group_9caac7da-7e15-4f68-926b-f7361e8a277d>
|
||||
</source_groups>
|
||||
<version>8</version>
|
||||
</config>
|
|
@ -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
Reference in a new issue