bsh project
This commit is contained in:
commit
7744ceaa33
|
@ -0,0 +1,79 @@
|
|||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterEnum: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakInheritanceList: BeforeComma
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros: ['ILIST_FOREACH', 'ILIST_FOREACH_ENTRY']
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '<.*>'
|
||||
Priority: 1
|
||||
- Regex: '.*'
|
||||
Priority: 2
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
Language: Cpp
|
||||
NamespaceIndentation: All
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
|
@ -0,0 +1,8 @@
|
|||
.DS_Store
|
||||
*.o
|
||||
tmp/
|
||||
bsh
|
||||
doc/
|
||||
.vscode/
|
||||
builddir/
|
||||
vgcore.*
|
|
@ -0,0 +1,36 @@
|
|||
all: rebuild
|
||||
|
||||
bsh: build
|
||||
|
||||
.PHONY: build
|
||||
|
||||
check: build
|
||||
@echo "Checking..."
|
||||
@python3 tests/moulinette.py --binary bsh --tests tests/*.yml
|
||||
@echo "Done."
|
||||
|
||||
rebuild:
|
||||
@echo "Rebuilding project..."
|
||||
@rm -f bsh
|
||||
@ninja -C builddir
|
||||
@cp builddir/bsh .
|
||||
@echo "Done."
|
||||
|
||||
build: clean
|
||||
@echo "Building project..."
|
||||
@rm -f bsh
|
||||
@meson setup builddir
|
||||
@ninja -C builddir
|
||||
@cp builddir/bsh .
|
||||
@echo "Done."
|
||||
|
||||
doc:
|
||||
@echo "Generating documentation..."
|
||||
@rm -rf doc
|
||||
@doxygen
|
||||
@echo "Done."
|
||||
|
||||
clean:
|
||||
@echo "Cleaning up..."
|
||||
@rm -rf builddir bsh doc vgcore.*
|
||||
@echo "Done."
|
|
@ -0,0 +1,44 @@
|
|||
# Bsh
|
||||
|
||||
## Testsuite
|
||||
|
||||
First, you need to install the dependencies using pip:
|
||||
|
||||
```sh
|
||||
➜ bsh ✗ pip install -r tests/requirements.txt
|
||||
```
|
||||
|
||||
Make sure to have the bsh binary, and then execute the tests/moulinette.py file using python3
|
||||
|
||||
```sh
|
||||
➜ bsh ✗ python3 tests/moulinette.py --binary bsh --tests tests/echo.yml
|
||||
```
|
||||
- ```--binary``` is the relative path of the bsh binary
|
||||
- ```--tests``` is a list of all yaml files containing tests
|
||||
|
||||
A test is formatted like this in a yaml file:
|
||||
|
||||
```yml
|
||||
- name: a name
|
||||
input: an input in a single line (for example -> echo "Foo!")
|
||||
file: a path to a file, relative from the root of the git
|
||||
```
|
||||
|
||||
You can choose either input or file for a test. In the case you set a value for each of them, only input will be taken into account.
|
||||
|
||||
## Options
|
||||
|
||||
In order to debug the program, there are 2 options.
|
||||
|
||||
- ```--petty-print``` print the AST before each execution
|
||||
- ```--verbose``` print some functions output during execution. For now, there is only the lexer output.
|
||||
|
||||
:warning: **The order is important**: ```--petty-print``` has to be before ```--verbose```
|
||||
|
||||
## Environment
|
||||
|
||||
```sh
|
||||
➜ b-sh-bitarrays git:(main) env -i ./bsh
|
||||
```
|
||||
|
||||
You need to execute the shell with this command, in order to start with an empty environment
|
|
@ -0,0 +1,8 @@
|
|||
rule call_make
|
||||
command = make $out
|
||||
|
||||
build bsh: call_make
|
||||
|
||||
build clean: call_make
|
||||
|
||||
build check: call_make
|
|
@ -0,0 +1,77 @@
|
|||
project(
|
||||
'bsh',
|
||||
'c',
|
||||
version : '1.0',
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
'warning_level=2',
|
||||
'werror=true',
|
||||
],
|
||||
)
|
||||
|
||||
# add_project_arguments('-fsanitize=address', language : 'c')
|
||||
# add_project_link_arguments('-fsanitize=address', language : 'c')
|
||||
|
||||
cflags = ['-D_GNU_SOURCE', '-D_POSIX_C_SOURCE=200809L']
|
||||
add_project_arguments(cflags, language: 'c')
|
||||
|
||||
incdirs = [
|
||||
include_directories('./src/lexer'),
|
||||
include_directories('./src/parser'),
|
||||
include_directories('./src/shell_input'),
|
||||
include_directories('./src/ast_evaluation'),
|
||||
include_directories('./src/builtins'),
|
||||
include_directories('./src/variables'),
|
||||
include_directories('./src/loops'),
|
||||
include_directories('./src/functions'),
|
||||
include_directories('./src/'),
|
||||
]
|
||||
|
||||
common = [
|
||||
'src/lexer/lexer.c',
|
||||
'src/lexer/lexer_print.c',
|
||||
'src/lexer/ionumbers.c',
|
||||
'src/lexer/create_token.c',
|
||||
'src/lexer/keywords.c',
|
||||
'src/lexer/split_input.c',
|
||||
'src/lexer/spaces.c',
|
||||
'src/lexer/export.c',
|
||||
'src/lexer/alias.c',
|
||||
'src/parser/parser.c',
|
||||
'src/parser/parser_high_level.c',
|
||||
'src/parser/parser_conditions.c',
|
||||
'src/parser/parser_commands.c',
|
||||
'src/parser/parser_loops.c',
|
||||
'src/parser/ast.c',
|
||||
'src/ast_evaluation/pretty_print.c',
|
||||
'src/ast_evaluation/ast_evaluation.c',
|
||||
'src/ast_evaluation/builtin_exec.c',
|
||||
'src/ast_evaluation/inner_exec.c',
|
||||
'src/ast_evaluation/prog_exec.c',
|
||||
'src/ast_evaluation/pipe.c',
|
||||
'src/ast_evaluation/redir.c',
|
||||
'src/ast_evaluation/redir_tools.c',
|
||||
'src/shell_input/shell_input.c',
|
||||
'src/builtins/builtins.c',
|
||||
'src/variables/var_list.c',
|
||||
'src/builtins/echo.c',
|
||||
'src/builtins/cd.c',
|
||||
'src/builtins/continue.c',
|
||||
'src/builtins/break.c',
|
||||
'src/builtins/dot.c',
|
||||
'src/parser/parse_functions.c',
|
||||
'src/loops/loop_stack.c',
|
||||
'src/builtins/exit.c',
|
||||
'src/builtins/export.c',
|
||||
'src/functions/functions.c',
|
||||
'src/builtins/unset.c',
|
||||
]
|
||||
|
||||
token_printer = executable(
|
||||
# name of the output executable
|
||||
'bsh',
|
||||
# source files for the executable
|
||||
common + [ 'src/bsh.c' ],
|
||||
# these are passed as -I
|
||||
include_directories: incdirs
|
||||
)
|
|
@ -0,0 +1,467 @@
|
|||
#include "ast_evaluation.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "builtins.h"
|
||||
#include "parser.h"
|
||||
#include "redir.h"
|
||||
|
||||
int evaluate_ast(struct ast *ast)
|
||||
{
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
return shell->return_code;
|
||||
if (!ast)
|
||||
return 0;
|
||||
|
||||
if (ast->type == AST_IF)
|
||||
{
|
||||
if (!evaluate_ast(ast->condition))
|
||||
{
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
return shell->return_code;
|
||||
return evaluate_ast(ast->left_child);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
return shell->return_code;
|
||||
return evaluate_ast(ast->right_child);
|
||||
}
|
||||
}
|
||||
if (ast->type == AST_CASE)
|
||||
{
|
||||
char **val = expand(ast->value, ast->enclosure);
|
||||
char *arg = merge_arg(val);
|
||||
int i = 0;
|
||||
while (val[i])
|
||||
free(val[i++]);
|
||||
free(val);
|
||||
ast = ast->left_child;
|
||||
int res = 0;
|
||||
int found = 0;
|
||||
while (ast && ast->type == AST_CASE_SWITCH && !found)
|
||||
{
|
||||
int j = 0;
|
||||
while (ast->value[j])
|
||||
{
|
||||
char *tmp = ast->value[j];
|
||||
if (!strcmp(tmp, arg) || !strcmp(tmp, "*"))
|
||||
{
|
||||
res = evaluate_ast(ast->left_child);
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
{
|
||||
free(arg);
|
||||
return shell->return_code;
|
||||
}
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
ast = ast->right_child;
|
||||
}
|
||||
free(arg);
|
||||
return res;
|
||||
}
|
||||
else if (ast->type == AST_FOR)
|
||||
{
|
||||
char **var;
|
||||
push_loop(shell, ast);
|
||||
if ((!ast->value[1] || !ast->value[2]))
|
||||
{
|
||||
if (shell->nb_args > 0)
|
||||
{
|
||||
enum quotes *enclosure =
|
||||
calloc(shell->nb_args, sizeof(enum quotes));
|
||||
char **tmp = calloc(2, sizeof(char *));
|
||||
tmp[0] = find_elt_list(shell, "@");
|
||||
var = split_arg(tmp, enclosure);
|
||||
free(enclosure);
|
||||
free(tmp);
|
||||
}
|
||||
else
|
||||
var = NULL;
|
||||
// wait impletation of env var
|
||||
// enum quotes enclosure[1] = {Q_NONE};
|
||||
// var = split_arg(shell->args, enclosure); // add var in array
|
||||
}
|
||||
else
|
||||
var = split_arg(
|
||||
ast->value + 2 /*expand(ast->value + 2, ast->enclosure + 2)*/,
|
||||
ast->enclosure + 2);
|
||||
if (var && ast->value)
|
||||
{
|
||||
int i = 0;
|
||||
while (var[i])
|
||||
{
|
||||
int res = push_elt_list(shell, ast->value[0], var[i++]);
|
||||
evaluate_ast(ast->left_child);
|
||||
if (shell->ctn)
|
||||
{
|
||||
shell->ctn--;
|
||||
if (shell->ctn > 0 && get_ast_loop(shell))
|
||||
{
|
||||
free_arg(var);
|
||||
pop_loop(shell);
|
||||
return res;
|
||||
}
|
||||
else if (shell->ctn)
|
||||
shell->ctn = 0;
|
||||
continue;
|
||||
}
|
||||
else if (shell->brk)
|
||||
{
|
||||
shell->brk--;
|
||||
pop_loop(shell);
|
||||
if (shell->brk > 0 && get_ast_loop(shell))
|
||||
{
|
||||
free_arg(var);
|
||||
return res;
|
||||
}
|
||||
else if (shell->brk)
|
||||
shell->brk = 0;
|
||||
break;
|
||||
}
|
||||
if (shell->exit)
|
||||
{
|
||||
free_arg(var);
|
||||
return shell->return_code;
|
||||
}
|
||||
}
|
||||
}
|
||||
free_arg(var);
|
||||
if (get_ast_loop(shell) == ast)
|
||||
pop_loop(shell);
|
||||
int res =
|
||||
evaluate_ast(ast->right_child); // check return code for a null for
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
return shell->return_code;
|
||||
return res;
|
||||
}
|
||||
else if (ast->type == AST_WHILE)
|
||||
{
|
||||
int ret = 0;
|
||||
push_loop(shell, ast);
|
||||
while (!evaluate_ast(ast->condition))
|
||||
{
|
||||
if (shell->exit)
|
||||
return shell->return_code;
|
||||
ret = evaluate_ast(ast->left_child);
|
||||
if (shell->ctn)
|
||||
{
|
||||
shell->ctn--;
|
||||
if (shell->ctn > 0 && get_ast_loop(shell))
|
||||
{
|
||||
pop_loop(shell);
|
||||
return ret;
|
||||
}
|
||||
else if (shell->ctn)
|
||||
shell->ctn = 0;
|
||||
continue;
|
||||
}
|
||||
else if (shell->brk)
|
||||
{
|
||||
shell->brk--;
|
||||
pop_loop(shell);
|
||||
if (shell->brk > 0 && get_ast_loop(shell))
|
||||
return ret;
|
||||
else if (shell->brk)
|
||||
shell->brk = 0;
|
||||
break;
|
||||
}
|
||||
if (shell->exit)
|
||||
return shell->return_code;
|
||||
}
|
||||
if (get_ast_loop(shell) == ast)
|
||||
pop_loop(shell);
|
||||
return ret;
|
||||
}
|
||||
else if (ast->type == AST_UNTIL)
|
||||
{
|
||||
int ret = 0;
|
||||
push_loop(shell, ast);
|
||||
while (evaluate_ast(ast->condition))
|
||||
{
|
||||
if (shell->exit)
|
||||
return shell->return_code;
|
||||
ret = evaluate_ast(ast->left_child);
|
||||
if (shell->ctn)
|
||||
{
|
||||
shell->ctn--;
|
||||
if (shell->ctn > 0 && get_ast_loop(shell))
|
||||
{
|
||||
pop_loop(shell);
|
||||
return ret;
|
||||
}
|
||||
else if (shell->ctn)
|
||||
shell->ctn = 0;
|
||||
continue;
|
||||
}
|
||||
else if (shell->brk)
|
||||
{
|
||||
shell->brk--;
|
||||
pop_loop(shell);
|
||||
if (shell->brk > 0 && get_ast_loop(shell))
|
||||
return ret;
|
||||
else if (shell->brk)
|
||||
shell->brk = 0;
|
||||
break;
|
||||
}
|
||||
if (shell->exit)
|
||||
return shell->return_code;
|
||||
}
|
||||
if (get_ast_loop(shell) == ast)
|
||||
pop_loop(shell);
|
||||
return ret;
|
||||
}
|
||||
else if (ast->type == AST_AND || ast->type == AST_OR)
|
||||
{
|
||||
int prec = !evaluate_ast(ast->left_child);
|
||||
while (ast->right_child
|
||||
&& (ast->right_child->type == AST_OR
|
||||
|| ast->right_child->type == AST_AND))
|
||||
{
|
||||
if (ast->type == AST_AND)
|
||||
prec = prec && !evaluate_ast(ast->right_child->left_child);
|
||||
else if (ast->type == AST_OR)
|
||||
prec = prec || !evaluate_ast(ast->right_child->left_child);
|
||||
/*if (shell->continue)
|
||||
{
|
||||
|
||||
}
|
||||
else if (shell->break)
|
||||
{
|
||||
|
||||
}*/
|
||||
if (shell->exit)
|
||||
return shell->return_code;
|
||||
ast = ast->right_child;
|
||||
}
|
||||
if (ast->type == AST_AND)
|
||||
prec = prec && !evaluate_ast(ast->right_child);
|
||||
else if (ast->type == AST_OR)
|
||||
prec = prec || !evaluate_ast(ast->right_child);
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
return shell->return_code;
|
||||
return !prec;
|
||||
}
|
||||
else if (ast->type == AST_REDIR)
|
||||
{
|
||||
int nb = 1;
|
||||
struct ast *tmp = ast;
|
||||
while (tmp->right_child->type == AST_REDIR)
|
||||
{
|
||||
tmp = tmp->right_child;
|
||||
nb++;
|
||||
}
|
||||
char ***redirs = calloc(nb + 1, sizeof(char **));
|
||||
if (!redirs)
|
||||
return 1;
|
||||
int i = 0;
|
||||
tmp = ast;
|
||||
while (tmp->right_child->type)
|
||||
{
|
||||
redirs[i] = calloc(4, sizeof(char *));
|
||||
if (!redirs[i])
|
||||
return 1; // error a recheck
|
||||
int j = 0;
|
||||
while (tmp->value[j])
|
||||
{
|
||||
redirs[i][j] = tmp->value[j];
|
||||
j++;
|
||||
}
|
||||
tmp = tmp->right_child;
|
||||
redirs[i++][j] = tmp->left_child->value[0];
|
||||
}
|
||||
redirs[i] = calloc(4, sizeof(char *));
|
||||
if (!redirs[i])
|
||||
return 1; // error a recheck
|
||||
int j = 0;
|
||||
while (tmp->value[j])
|
||||
{
|
||||
redirs[i][j] = tmp->value[j];
|
||||
j++;
|
||||
}
|
||||
redirs[i][j] = tmp->right_child->value[0];
|
||||
// call redir
|
||||
exec_redirections(redirs);
|
||||
int res = evaluate_ast(ast->left_child);
|
||||
int k = 0;
|
||||
while (redirs[k])
|
||||
free(redirs[k++]);
|
||||
free(redirs);
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
return shell->return_code;
|
||||
return res;
|
||||
// int fd = atoi_begining(char *s);
|
||||
/*if (shell->verbose)
|
||||
{
|
||||
printf("%d\n", nb);
|
||||
printf("%s\n", ast->left_child->value[0]);
|
||||
}*/
|
||||
}
|
||||
else if (ast->type == AST_PIPE)
|
||||
{
|
||||
int nb = 1;
|
||||
struct ast *tmp = ast;
|
||||
while (tmp->right_child->type == AST_PIPE)
|
||||
{
|
||||
tmp = tmp->right_child;
|
||||
nb++;
|
||||
}
|
||||
char ***redirs = calloc(nb + 2, sizeof(char **));
|
||||
if (!redirs)
|
||||
return 1;
|
||||
enum quotes **enclosure = calloc(nb + 2, sizeof(enum quotes *));
|
||||
if (!redirs)
|
||||
return 1;
|
||||
redirs[0] = ast->left_child->value;
|
||||
enclosure[0] = ast->left_child->enclosure;
|
||||
int i = 1;
|
||||
while (ast->right_child->type == AST_PIPE)
|
||||
{
|
||||
enclosure[i] = ast->left_child->enclosure;
|
||||
redirs[i++] = ast->left_child->value;
|
||||
ast = ast->right_child;
|
||||
}
|
||||
/* int y = 0;
|
||||
while (ast->left_child->value[y])
|
||||
{
|
||||
printf("1\n");
|
||||
printf("%s\n", ast->right_child->value[y]);
|
||||
printf("%d\n", ast->right_child->enclosure[y++]);
|
||||
}*/
|
||||
redirs[i] = ast->right_child->value;
|
||||
enclosure[i] = ast->right_child->enclosure;
|
||||
int res = exec_pipe(redirs, enclosure, nb);
|
||||
push_int_elt_list(shell, "?", res);
|
||||
return res;
|
||||
}
|
||||
else if (ast->type == AST_ASSIGNMENT)
|
||||
{
|
||||
if (ast->value)
|
||||
{
|
||||
char **var = expand(ast->value, ast->enclosure);
|
||||
char *val = merge_arg(var);
|
||||
push_elt_list(shell, ast->var_name, val);
|
||||
free_arg(var);
|
||||
free(val);
|
||||
}
|
||||
}
|
||||
else if (ast->type == AST_FUNC)
|
||||
{
|
||||
if (ast->var_name)
|
||||
push_elt_fun(shell, ast->var_name, ast->left_child);
|
||||
else
|
||||
evaluate_ast(ast->left_child);
|
||||
/*new_var(shell, ast->value);
|
||||
int res = ast_evaluation(ast->left_child);
|
||||
del_stack(shell);
|
||||
push_int_elt_list(shell, "?", res);
|
||||
res = ast_evaluation(ast->right_child);
|
||||
return res;*/
|
||||
}
|
||||
else if (ast->type == AST_SUBSHELL)
|
||||
{
|
||||
struct var *cpy = var_list_cpy(shell);
|
||||
struct functions *fn_cpy = fun_list_cpy(shell);
|
||||
int res = evaluate_ast(ast->left_child);
|
||||
free_list_sub(shell->var_list);
|
||||
free_fun_sub(shell);
|
||||
shell->ctn = 0;
|
||||
shell->brk = 0;
|
||||
shell->exit = 0;
|
||||
shell->var_list = cpy;
|
||||
shell->functions = fn_cpy;
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
return shell->return_code;
|
||||
return res;
|
||||
}
|
||||
else if (ast->type == AST_CMD_SUBSTITUTION)
|
||||
{
|
||||
struct var *cpy = var_list_cpy(shell);
|
||||
struct functions *fn_cpy = fun_list_cpy(shell);
|
||||
int save = dup(STDOUT_FILENO);
|
||||
char *path = get_next_free_file();
|
||||
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
close(fd);
|
||||
int res = evaluate_ast(ast->left_child);
|
||||
dup2(save, STDOUT_FILENO);
|
||||
char *cmd_val = get_file_in_var(path);
|
||||
free(path);
|
||||
free_list_sub(shell->var_list);
|
||||
free_fun_sub(shell);
|
||||
shell->ctn = 0;
|
||||
shell->brk = 0;
|
||||
shell->exit = 0;
|
||||
shell->var_list = cpy;
|
||||
shell->functions = fn_cpy;
|
||||
/*struct ast *cmd = ast_new(AST_COMMAND);
|
||||
cmd->value = calloc(2, sizeof(char *));
|
||||
cmd->enclosure = calloc(2, sizeof(enum quotes));
|
||||
cmd->value[0] = cmd_val;
|
||||
cmd->enclosure[0] = Q_NONE;*/
|
||||
parse_input(cmd_val, NULL);
|
||||
res = shell->return_code;
|
||||
free(cmd_val);
|
||||
push_int_elt_list(shell, "?", res);
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
return shell->return_code;
|
||||
return res;
|
||||
}
|
||||
else if (ast->type == AST_COMMAND)
|
||||
{
|
||||
char **val = expand(ast->value, ast->enclosure);
|
||||
// val = split_arg(val, ast->enclosure);
|
||||
if (!val)
|
||||
return 1;
|
||||
int res;
|
||||
struct ast *block;
|
||||
if (is_builtin(*(val)))
|
||||
res = find_command(val, 1);
|
||||
else if ((block = find_elt_fun(shell, *(ast->value))) != NULL)
|
||||
{
|
||||
new_var(shell, ast->value);
|
||||
res = evaluate_ast(block);
|
||||
del_stack(shell);
|
||||
shell->ctn = 0;
|
||||
shell->brk = 0;
|
||||
shell->exit = 0;
|
||||
}
|
||||
else
|
||||
res = call_exec(val);
|
||||
char *tmp = val[0];
|
||||
int pos = 0;
|
||||
while (tmp)
|
||||
{
|
||||
free(tmp);
|
||||
tmp = val[++pos];
|
||||
}
|
||||
free(val);
|
||||
push_int_elt_list(shell, "?", res);
|
||||
return res;
|
||||
}
|
||||
else if (ast->type == AST_NOT)
|
||||
return !evaluate_ast(ast->left_child);
|
||||
else if (ast->type == AST_LIST)
|
||||
{
|
||||
int r = evaluate_ast(ast->right_child);
|
||||
// printf("%d\n", shell->exit);
|
||||
if (shell->exit || shell->ctn || shell->brk)
|
||||
{
|
||||
// printf("%d\n", shell->return_code);
|
||||
return shell->return_code;
|
||||
}
|
||||
if (ast->left_child && ast->left_child->type != AST_EOF)
|
||||
return evaluate_ast(ast->left_child);
|
||||
return r;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%d, Not implemented yet\n", ast->type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef AST_EVALUATION_H
|
||||
#define AST_EVALUATION_H
|
||||
|
||||
#include "../bsh.h"
|
||||
#include "../parser/ast.h" // refaire plus propre
|
||||
#include "ast_evaluation_tools.h"
|
||||
#include "functions.h"
|
||||
#include "loop_stack.h"
|
||||
#include "var_list.h"
|
||||
|
||||
int evaluate_ast(struct ast *ast);
|
||||
|
||||
#endif /* !AST_EVALUATION_H */
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef AST_EVALUATION_TOOLS_H
|
||||
#define AST_EVALUATION_TOOLS_H
|
||||
|
||||
#include "bsh.h"
|
||||
#include "ast.h"
|
||||
#include "var_list.h"
|
||||
|
||||
// include builtin
|
||||
extern struct shell *shell;
|
||||
// int call_builtin(char *cmd);
|
||||
int is_builtin(char *);
|
||||
int call_exec(char **cmd);
|
||||
int is_in(char **condition);
|
||||
char **expand(char **arg, enum quotes *enclosure);
|
||||
char **split_arg(char **arg, enum quotes *enclosure);
|
||||
char *merge_arg(char **arg);
|
||||
int atoi_begining(char *s);
|
||||
int exec_pipe(char ***args, enum quotes **enclosure, int pipe_nb);
|
||||
void free_arg(char **var);
|
||||
char *get_next_free_file(void);
|
||||
char *get_file_in_var(char *path);
|
||||
|
||||
#endif /* !AST_EVALUATION_TOOLS_H */
|
|
@ -0,0 +1,65 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "ast_evaluation_tools.h"
|
||||
#include "builtins.h"
|
||||
|
||||
/*
|
||||
* The main objectif of this source code is to call the builtin implemented
|
||||
* inside of the bsh project
|
||||
*/
|
||||
|
||||
int is_builtin(char *cmd)
|
||||
{
|
||||
if (!strcmp(cmd, "echo"))
|
||||
return 1;
|
||||
if (!strcmp(cmd, "cd"))
|
||||
return 1;
|
||||
if (!strcmp(cmd, "break"))
|
||||
return 1;
|
||||
if (!strcmp(cmd, "continue"))
|
||||
return 1;
|
||||
if (!strcmp(cmd, "exit"))
|
||||
return 1;
|
||||
if (!strcmp(cmd, "export"))
|
||||
return 1;
|
||||
if (!strcmp(cmd, "unset"))
|
||||
return 1;
|
||||
if (!strcmp(cmd, "."))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*int call_echo(char *cmd)
|
||||
{
|
||||
int i = 0;
|
||||
// int flag = 0;
|
||||
while (cmd[i] != '\0')
|
||||
{
|
||||
while (!(cmd[i] > ' '))
|
||||
i++;
|
||||
if (!strcmp(cmd, "-e", i))
|
||||
if (i != 1 && i != 3)
|
||||
flag++;
|
||||
if (!strcmp(cmd, "-n", i))
|
||||
if (i < 2)
|
||||
flag += 2;
|
||||
if (!strcmp(cmd, "-ne", i))
|
||||
flag = 3;
|
||||
}
|
||||
|
||||
printf("%s\n", cmd);
|
||||
echo(cmd + i, 1);
|
||||
return 0;
|
||||
}*/
|
||||
|
||||
/*int call_builtin(char **cmd)
|
||||
{
|
||||
int i = 0;
|
||||
while (cmd[i] != ' ')
|
||||
i++;
|
||||
if (!strncmp(cmd, "echo", i))
|
||||
{
|
||||
return call_echo(cmd + i);
|
||||
}
|
||||
return 0;
|
||||
}*/
|
|
@ -0,0 +1,374 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast_evaluation_tools.h"
|
||||
|
||||
char *get_next_free_file(void)
|
||||
{
|
||||
int i = 0;
|
||||
char *file_name = calloc(25, sizeof(char));
|
||||
sprintf(file_name, "/tmp/bsh_%d", i);
|
||||
while (access(file_name, F_OK) == 0)
|
||||
{
|
||||
i++;
|
||||
free(file_name);
|
||||
file_name = calloc(25, sizeof(char));
|
||||
sprintf(file_name, "/tmp/bsh_%d", i);
|
||||
}
|
||||
return file_name;
|
||||
}
|
||||
|
||||
char *get_file_in_var(char *path)
|
||||
{
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file)
|
||||
return NULL;
|
||||
fseek(file, 0, SEEK_END);
|
||||
long size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
char *content = malloc(sizeof(char) * (size + 1));
|
||||
fread(content, sizeof(char), size, file);
|
||||
content[size] = '\0';
|
||||
fclose(file);
|
||||
return content;
|
||||
}
|
||||
|
||||
void free_arg(char **var)
|
||||
{
|
||||
if (!var)
|
||||
return;
|
||||
int i = 0;
|
||||
while (var[i])
|
||||
free(var[i++]);
|
||||
free(var);
|
||||
}
|
||||
|
||||
int is_in(char **condition)
|
||||
{
|
||||
while ((*condition)[0] != '\0')
|
||||
{
|
||||
if (!strcmp(*condition, "in"))
|
||||
return 1;
|
||||
condition++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int is_char_name(char c)
|
||||
{
|
||||
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
|
||||
|| (c >= 'a' && c <= 'z') || c == '_';
|
||||
}
|
||||
|
||||
int is_special(char c)
|
||||
{
|
||||
return c == '#' || c == '?' || c == '*' || c == '@' || c == '$' || c == '!';
|
||||
}
|
||||
|
||||
int expand_s(char **elt, char *s, enum quotes type)
|
||||
{
|
||||
int size = strlen(s);
|
||||
char *new = calloc(size + 1, sizeof(char));
|
||||
if (!new)
|
||||
return 0;
|
||||
strcpy(new, s);
|
||||
if (type == Q_NONE)
|
||||
{
|
||||
int i = 0;
|
||||
int i_new = 0;
|
||||
while (s[i] != '\0')
|
||||
{
|
||||
if (s[i] == '$')
|
||||
{
|
||||
int begin = i;
|
||||
int bracket = s[++i] == '{';
|
||||
int offset = 1;
|
||||
if (bracket)
|
||||
{
|
||||
offset++;
|
||||
i++;
|
||||
}
|
||||
int start = i;
|
||||
int size_var = 0;
|
||||
if (is_special(s[i]))
|
||||
{
|
||||
i++;
|
||||
size_var++;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (is_char_name(s[i])/*s[i] != '\0' && s[i] != ' ' && s[i] != '\t'
|
||||
&& s[i] != '$' && s[i] != '\\'
|
||||
&& (!bracket || (bracket && s[i] != '}'))*/)
|
||||
{
|
||||
i++;
|
||||
size_var++;
|
||||
}
|
||||
}
|
||||
if (bracket)
|
||||
i++;
|
||||
if (i - begin != 1)
|
||||
{
|
||||
char *name = calloc(size_var + 1, sizeof(char));
|
||||
strncpy(name, s + start, size_var);
|
||||
name[size_var] = '\0';
|
||||
char *var = find_elt_list(shell, name);
|
||||
if (!var)
|
||||
var = "";
|
||||
int new_size = strlen(var);
|
||||
size += begin - i + new_size;
|
||||
char *tmp = realloc(new, (size + 1) * sizeof(char));
|
||||
if (!tmp)
|
||||
return 0;
|
||||
new = tmp;
|
||||
strcpy(new + i_new, var);
|
||||
i_new += new_size;
|
||||
free(name);
|
||||
if (s[i] == '\0')
|
||||
break;
|
||||
if (s[i] == '$')
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
new[i_new++] = s[i++];
|
||||
}
|
||||
}
|
||||
else if (s[i] == '\\')
|
||||
{
|
||||
i++;
|
||||
new[i_new++] = s[i++];
|
||||
}
|
||||
else
|
||||
new[i_new++] = s[i++];
|
||||
}
|
||||
new[i_new] = '\0';
|
||||
}
|
||||
else if (type == Q_DOUBLE)
|
||||
{
|
||||
int i = 0;
|
||||
int i_new = 0;
|
||||
while (s[i] != '\0')
|
||||
{
|
||||
if (s[i] == '$')
|
||||
{
|
||||
int begin = i;
|
||||
int bracket = s[++i] == '{';
|
||||
int offset = 1;
|
||||
if (bracket)
|
||||
{
|
||||
offset++;
|
||||
i++;
|
||||
}
|
||||
int start = i;
|
||||
int size_var = 0;
|
||||
if (is_special(s[i]))
|
||||
{
|
||||
i++;
|
||||
size_var++;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (is_char_name(s[i])/*s[i] != '\0' && s[i] != ' ' && s[i] != '\t'
|
||||
&& s[i] != '$' && s[i] != '\\'
|
||||
&& (!bracket || (bracket && s[i] != '}'))*/)
|
||||
{
|
||||
i++;
|
||||
size_var++;
|
||||
}
|
||||
}
|
||||
if (bracket)
|
||||
i++;
|
||||
if (i - begin != 1)
|
||||
{
|
||||
char *name = calloc(size_var + 1, sizeof(char));
|
||||
strncpy(name, s + start, size_var);
|
||||
name[size_var] = '\0';
|
||||
char *var = find_elt_list(shell, name);
|
||||
if (!var)
|
||||
var = "";
|
||||
int new_size = strlen(var);
|
||||
size += begin - i + new_size;
|
||||
char *tmp = realloc(new, (size + 1) * sizeof(char));
|
||||
if (!tmp)
|
||||
return 0;
|
||||
new = tmp;
|
||||
strcpy(new + i_new, var);
|
||||
i_new += new_size;
|
||||
free(name);
|
||||
if (s[i] == '\0')
|
||||
break;
|
||||
if (s[i] == '$')
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
new[i_new++] = s[i++];
|
||||
}
|
||||
}
|
||||
else if (s[i] == '\\')
|
||||
{
|
||||
i++;
|
||||
if (s[i] == 'n')
|
||||
{
|
||||
i++;
|
||||
new[i_new++] = '\n';
|
||||
}
|
||||
else if (s[i] == '\"')
|
||||
{
|
||||
i++;
|
||||
new[i_new++] = '\"';
|
||||
}
|
||||
else if (s[i] == '\'')
|
||||
{
|
||||
i++;
|
||||
new[i_new++] = '\'';
|
||||
}
|
||||
new[i_new++] = s[i];
|
||||
if (s[i++] == '\0')
|
||||
break;
|
||||
}
|
||||
else
|
||||
new[i_new++] = s[i++];
|
||||
}
|
||||
new[i_new] = '\0';
|
||||
}
|
||||
*elt = new;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int array_len(char **arr)
|
||||
{
|
||||
int i = 0;
|
||||
while (arr[i] != NULL)
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
char **expand(char **arg, enum quotes *enclosure)
|
||||
{
|
||||
char **new = calloc(array_len(arg) + 1, sizeof(char *));
|
||||
if (!new)
|
||||
return NULL;
|
||||
int ret_val = 1;
|
||||
int i = 0;
|
||||
while (arg[i] != NULL && ret_val)
|
||||
{
|
||||
ret_val = expand_s(new + i, arg[i], enclosure[i]);
|
||||
i++;
|
||||
}
|
||||
new[i] = NULL;
|
||||
if (!ret_val)
|
||||
return NULL;
|
||||
return new;
|
||||
}
|
||||
|
||||
int str_in(char *s, char c)
|
||||
{
|
||||
int i = 0;
|
||||
while (s[i] != '\0')
|
||||
{
|
||||
if (s[i++] == c)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *merge_arg(char **arg)
|
||||
{
|
||||
if (!arg)
|
||||
return NULL;
|
||||
char *s = calloc(1, sizeof(char));
|
||||
s[0] = '\0';
|
||||
int size = 0;
|
||||
int i = 0;
|
||||
while (arg[i])
|
||||
{
|
||||
size += strlen(arg[i]);
|
||||
char *tmp = realloc(s, (size + 1) * sizeof(char));
|
||||
if (!tmp)
|
||||
{
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
s = tmp;
|
||||
strcat(s, arg[i]);
|
||||
i++;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
char **split_arg(char **arg, enum quotes *enclosure)
|
||||
{
|
||||
if (!arg)
|
||||
return NULL;
|
||||
int size = array_len(arg) + 1;
|
||||
char **new = calloc(size, sizeof(char *));
|
||||
if (!new)
|
||||
return NULL;
|
||||
int ret_val = 1;
|
||||
int i = 0;
|
||||
int i_new = 0;
|
||||
while (arg[i] != NULL && ret_val)
|
||||
{
|
||||
ret_val = expand_s(new + i_new, arg[i], enclosure[i]);
|
||||
if (enclosure[i] == Q_NONE)
|
||||
{
|
||||
char *s = *(new + i_new);
|
||||
int j = 0;
|
||||
int start = 0;
|
||||
while (s[j] != '\0')
|
||||
{
|
||||
start = 0;
|
||||
if (str_in(shell->ifs, s[j]))
|
||||
{
|
||||
start = 1;
|
||||
size++;
|
||||
char **tmp = realloc(new, size * sizeof(char *));
|
||||
if (!tmp)
|
||||
return NULL;
|
||||
new = tmp;
|
||||
new[i_new + 1] =
|
||||
calloc(strlen(new[i_new] + j + 1) + 1, sizeof(char));
|
||||
strcpy(new[i_new + 1], new[i_new] + j + 1);
|
||||
new[i_new][j] = '\0';
|
||||
s = new[++i_new];
|
||||
j = 0;
|
||||
}
|
||||
else
|
||||
j++;
|
||||
}
|
||||
if (start == 0)
|
||||
i_new++;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
i_new++;
|
||||
}
|
||||
}
|
||||
new[i_new] = NULL;
|
||||
if (!ret_val)
|
||||
return NULL;
|
||||
return new;
|
||||
}
|
||||
|
||||
int atoi_begining(char *s)
|
||||
{
|
||||
int i = 0;
|
||||
int nb = 0;
|
||||
while (s[i] != '\0')
|
||||
{
|
||||
if (s[i] < '0' || s[i] > '9')
|
||||
break;
|
||||
nb = nb * 10 + (s[i++] - '0');
|
||||
}
|
||||
if (nb == 0 && i == 0)
|
||||
return -1;
|
||||
return nb;
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast_evaluation_tools.h"
|
||||
#include "builtins.h"
|
||||
|
||||
int exec_with_fork(char **cmd, int i, int pipe_nb, int ***fds)
|
||||
{
|
||||
int pid = fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
fprintf(stderr, "bsh: fork error\n");
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
dup2((*fds)[i][1], 1);
|
||||
close((*fds)[i][0]);
|
||||
close((*fds)[i][1]);
|
||||
if (is_builtin(cmd[0]))
|
||||
{
|
||||
find_command(cmd, 1);
|
||||
kill(getpid(), SIGKILL);
|
||||
return shell->return_code;
|
||||
}
|
||||
else
|
||||
execvp(cmd[0], cmd);
|
||||
fprintf(stderr, "bsh: command not found: %s\n", cmd[0]);
|
||||
kill(getpid(), SIGKILL);
|
||||
return 127;
|
||||
}
|
||||
else if (i == pipe_nb)
|
||||
{
|
||||
dup2((*fds)[i - 1][0], 0);
|
||||
close((*fds)[i - 1][0]);
|
||||
close((*fds)[i - 1][1]);
|
||||
if (is_builtin(cmd[0]))
|
||||
{
|
||||
find_command(cmd, 1);
|
||||
kill(getpid(), SIGKILL);
|
||||
return shell->return_code;
|
||||
}
|
||||
else
|
||||
execvp(cmd[0], cmd);
|
||||
fprintf(stderr, "bsh: command not found: %s\n", cmd[0]);
|
||||
kill(getpid(), SIGKILL);
|
||||
return 127;
|
||||
}
|
||||
else
|
||||
{
|
||||
dup2((*fds)[i - 1][0], 0);
|
||||
dup2((*fds)[i][1], 1);
|
||||
close((*fds)[i - 1][0]);
|
||||
close((*fds)[i - 1][1]);
|
||||
close((*fds)[i][0]);
|
||||
close((*fds)[i][1]);
|
||||
if (is_builtin(cmd[0]))
|
||||
{
|
||||
find_command(cmd, 1);
|
||||
kill(getpid(), SIGKILL);
|
||||
return shell->return_code;
|
||||
}
|
||||
else
|
||||
execvp(cmd[0], cmd);
|
||||
fprintf(stderr, "bsh: command not found: %s\n", cmd[0]);
|
||||
kill(getpid(), SIGKILL);
|
||||
return 127;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
close((*fds)[i - 1][0]);
|
||||
close((*fds)[i - 1][1]);
|
||||
|
||||
int wstatus;
|
||||
if (waitpid(pid, &wstatus, 0) == -1)
|
||||
shell->return_code = 1;
|
||||
|
||||
if (!WIFEXITED(wstatus))
|
||||
shell->return_code = 127;
|
||||
else
|
||||
shell->return_code = WEXITSTATUS(wstatus);
|
||||
}
|
||||
return shell->return_code;
|
||||
}
|
||||
return shell->return_code;
|
||||
}
|
||||
|
||||
int exec_pipe(char ***args, enum quotes **enclosure, int pipe_nb)
|
||||
{
|
||||
int res = 0;
|
||||
int **fds = calloc(pipe_nb, sizeof(int *));
|
||||
|
||||
for (int i = 0; i < pipe_nb; i++)
|
||||
fds[i] = calloc(2, sizeof(int));
|
||||
|
||||
for (int i = 0; i <= pipe_nb; i++)
|
||||
{
|
||||
if (i != pipe_nb)
|
||||
{
|
||||
if (pipe(fds[i]) == -1)
|
||||
{
|
||||
fprintf(stderr, "bsh: bad pipe\n");
|
||||
}
|
||||
}
|
||||
char **val = expand(args[i], enclosure[i]);
|
||||
// if (is_builtin(val[0]))
|
||||
// res = exec_without_fork(val, i, pipe_nb, &fds);
|
||||
// else
|
||||
res = exec_with_fork(val, i, pipe_nb, &fds);
|
||||
char *tmp = val[0];
|
||||
int pos = 0;
|
||||
while (tmp)
|
||||
{
|
||||
free(tmp);
|
||||
tmp = val[++pos];
|
||||
}
|
||||
free(val);
|
||||
}
|
||||
for (int i = 0; i < pipe_nb; i++)
|
||||
free(fds[i]);
|
||||
free(fds);
|
||||
free(args);
|
||||
free(enclosure);
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "ast_evaluation_tools.h"
|
||||
|
||||
void pretty_print(struct ast *ast)
|
||||
{
|
||||
if (ast)
|
||||
{
|
||||
if (ast->type == AST_IF || ast->type == AST_FOR
|
||||
|| ast->type == AST_WHILE || ast->type == AST_UNTIL)
|
||||
{
|
||||
if (ast->type == AST_IF)
|
||||
{
|
||||
printf("if { ");
|
||||
pretty_print(ast->condition);
|
||||
printf("}; then { ");
|
||||
pretty_print(ast->left_child);
|
||||
printf("}");
|
||||
if (ast->right_child)
|
||||
{
|
||||
printf(" else { ");
|
||||
pretty_print(ast->right_child);
|
||||
printf("}");
|
||||
}
|
||||
}
|
||||
else if (ast->type == AST_FOR)
|
||||
{
|
||||
printf("for { ");
|
||||
for (int i = 0; ast->value[i]; i++)
|
||||
{
|
||||
printf("%s ", ast->value[i]);
|
||||
}
|
||||
printf("}; do { ");
|
||||
pretty_print(ast->left_child);
|
||||
printf("}");
|
||||
}
|
||||
else if (ast->type == AST_WHILE)
|
||||
{
|
||||
printf("while { ");
|
||||
pretty_print(ast->condition);
|
||||
printf("}; do { ");
|
||||
pretty_print(ast->left_child);
|
||||
printf("}");
|
||||
}
|
||||
else if (ast->type == AST_UNTIL)
|
||||
{
|
||||
printf("until { ");
|
||||
pretty_print(ast->condition);
|
||||
printf("}; do { ");
|
||||
pretty_print(ast->left_child);
|
||||
printf("}");
|
||||
}
|
||||
}
|
||||
else if (ast->type == AST_AND)
|
||||
{
|
||||
printf("{ ");
|
||||
pretty_print(ast->left_child);
|
||||
printf("} && { ");
|
||||
pretty_print(ast->right_child);
|
||||
printf("}");
|
||||
}
|
||||
else if (ast->type == AST_OR)
|
||||
{
|
||||
printf("{ ");
|
||||
pretty_print(ast->left_child);
|
||||
printf("} OR { ");
|
||||
pretty_print(ast->right_child);
|
||||
printf("}");
|
||||
}
|
||||
else if (ast->type == AST_REDIR)
|
||||
{
|
||||
pretty_print(ast->left_child);
|
||||
printf(" %s ", ast->value[0]);
|
||||
pretty_print(ast->right_child);
|
||||
}
|
||||
else if (ast->type == AST_PIPE)
|
||||
{
|
||||
pretty_print(ast->left_child);
|
||||
printf(" | ");
|
||||
pretty_print(ast->right_child);
|
||||
}
|
||||
else if (ast->type == AST_COMMAND)
|
||||
{
|
||||
if (ast->value[0])
|
||||
{
|
||||
printf("%s", ast->value[0]);
|
||||
for (size_t i = 1; ast->value[i] != NULL; i++)
|
||||
printf(" %s", ast->value[i]);
|
||||
}
|
||||
}
|
||||
else if (ast->type == AST_LIST)
|
||||
{
|
||||
pretty_print(ast->right_child);
|
||||
printf("; ");
|
||||
pretty_print(ast->left_child);
|
||||
}
|
||||
else if (ast->type == AST_NOT)
|
||||
{
|
||||
printf("! ");
|
||||
pretty_print(ast->left_child);
|
||||
}
|
||||
else if (ast->type == AST_ASSIGNMENT)
|
||||
{
|
||||
printf("%s=", ast->var_name);
|
||||
for (size_t i = 0; ast->value && ast->value[i] != NULL; i++)
|
||||
printf("%s ", ast->value[i]);
|
||||
}
|
||||
else if (ast->type == AST_FUNC)
|
||||
{
|
||||
if (ast->var_name)
|
||||
printf("%s() ", ast->var_name);
|
||||
printf("{ ");
|
||||
pretty_print(ast->left_child);
|
||||
printf("}");
|
||||
}
|
||||
else if (ast->type == AST_CMD_SUBSTITUTION)
|
||||
{
|
||||
printf("$(");
|
||||
pretty_print(ast->left_child);
|
||||
printf(")");
|
||||
}
|
||||
else if (ast->type == AST_SUBSHELL)
|
||||
{
|
||||
printf("(");
|
||||
pretty_print(ast->left_child);
|
||||
printf(")");
|
||||
}
|
||||
else if (ast->type == AST_CASE)
|
||||
{
|
||||
printf("case %s in ", ast->value[0]);
|
||||
pretty_print(ast->left_child);
|
||||
printf("esac");
|
||||
}
|
||||
else if (ast->type == AST_CASE_SWITCH)
|
||||
{
|
||||
printf("%s", ast->value[0]);
|
||||
for (int i = 1; ast->value[i]; i++)
|
||||
printf("|%s", ast->value[i]);
|
||||
printf(") ");
|
||||
pretty_print(ast->left_child);
|
||||
printf(";; ");
|
||||
pretty_print(ast->right_child);
|
||||
}
|
||||
else if (ast->type == AST_EOF)
|
||||
printf("EOF\n");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ast_evaluation_tools.h"
|
||||
|
||||
/*
|
||||
* The main objectif of this source code is to call some programe of the
|
||||
* computer using fork and exec
|
||||
*/
|
||||
|
||||
int call_exec(char **cmd)
|
||||
{
|
||||
pid_t cpid = fork();
|
||||
if (cpid == -1)
|
||||
{
|
||||
perror("bsh");
|
||||
return 1;
|
||||
}
|
||||
else if (!cpid)
|
||||
{
|
||||
execvp(cmd[0], cmd);
|
||||
fprintf(stderr, "bsh: command not found: %s\n", cmd[0]);
|
||||
kill(getpid(), SIGKILL);
|
||||
return 127;
|
||||
}
|
||||
else
|
||||
{
|
||||
int cstatus = 0;
|
||||
if (waitpid(cpid, &cstatus, 0) == -1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!WIFEXITED(cstatus))
|
||||
{
|
||||
return 127;
|
||||
}
|
||||
|
||||
return WEXITSTATUS(cstatus);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
#include "redir.h"
|
||||
|
||||
static bool is_int(char *str)
|
||||
{
|
||||
int i = 0;
|
||||
while (str[i])
|
||||
{
|
||||
if (str[i] < '0' || str[i] > '9')
|
||||
return false;
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int get_open_flags(char **redir)
|
||||
{
|
||||
char *type = redir[0][0] == '>' ? redir[0] : redir[1];
|
||||
switch (type[1])
|
||||
{
|
||||
case 0:
|
||||
case '|':
|
||||
return O_CREAT | O_WRONLY | O_TRUNC;
|
||||
case '>':
|
||||
return O_CREAT | O_WRONLY | O_APPEND;
|
||||
default:
|
||||
return O_CREAT | O_WRONLY | O_TRUNC;
|
||||
}
|
||||
}
|
||||
|
||||
static bool readable(int fd)
|
||||
{
|
||||
if (fd >= 0 && fd <= 2)
|
||||
return true;
|
||||
int o_accmode = 0;
|
||||
int rc = fcntl(fd, F_GETFL, &o_accmode);
|
||||
if (rc == -1)
|
||||
return false;
|
||||
rc = (o_accmode & O_ACCMODE);
|
||||
printf("%d\n", rc);
|
||||
return (rc == O_RDONLY || rc == O_RDWR);
|
||||
}
|
||||
|
||||
static bool writeable(int fd)
|
||||
{
|
||||
if (fd == 1 || fd == 2)
|
||||
return true;
|
||||
int o_accmode = 0;
|
||||
int rc = fcntl(fd, F_GETFL, &o_accmode);
|
||||
if (rc == -1)
|
||||
return false;
|
||||
rc = (o_accmode & O_ACCMODE);
|
||||
return (rc == O_WRONLY || rc == O_RDWR);
|
||||
}
|
||||
|
||||
static void setup_out_redir(char **redir)
|
||||
{
|
||||
char *filename = get_filename_from_redir(redir);
|
||||
int fd = open(filename, get_open_flags(redir), 0644);
|
||||
int ionumber = get_fd_from_redir(redir, true);
|
||||
if (ionumber < 0)
|
||||
{
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
dup2(fd, ionumber);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void setup_in_redir(char **redir)
|
||||
{
|
||||
char *filename = get_filename_from_redir(redir);
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
fprintf(stderr, "bsh: %s: No such file or directory\n", filename);
|
||||
return;
|
||||
}
|
||||
int ionumber = get_fd_from_redir(redir, false);
|
||||
if (ionumber < 0)
|
||||
{
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
dup2(fd, ionumber);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static int is_out_dup(char **redir)
|
||||
{
|
||||
if (redir[0][1] == '&')
|
||||
return redir[0][0] == '>';
|
||||
return redir[1][0] == '>';
|
||||
}
|
||||
|
||||
static void setup_dup_redir(char **redir)
|
||||
{
|
||||
int is_out = is_out_dup(redir);
|
||||
int fd1 = get_fd_from_redir(redir, is_out);
|
||||
char *filename = get_filename_from_redir(redir);
|
||||
if (is_out)
|
||||
{
|
||||
if (!strcmp(filename, "-"))
|
||||
close(fd1);
|
||||
else if (is_int(filename))
|
||||
{
|
||||
int fd2 = atoi(filename);
|
||||
if (fd2 == fd1)
|
||||
return;
|
||||
if (!writeable(fd2))
|
||||
{
|
||||
fprintf(stderr, "bsh: file descriptor %d is not writable\n",
|
||||
fd2);
|
||||
return;
|
||||
}
|
||||
int tmpout = dup(fd1);
|
||||
dup2(fd2, tmpout);
|
||||
close(tmpout);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!strcmp(filename, "-"))
|
||||
close(fd1);
|
||||
else if (is_int(filename))
|
||||
{
|
||||
int fd2 = atoi(filename);
|
||||
if (fd2 == fd1)
|
||||
return;
|
||||
if (!readable(fd2))
|
||||
{
|
||||
fprintf(stderr, "bsh: file descriptor %d is not readable\n",
|
||||
fd2);
|
||||
return;
|
||||
}
|
||||
int tmpin = dup(fd1);
|
||||
dup2(fd2, tmpin);
|
||||
close(tmpin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exec_redirections(char ***redirs)
|
||||
{
|
||||
int redirs_pos = 0;
|
||||
while (redirs[redirs_pos])
|
||||
{
|
||||
if (is_in_redir(redirs[redirs_pos]))
|
||||
setup_in_redir(redirs[redirs_pos]);
|
||||
if (is_out_redir(redirs[redirs_pos]))
|
||||
setup_out_redir(redirs[redirs_pos]);
|
||||
else
|
||||
setup_dup_redir(redirs[redirs_pos]);
|
||||
redirs_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
// int main(void)
|
||||
// {
|
||||
// char **cmd = calloc(3, sizeof(char *));
|
||||
// cmd[0] = "xargs";
|
||||
// cmd[1] = "-0";
|
||||
// char ***redirs = calloc(3, sizeof(char *));
|
||||
// redirs[0] = calloc(4, sizeof(char *));
|
||||
// redirs[0][0] = "3";
|
||||
// redirs[0][1] = ">";
|
||||
// redirs[0][2] = "test.txt";
|
||||
// exec_redirections(cmd, redirs);
|
||||
// free(redirs[0]);
|
||||
// free(redirs[1]);
|
||||
// free(redirs);
|
||||
// free(cmd);
|
||||
// }
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef REDIR_H
|
||||
#define REDIR_H
|
||||
|
||||
#include "redir_tools.h"
|
||||
|
||||
void exec_redirections(char ***redirs);
|
||||
|
||||
#endif // !REDIR_H
|
|
@ -0,0 +1,56 @@
|
|||
#include "redir_tools.h"
|
||||
|
||||
static bool is_int(char *str)
|
||||
{
|
||||
int i = 0;
|
||||
while (str[i])
|
||||
{
|
||||
if (str[i] < '0' || str[i] > '9')
|
||||
return false;
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char *get_filename_from_redir(char **redir)
|
||||
{
|
||||
char *filename = NULL;
|
||||
int len = 0;
|
||||
while (redir[len])
|
||||
len++;
|
||||
filename = redir[len - 1];
|
||||
return filename;
|
||||
}
|
||||
|
||||
int get_fd_from_redir(char **redir, bool out_redir)
|
||||
{
|
||||
if (is_int(redir[0]))
|
||||
{
|
||||
int res = atoi(redir[0]);
|
||||
if (res > 2)
|
||||
{
|
||||
fprintf(stderr, "bsh: bad file descriptor\n");
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return out_redir;
|
||||
}
|
||||
|
||||
bool is_out_redir(char **redir)
|
||||
{
|
||||
if (redir[0][0] == '>')
|
||||
return redir[0][1] != '&';
|
||||
if (redir[1][0] == '>')
|
||||
return redir[1][1] != '&';
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_in_redir(char **redir)
|
||||
{
|
||||
if (redir[0][0] == '<')
|
||||
return redir[0][1] != '&';
|
||||
if (redir[1][0] == '<')
|
||||
return redir[1][1] != '&';
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef REDIR_TOOLS_H
|
||||
#define REDIR_TOOLS_H
|
||||
|
||||
// #define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
char *get_filename_from_redir(char **redir);
|
||||
|
||||
int get_fd_from_redir(char **redir, bool out_redir);
|
||||
|
||||
bool is_out_redir(char **redir);
|
||||
|
||||
bool is_in_redir(char **redir);
|
||||
|
||||
#endif // !REDIR_TOOLS_H
|
|
@ -0,0 +1,128 @@
|
|||
#include "bsh.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "functions.h"
|
||||
#include "lexer.h"
|
||||
#include "loop_stack.h"
|
||||
#include "shell_input.h"
|
||||
#include "var_list.h"
|
||||
|
||||
struct shell *shell;
|
||||
|
||||
static void init_shell(int argc, char **argv)
|
||||
{
|
||||
shell = calloc(1, sizeof(struct shell));
|
||||
shell->pid = getppid();
|
||||
shell->pretty_print = argc > 1 ? !strcmp(argv[1], "--pretty-print") : false;
|
||||
if (shell->pretty_print)
|
||||
{
|
||||
printf("Pretty print enabled\n");
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
shell->verbose = argc > 1 ? !strcmp(argv[1], "--verbose") : false;
|
||||
if (shell->verbose)
|
||||
printf("Verbose mode enabled\n");
|
||||
shell->oldpwd = calloc(2048, sizeof(char));
|
||||
if (getenv("OLDPWD"))
|
||||
strcpy(shell->oldpwd, getenv("OLDPWD"));
|
||||
else if (getcwd(shell->oldpwd, 2048) == NULL)
|
||||
shell->exit = true;
|
||||
|
||||
shell->pwd = calloc(2048, sizeof(char));
|
||||
if (!shell->exit && getcwd(shell->pwd, 2048) == NULL)
|
||||
shell->exit = true;
|
||||
|
||||
// TODO: what are the shell parameters?
|
||||
shell->args = NULL;
|
||||
shell->nb_args = 0;
|
||||
|
||||
shell->ifs = calloc(100, sizeof(char));
|
||||
strcpy(shell->ifs, " \t\n");
|
||||
shell->uid = getuid();
|
||||
shell->var_list = NULL;
|
||||
shell->var_stack = NULL;
|
||||
shell->functions = NULL;
|
||||
shell->loop_stack = NULL;
|
||||
shell->random_nb = NULL;
|
||||
// append param shell->var_stack
|
||||
}
|
||||
|
||||
void free_shell(void)
|
||||
{
|
||||
int i = 0;
|
||||
while (shell->args && shell->args[i])
|
||||
free(shell->args[i++]);
|
||||
free(shell->args);
|
||||
free_list(shell);
|
||||
free_fun_sub(shell);
|
||||
free(shell->oldpwd);
|
||||
free(shell->pwd);
|
||||
free(shell->ifs);
|
||||
if (shell->random_nb)
|
||||
free(shell->random_nb);
|
||||
struct lexer_alias *alias = shell->alias_list;
|
||||
while (alias)
|
||||
{
|
||||
struct lexer_alias *next = alias->next;
|
||||
free(alias->name);
|
||||
struct lexer_token *token = alias->value;
|
||||
while (token)
|
||||
{
|
||||
struct lexer_token *next = token->next;
|
||||
lexer_token_free(token);
|
||||
token = next;
|
||||
}
|
||||
free(alias);
|
||||
alias = next;
|
||||
}
|
||||
free_loop(shell);
|
||||
free(shell);
|
||||
}
|
||||
|
||||
void print_shell(void)
|
||||
{
|
||||
printf("bsh\n");
|
||||
printf(" + oldpwd: %s\n", shell->oldpwd);
|
||||
printf(" + pwd: %s\n", shell->pwd);
|
||||
printf(" + args (%d):\n", shell->nb_args);
|
||||
for (int i = 0; i < shell->nb_args; i++)
|
||||
printf(" %s\n", shell->args[i]);
|
||||
printf(" + ifs:");
|
||||
for (int i = 0; shell->ifs[i] != '\0'; i++)
|
||||
printf(" %d", shell->ifs[i]);
|
||||
printf("\n");
|
||||
printf(" + uid: %d\n", shell->uid);
|
||||
printf(" + exit: %d\n", shell->exit);
|
||||
printf(" + last_return_code: %d\n", shell->return_code);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
init_shell(argc, argv);
|
||||
if (shell->exit)
|
||||
{
|
||||
free_shell();
|
||||
fprintf(stderr, "bsh: error during initialization.\n");
|
||||
return 1;
|
||||
}
|
||||
// print_shell();
|
||||
int res;
|
||||
if (shell->pretty_print && shell->verbose)
|
||||
res = get_input(argc - 2, argv + 2);
|
||||
else if (shell->pretty_print || shell->verbose)
|
||||
res = get_input(argc - 1, argv + 1);
|
||||
else
|
||||
res = get_input(argc, argv);
|
||||
free_shell();
|
||||
return res;
|
||||
char *input = calloc(49, sizeof(char));
|
||||
strcpy(input, "if test then bsh.c\n then echo 'bsh.c exists'\n fi");
|
||||
struct lexer *lexer = lexer_create(input);
|
||||
lexer_build(lexer);
|
||||
lexer_free(lexer);
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
#ifndef SHELL_H
|
||||
#define SHELL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
struct var
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
struct var *next;
|
||||
};
|
||||
|
||||
struct var_stack
|
||||
{
|
||||
struct var *var_list;
|
||||
struct var_stack *next;
|
||||
};
|
||||
|
||||
struct loop_stack
|
||||
{
|
||||
struct ast *loop;
|
||||
struct loop_stack *next;
|
||||
};
|
||||
|
||||
struct functions
|
||||
{
|
||||
char *name;
|
||||
struct ast *function;
|
||||
struct functions *next;
|
||||
};
|
||||
|
||||
struct shell
|
||||
{
|
||||
bool pretty_print;
|
||||
char *oldpwd;
|
||||
char *pwd;
|
||||
bool exit;
|
||||
char **args;
|
||||
int nb_args;
|
||||
char *ifs;
|
||||
uid_t uid;
|
||||
int return_code;
|
||||
char *random_nb;
|
||||
bool verbose;
|
||||
bool interupt;
|
||||
struct var_stack *var_stack;
|
||||
struct var *var_list;
|
||||
struct loop_stack *loop_stack;
|
||||
struct functions *functions;
|
||||
int ctn; // continue is a keyword
|
||||
int brk; // break is a keyword
|
||||
pid_t pid;
|
||||
struct lexer_alias *alias_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Print the shell structure.
|
||||
*
|
||||
*/
|
||||
void print_shell(void);
|
||||
|
||||
/**
|
||||
* @brief Free the shell structure.
|
||||
*
|
||||
*/
|
||||
void free_shell(void);
|
||||
|
||||
#endif // !SHELL_H
|
|
@ -0,0 +1,31 @@
|
|||
#include "builtins.h"
|
||||
|
||||
static bool is_int(char *str)
|
||||
{
|
||||
int i = 0;
|
||||
while (str[i])
|
||||
{
|
||||
if (str[i] < '0' || str[i] > '9')
|
||||
return false;
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int my_break(char **args)
|
||||
{
|
||||
if (args[1] == NULL)
|
||||
{
|
||||
shell->brk = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_int(args[1]))
|
||||
{
|
||||
shell->brk = atoi(args[1]);
|
||||
return shell->brk;
|
||||
}
|
||||
|
||||
fprintf(stderr, "bsh: break: Illegal number: %s\n", args[1]);
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#include "builtins.h"
|
||||
|
||||
int find_command(char **args, int fd_write)
|
||||
{
|
||||
if (!strcmp(args[0], "echo"))
|
||||
{
|
||||
echo(args, fd_write);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(args[0], "cd"))
|
||||
return cd(args);
|
||||
if (!strcmp(args[0], "continue"))
|
||||
return my_continue(args);
|
||||
if (!strcmp(args[0], "break"))
|
||||
return my_break(args);
|
||||
if (!strcmp(args[0], "exit"))
|
||||
return my_exit(args);
|
||||
if (!strcmp(args[0], "export"))
|
||||
return export(args);
|
||||
if (!strcmp(args[0], "unset"))
|
||||
return unset(args);
|
||||
if (!strcmp(args[0], "."))
|
||||
return dot(args);
|
||||
else
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
#ifndef BUILTINS_H
|
||||
#define BUILTINS_H
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bsh.h"
|
||||
#include "lexer.h"
|
||||
|
||||
extern struct shell *shell;
|
||||
|
||||
/**
|
||||
* @brief : Check which builtin command is received and execute it.
|
||||
*
|
||||
* @param toExecute : Builtin command starting from the bultin to the end
|
||||
* of it's argument.
|
||||
* @return int : 0 on success, 1 on failure.
|
||||
*/
|
||||
int find_command(char **toExecute, int fd_write);
|
||||
|
||||
/**
|
||||
* @brief Execute cd command.
|
||||
*
|
||||
* @param args the list of arguments
|
||||
* @return int return code
|
||||
*/
|
||||
int cd(char **args);
|
||||
|
||||
/**
|
||||
* @brief The echo command.
|
||||
*
|
||||
* @param args the list of arguments
|
||||
*/
|
||||
void echo(char **args, int fd_write);
|
||||
|
||||
/**
|
||||
* @brief @brief Modify the shell->exit and shell->return_code parameter in the
|
||||
* global variable shell
|
||||
*
|
||||
* @param args the list of arguments
|
||||
* @return int return 0 on success, 1 on failure
|
||||
*/
|
||||
int my_exit(char **args);
|
||||
|
||||
/**
|
||||
* @brief Put a variable to the exported variable-list, if it doesn't exist, it
|
||||
* creates it.
|
||||
*
|
||||
* @param args the list of arguments.
|
||||
* @return int return 0 on success, 1 on failure.
|
||||
*/
|
||||
int export(char **args);
|
||||
|
||||
/**
|
||||
* @brief The continue command.
|
||||
*
|
||||
* @param args the list of arguments.
|
||||
* @return int the number of enclosing loops to continue. -1 on failure.
|
||||
*/
|
||||
int my_continue(char **args);
|
||||
|
||||
/**
|
||||
* @brief The break command.
|
||||
*
|
||||
* @param args the list of arguments.
|
||||
* @return int the number of enclosing loops to break. -1 on failure.
|
||||
*/
|
||||
int my_break(char **args);
|
||||
|
||||
/**
|
||||
* @brief The unset command.
|
||||
*
|
||||
* @param args the list of arguments.
|
||||
* @return int return 0 on success, -1 on failure.
|
||||
*/
|
||||
int unset(char **args);
|
||||
|
||||
/**
|
||||
* @brief The dot builtin
|
||||
*
|
||||
* @param argv the list of arguments.
|
||||
* @return int
|
||||
*/
|
||||
int dot(char **argv);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
#include "builtins.h"
|
||||
#include "var_list.h"
|
||||
|
||||
int cd(char **args)
|
||||
{
|
||||
int len = 0;
|
||||
while (args[len])
|
||||
len++;
|
||||
if (len < 2)
|
||||
return 0;
|
||||
if (!strcmp(args[1], "-"))
|
||||
{
|
||||
chdir(shell->oldpwd);
|
||||
char *swap = shell->oldpwd;
|
||||
shell->oldpwd = shell->pwd;
|
||||
shell->pwd = swap;
|
||||
printf("%s\n", shell->pwd);
|
||||
setenv("OLDPWD", shell->oldpwd, 1);
|
||||
setenv("PWD", shell->pwd, 1);
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int error_chdir = chdir(args[1]);
|
||||
|
||||
if (error_chdir == -1)
|
||||
{
|
||||
fprintf(stderr, "bsh: cd: can't cd to %s\n", args[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
shell->oldpwd = strcpy(shell->oldpwd, shell->pwd);
|
||||
push_elt_list(shell, "OLDPWD", shell->pwd);
|
||||
|
||||
getcwd(shell->pwd, 2048);
|
||||
|
||||
setenv("OLDPWD", shell->oldpwd, 1);
|
||||
setenv("PWD", shell->pwd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#include "builtins.h"
|
||||
|
||||
static bool is_int(char *str)
|
||||
{
|
||||
int i = 0;
|
||||
while (str[i])
|
||||
{
|
||||
if (str[i] < '0' || str[i] > '9')
|
||||
return false;
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int my_continue(char **args)
|
||||
{
|
||||
if (args[1] == NULL)
|
||||
{
|
||||
shell->ctn = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_int(args[1]))
|
||||
{
|
||||
shell->ctn = atoi(args[1]);
|
||||
return shell->ctn;
|
||||
}
|
||||
|
||||
fprintf(stderr, "bsh: continue: Illegal number: %s\n", args[1]);
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bsh.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "shell_input.h"
|
||||
#include "var_list.h"
|
||||
|
||||
extern struct shell *shell;
|
||||
|
||||
static char **split_path(char *path)
|
||||
{
|
||||
char *rest = path;
|
||||
char *token;
|
||||
char **paths = NULL;
|
||||
int paths_nb = 0;
|
||||
while ((token = strtok_r(rest, ":", &rest)) != NULL)
|
||||
{
|
||||
paths = realloc(paths, sizeof(char *) * (paths_nb + 2));
|
||||
paths[paths_nb++] = strdup(token);
|
||||
}
|
||||
paths[paths_nb] = NULL;
|
||||
return paths;
|
||||
}
|
||||
|
||||
static char *find_in_path(const char *input)
|
||||
{
|
||||
char *path_tmp = getenv("PATH");
|
||||
char *path = calloc(strlen(path_tmp) + 1, sizeof(char));
|
||||
strcpy(path, path_tmp);
|
||||
if (!path)
|
||||
{
|
||||
fprintf(stderr, "bsh: .: %s: not found\n", input);
|
||||
shell->return_code = 2;
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
char **path_single = split_path(path);
|
||||
if (!path_single)
|
||||
{
|
||||
fprintf(stderr, "bsh: .: %s: not found\n", input);
|
||||
shell->return_code = 2;
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
int i = 0;
|
||||
bool found = false;
|
||||
char *final_path = NULL;
|
||||
while (path_single[i] && !found)
|
||||
{
|
||||
char *full_path =
|
||||
calloc(strlen(path_single[i]) + strlen(input) + 2, sizeof(char));
|
||||
strcpy(full_path, path_single[i]);
|
||||
strcat(full_path, "/");
|
||||
strcat(full_path, input);
|
||||
struct stat sb;
|
||||
if (access(full_path, F_OK) == 0
|
||||
&& (stat(path, &sb) == 0 && sb.st_mode & S_IXUSR))
|
||||
{
|
||||
found = true;
|
||||
final_path = full_path;
|
||||
continue;
|
||||
}
|
||||
free(full_path);
|
||||
free(path_single[i]);
|
||||
i++;
|
||||
}
|
||||
if (!final_path)
|
||||
{
|
||||
fprintf(stderr, "bsh: .: %s: not found\n", input);
|
||||
shell->return_code = 2;
|
||||
}
|
||||
free(path);
|
||||
free(path_single);
|
||||
return final_path;
|
||||
}
|
||||
|
||||
static char get_first_char(const char *arg)
|
||||
{
|
||||
int pos = 0;
|
||||
while (arg[pos] != 0 && arg[pos] == '.')
|
||||
pos++;
|
||||
return arg[pos];
|
||||
}
|
||||
|
||||
char *get_file_content(const char *path)
|
||||
{
|
||||
if (get_first_char(path) != '/')
|
||||
path = find_in_path(path);
|
||||
if (!path)
|
||||
return NULL;
|
||||
char *buffer = 0;
|
||||
long length;
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f)
|
||||
{
|
||||
shell->return_code = 127;
|
||||
fprintf(stderr, "bsh: .: Can't open %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
struct stat sb;
|
||||
if (!(stat(path, &sb) == 0 && sb.st_mode & S_IXUSR))
|
||||
{
|
||||
shell->return_code = 126;
|
||||
fprintf(stderr, "bsh: %s: Permission denied\n", path);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
length = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
buffer = calloc(length + 1, sizeof(char));
|
||||
if (buffer)
|
||||
{
|
||||
fread(buffer, 1, length, f);
|
||||
}
|
||||
fclose(f);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void restore_shell(struct var *vars, struct var_stack *stack)
|
||||
{
|
||||
free_list(shell);
|
||||
shell->var_list = vars;
|
||||
shell->var_stack = stack;
|
||||
}
|
||||
|
||||
int dot(char **argv)
|
||||
{
|
||||
struct var *save_var_list = shell->var_list;
|
||||
struct var_stack *save_var_stack = shell->var_stack;
|
||||
shell->var_list = NULL;
|
||||
shell->var_stack = NULL;
|
||||
char **args = calloc(2, sizeof(char *));
|
||||
new_var(shell, args);
|
||||
if (!argv[1])
|
||||
{
|
||||
free(args);
|
||||
restore_shell(save_var_list, save_var_stack);
|
||||
return 0;
|
||||
}
|
||||
char *buf = get_file_content(argv[1]);
|
||||
if (!buf)
|
||||
{
|
||||
free(args);
|
||||
restore_shell(save_var_list, save_var_stack);
|
||||
return shell->return_code;
|
||||
}
|
||||
parse_input(buf, NULL);
|
||||
free(buf);
|
||||
free(args);
|
||||
restore_shell(save_var_list, save_var_stack);
|
||||
return shell->return_code;
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
#include "builtins.h"
|
||||
|
||||
static void afterBackslash(char *toCheck, int *index, int fd_write)
|
||||
{
|
||||
*index += 1;
|
||||
|
||||
if (toCheck[*index] != '\0')
|
||||
{
|
||||
switch (toCheck[*index])
|
||||
{
|
||||
case '\\':
|
||||
write(fd_write, "\\", 1);
|
||||
break;
|
||||
case 'n':
|
||||
write(fd_write, "\n", 1);
|
||||
break;
|
||||
case 't':
|
||||
write(fd_write, "\t", 1);
|
||||
break;
|
||||
default:
|
||||
write(fd_write, "\\", 1);
|
||||
write(fd_write, &(toCheck[*index]), 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
write(fd_write, "\\", 1);
|
||||
}
|
||||
|
||||
void echo(char **args, int fd_write)
|
||||
{
|
||||
bool n_option = false;
|
||||
bool e_option = false;
|
||||
int start_print = 1;
|
||||
|
||||
for (; args[start_print] != NULL; start_print++)
|
||||
{
|
||||
if (!strcmp(args[start_print], "-n"))
|
||||
n_option = true;
|
||||
else if (!strcmp(args[start_print], "-e"))
|
||||
e_option = true;
|
||||
else if (!strcmp(args[start_print], "-ne")
|
||||
|| !strcmp(args[start_print], "-en"))
|
||||
{
|
||||
e_option = true;
|
||||
n_option = true;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (e_option)
|
||||
{
|
||||
for (; args[start_print] != NULL; start_print++)
|
||||
{
|
||||
for (int i = 0; args[start_print][i] != '\0'; i++)
|
||||
{
|
||||
if (args[start_print][i] == '\\')
|
||||
afterBackslash(args[start_print], &i, fd_write);
|
||||
else
|
||||
write(fd_write, &(args[start_print][i]), 1);
|
||||
}
|
||||
|
||||
if (args[start_print + 1] != NULL)
|
||||
write(fd_write, " ", 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (; args[start_print] != NULL; start_print++)
|
||||
{
|
||||
for (int i = 0; args[start_print][i] != '\0'; i++)
|
||||
write(fd_write, &(args[start_print][i]), 1);
|
||||
|
||||
if (args[start_print + 1] != NULL)
|
||||
write(fd_write, " ", 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!n_option)
|
||||
printf("\n");
|
||||
|
||||
fflush(stdout);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#include "builtins.h"
|
||||
|
||||
static bool is_int(char *str)
|
||||
{
|
||||
int i = 0;
|
||||
while (str[i])
|
||||
{
|
||||
if (str[i] < '0' || str[i] > '9')
|
||||
return false;
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int my_exit(char **args)
|
||||
{
|
||||
if (args[1] == NULL)
|
||||
shell->return_code = 0;
|
||||
else
|
||||
{
|
||||
if (is_int(args[1]))
|
||||
{
|
||||
int return_code = atoi(args[1]);
|
||||
shell->return_code = return_code % 256;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "bsh: exit: Illegal number: %s\n", args[1]);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
shell->exit = true;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#include "../variables/var_list.h"
|
||||
#include "builtins.h"
|
||||
|
||||
int export(char **args)
|
||||
{
|
||||
if (args[1] == NULL)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"bsh: export: expected export <name> or export <name>=<value> "
|
||||
"but got export\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; args[1][i] != '\0'; i++)
|
||||
{
|
||||
if ((args[1][i] >= 'a' && args[1][i] <= 'z')
|
||||
|| (args[1][i] >= 'A' && args[1][i] <= 'Z')
|
||||
|| (args[1][i] >= '0' && args[1][i] <= '9') || args[1][i] == '_')
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "bsh: export %s: bad variable name\n", args[1]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (args[2] == NULL)
|
||||
{
|
||||
char *value = find_elt_list(shell, args[1]);
|
||||
if (value != NULL)
|
||||
{
|
||||
setenv(args[1], value, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setenv(args[1], args[2], 1);
|
||||
push_elt_list(shell, args[1], args[2]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#include "../functions/functions.h"
|
||||
#include "../variables/var_list.h"
|
||||
#include "builtins.h"
|
||||
|
||||
int unset(char **args)
|
||||
{
|
||||
bool v_option = false;
|
||||
bool f_option = false;
|
||||
int start_print = 1;
|
||||
|
||||
for (; args[start_print] != NULL; start_print++)
|
||||
{
|
||||
if (!strcmp(args[start_print], "-v"))
|
||||
v_option = true;
|
||||
else if (!strcmp(args[start_print], "-f"))
|
||||
f_option = true;
|
||||
else if (!strcmp(args[start_print], "-fv")
|
||||
|| !strcmp(args[start_print], "-vf"))
|
||||
{
|
||||
v_option = true;
|
||||
f_option = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args[start_print][0] == '-')
|
||||
{
|
||||
fprintf(stderr, "bsh: unset: Illegal option %s\n",
|
||||
args[start_print]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!v_option && !f_option)
|
||||
v_option = true;
|
||||
|
||||
for (int i = 0; args[start_print][i] != '\0'; i++)
|
||||
{
|
||||
if ((args[start_print][i] >= 'a' && args[start_print][i] <= 'z')
|
||||
|| (args[start_print][i] >= 'A' && args[start_print][i] <= 'Z')
|
||||
|| (args[start_print][i] >= '0' && args[start_print][i] <= '9')
|
||||
|| args[start_print][i] == '_')
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "bsh: unset: %s: bad variable name\n",
|
||||
args[start_print]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (v_option)
|
||||
{
|
||||
del_name(shell, args[start_print]);
|
||||
unsetenv(args[start_print]);
|
||||
}
|
||||
|
||||
if (f_option)
|
||||
del_fun_name(shell, args[start_print]);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
#include "functions.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../bsh.h"
|
||||
|
||||
int push_elt_fun(struct shell *sh, char *name, struct ast *fun)
|
||||
{
|
||||
struct functions *tmp = sh->functions;
|
||||
while (tmp && strcmp(tmp->name, name))
|
||||
tmp = tmp->next;
|
||||
if (tmp)
|
||||
tmp->function = fun;
|
||||
else
|
||||
{
|
||||
struct functions *new = calloc(1, sizeof(struct functions));
|
||||
if (!new)
|
||||
return 1;
|
||||
new->name = calloc(strlen(name) + 1, sizeof(char));
|
||||
if (!new->name)
|
||||
{
|
||||
free(new);
|
||||
return 1;
|
||||
}
|
||||
strcpy(new->name, name);
|
||||
new->function = fun;
|
||||
new->next = sh->functions;
|
||||
sh->functions = new;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ast *find_elt_fun(struct shell *sh, char *name)
|
||||
{
|
||||
struct functions *tmp = sh->functions;
|
||||
while (tmp && strcmp(tmp->name, name))
|
||||
tmp = tmp->next;
|
||||
// printf("%s\n", tmp->value);
|
||||
if (tmp)
|
||||
return tmp->function;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free_fun_sub(struct shell *sh)
|
||||
{
|
||||
struct functions *fun = sh->functions;
|
||||
while (fun)
|
||||
{
|
||||
struct functions *tmp = fun;
|
||||
fun = fun->next;
|
||||
free(tmp->name);
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
int del_fun_name(struct shell *sh, char *name)
|
||||
{
|
||||
struct functions *actual = sh->functions;
|
||||
struct functions *previous = sh->functions;
|
||||
int index = 0;
|
||||
|
||||
while (actual)
|
||||
{
|
||||
if (!strcmp(actual->name, name))
|
||||
{
|
||||
if (index == 0)
|
||||
sh->functions = actual->next;
|
||||
else
|
||||
previous->next = actual->next;
|
||||
|
||||
free(actual->name);
|
||||
free(actual);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
index++;
|
||||
previous = actual;
|
||||
actual = actual->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct functions *fun_list_cpy(struct shell *sh)
|
||||
{
|
||||
struct functions *new = NULL;
|
||||
struct functions *fun = sh->functions;
|
||||
while (fun)
|
||||
{
|
||||
struct functions *tmp = new;
|
||||
new = calloc(1, sizeof(struct functions));
|
||||
if (!new)
|
||||
return NULL;
|
||||
new->name = calloc(strlen(fun->name) + 1, sizeof(char));
|
||||
if (!new->name)
|
||||
{
|
||||
free(new);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(new->name, fun->name);
|
||||
new->function = fun->function;
|
||||
new->next = tmp;
|
||||
fun = fun->next;
|
||||
}
|
||||
return new;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef FUNCTIONS_H
|
||||
#define FUNCTIONS_H
|
||||
|
||||
#include "bsh.h"
|
||||
|
||||
int push_elt_fun(struct shell *sh, char *name, struct ast *fun);
|
||||
struct ast *find_elt_fun(struct shell *sh, char *name);
|
||||
void free_fun_sub(struct shell *sh);
|
||||
int del_fun_name(struct shell *sh, char *name);
|
||||
struct functions *fun_list_cpy(struct shell *sh);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,278 @@
|
|||
#include "bsh.h"
|
||||
#include "lexer_tools.h"
|
||||
|
||||
extern struct shell *shell;
|
||||
|
||||
struct lexer_alias *get_alias(char *name)
|
||||
{
|
||||
struct lexer_alias *head = shell->alias_list;
|
||||
while (head)
|
||||
{
|
||||
if (head->name && !strcmp(head->name, name))
|
||||
return head;
|
||||
head = head->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *get_token_string(enum token_type type)
|
||||
{
|
||||
char *token_string[] = { "",
|
||||
"if",
|
||||
"else",
|
||||
"elif",
|
||||
"fi",
|
||||
"then",
|
||||
"do",
|
||||
"done",
|
||||
"while",
|
||||
"until",
|
||||
"for",
|
||||
"in",
|
||||
"&&",
|
||||
"||",
|
||||
";",
|
||||
"\n",
|
||||
"REDIR",
|
||||
"IONUMBER",
|
||||
"|",
|
||||
"!",
|
||||
"ASSIGNMENT_WORD",
|
||||
"(",
|
||||
"$(",
|
||||
"`",
|
||||
")",
|
||||
"{",
|
||||
"}",
|
||||
"$",
|
||||
"case",
|
||||
"esac",
|
||||
"WORD",
|
||||
"WORD_DOUBLE_QUOTE",
|
||||
"WORD_SINGLE_QUOTE",
|
||||
"EOF",
|
||||
" " };
|
||||
return strdup(token_string[type]);
|
||||
}
|
||||
|
||||
static char *get_alias_value(struct lexer_token *head)
|
||||
{
|
||||
char *res = NULL;
|
||||
while (head)
|
||||
{
|
||||
size_t len = res ? strlen(res) : 0;
|
||||
char *to_append =
|
||||
head->value ? head->value : get_token_string(head->type);
|
||||
res = realloc(res, sizeof(char) * (len + strlen(to_append) + 1));
|
||||
res[len] = '\0';
|
||||
strcat(res, to_append);
|
||||
if (!head->value)
|
||||
free(to_append);
|
||||
head = head->next;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void lexer_append_alias(struct lexer *lexer, struct lexer_alias *alias)
|
||||
{
|
||||
struct lexer_token *token = alias->value;
|
||||
while (token)
|
||||
{
|
||||
struct lexer_token *new = calloc(1, sizeof(struct lexer_token));
|
||||
new->type = token->type;
|
||||
if (token->value)
|
||||
new->value = strdup(token->value);
|
||||
else
|
||||
new->value = NULL;
|
||||
lexer_append(lexer, new);
|
||||
token = token->next;
|
||||
}
|
||||
}
|
||||
|
||||
struct lexer_token *copy_lexer_alias(struct lexer_token *head)
|
||||
{
|
||||
struct lexer_token *new_head = calloc(1, sizeof(struct lexer_token));
|
||||
new_head->type = head->type;
|
||||
if (head->value)
|
||||
new_head->value = strdup(head->value);
|
||||
head = head->next;
|
||||
struct lexer_token *current_tail = head;
|
||||
while (head)
|
||||
{
|
||||
struct lexer_token *new_token = calloc(1, sizeof(struct lexer_token));
|
||||
new_token->type = head->type;
|
||||
if (head->value)
|
||||
new_token->value = strdup(head->value);
|
||||
current_tail->next = new_token;
|
||||
current_tail = new_token;
|
||||
head = head->next;
|
||||
}
|
||||
return new_head;
|
||||
}
|
||||
|
||||
static void process_single_alias(struct lexer *lexer, char *name,
|
||||
struct lexer_token *value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
struct lexer_alias *alias = calloc(1, sizeof(struct lexer_alias));
|
||||
alias->name = strdup(name);
|
||||
if (value->type != TOKEN_NEWLINE && value->type != TOKEN_SEMICOLON
|
||||
&& value->type != TOKEN_EOF)
|
||||
alias->value = value;
|
||||
else
|
||||
{
|
||||
struct lexer_token *token = calloc(1, sizeof(struct lexer_token));
|
||||
token->type = TOKEN_WORD;
|
||||
token->value = strdup("");
|
||||
alias->value = token;
|
||||
}
|
||||
alias->next = lexer->alias_list;
|
||||
lexer->alias_list = alias;
|
||||
shell->return_code = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct lexer_alias *alias = get_alias(name);
|
||||
if (!alias)
|
||||
{
|
||||
shell->return_code = 1;
|
||||
fprintf(stderr, "bsh: alias: %s: not found\n", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *a_value = get_alias_value(alias->value);
|
||||
printf("%s='%s'\n", alias->name, a_value);
|
||||
free(a_value);
|
||||
shell->return_code = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process_alias(struct lexer_token *prev, struct lexer_token *head,
|
||||
struct lexer *lexer)
|
||||
{
|
||||
shell->return_code = 0;
|
||||
if (!head
|
||||
|| (head->type != TOKEN_ALIAS && head->type != TOKEN_SPACE
|
||||
&& head->type != TOKEN_ASSIGNMENT_WORD && head->type != TOKEN_WORD
|
||||
&& head->type != TOKEN_WORD_SINGLE_QUOTE
|
||||
&& head->type != TOKEN_WORD_DOUBLE_QUOTE))
|
||||
{
|
||||
if (head
|
||||
&& (head->type == TOKEN_SEMICOLON || head->type == TOKEN_NEWLINE))
|
||||
{
|
||||
if (prev)
|
||||
prev->next = head->next;
|
||||
else
|
||||
lexer->tokens = head->next;
|
||||
lexer_token_free(head);
|
||||
}
|
||||
return;
|
||||
}
|
||||
struct lexer_token *name = head;
|
||||
if (head->type == TOKEN_ALIAS && head->next)
|
||||
{
|
||||
name = head->next;
|
||||
lexer_token_free(head);
|
||||
}
|
||||
while (name->type == TOKEN_SPACE)
|
||||
{
|
||||
struct lexer_token *next = name->next;
|
||||
lexer_token_free(name);
|
||||
name = next;
|
||||
}
|
||||
head = name;
|
||||
if (!name || !name->value)
|
||||
{
|
||||
if (prev)
|
||||
prev->next = NULL;
|
||||
else
|
||||
lexer->tokens = NULL;
|
||||
return;
|
||||
}
|
||||
struct lexer_token *value = NULL;
|
||||
struct lexer_token *end = head->next ? head->next : head;
|
||||
if (name->type == TOKEN_ASSIGNMENT_WORD)
|
||||
{
|
||||
value = head->next;
|
||||
while (value->type == TOKEN_SPACE)
|
||||
{
|
||||
struct lexer_token *next = value->next;
|
||||
lexer_token_free(value);
|
||||
value = next;
|
||||
}
|
||||
end = value;
|
||||
if (value->next)
|
||||
end = value->next;
|
||||
struct lexer_token *previous = value;
|
||||
while (end && end->type != TOKEN_SPACE && end->type != TOKEN_NEWLINE
|
||||
&& end->type != TOKEN_EOF && end->type != TOKEN_SEMICOLON)
|
||||
{
|
||||
previous = end;
|
||||
end = end->next;
|
||||
}
|
||||
previous->next = NULL;
|
||||
if (prev)
|
||||
prev->next = end;
|
||||
else
|
||||
lexer->tokens = end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prev)
|
||||
prev->next = end;
|
||||
else
|
||||
lexer->tokens = end;
|
||||
}
|
||||
process_single_alias(lexer, name->value, value);
|
||||
lexer_token_free(name);
|
||||
if (end && end->type == TOKEN_SEMICOLON)
|
||||
end->type = TOKEN_SPACE;
|
||||
process_alias(end, end ? end->next : NULL, lexer);
|
||||
}
|
||||
|
||||
void process_unalias(struct lexer_token *prev, struct lexer_token *head,
|
||||
struct lexer *lexer)
|
||||
{
|
||||
shell->return_code = 0;
|
||||
while (head && head->type != TOKEN_SEMICOLON && head->type != TOKEN_NEWLINE
|
||||
&& head->type != TOKEN_EOF)
|
||||
{
|
||||
if (head->type < TOKEN_WORD || head->type > TOKEN_WORD_DOUBLE_QUOTE)
|
||||
{
|
||||
struct lexer_token *next = head->next;
|
||||
lexer_token_free(head);
|
||||
head = next;
|
||||
continue;
|
||||
}
|
||||
bool to_free = false;
|
||||
if (!head->value)
|
||||
{
|
||||
head->value = get_token_string(head->type);
|
||||
to_free = true;
|
||||
}
|
||||
struct lexer_alias *alias = get_alias(head->value);
|
||||
if (alias)
|
||||
{
|
||||
free(alias->name);
|
||||
alias->name = calloc(1, sizeof(char));
|
||||
}
|
||||
else
|
||||
{
|
||||
shell->return_code = 1;
|
||||
fprintf(stderr, "bsh: alias: %s: not found\n", head->value);
|
||||
}
|
||||
if (to_free)
|
||||
free(head->value);
|
||||
struct lexer_token *next = head->next;
|
||||
lexer_token_free(head);
|
||||
head = next;
|
||||
}
|
||||
if (head && head->type == TOKEN_SEMICOLON)
|
||||
head->type = TOKEN_SPACE;
|
||||
if (prev)
|
||||
prev->next = head;
|
||||
else
|
||||
lexer->tokens = head;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#include "lexer_tools.h"
|
||||
|
||||
void create_and_append_token(struct lexer *lexer, enum token_type type,
|
||||
char *value)
|
||||
{
|
||||
struct lexer_token *token = calloc(1, sizeof(struct lexer_token));
|
||||
token->type = type;
|
||||
token->value = value;
|
||||
lexer_append(lexer, token);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#include "lexer_tools.h"
|
||||
|
||||
void process_export(struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *token = lexer->tokens;
|
||||
while (token && token->next)
|
||||
{
|
||||
if (token->value && !strcmp(token->value, "export")
|
||||
&& token->next->type == TOKEN_ASSIGNMENT_WORD)
|
||||
{
|
||||
token->next->type = TOKEN_WORD;
|
||||
}
|
||||
token = token->next;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#include "lexer.h"
|
||||
#include "lexer_tools.h"
|
||||
|
||||
bool is_int(char *input)
|
||||
{
|
||||
int i = 0;
|
||||
while (input[i])
|
||||
{
|
||||
if (input[i] < '0' || input[i] > '9')
|
||||
return false;
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// void words_to_ionumber(struct lexer *lexer)
|
||||
// {
|
||||
// struct lexer_token *token = lexer->tokens;
|
||||
// struct lexer_token *prev = lexer->tokens;
|
||||
// while (token)
|
||||
// {
|
||||
// if (token->type == TOKEN_REDIR && prev && prev->type == TOKEN_WORD
|
||||
// && is_int(prev->value))
|
||||
// {
|
||||
// prev->type = TOKEN_IONUMBER;
|
||||
// }
|
||||
// prev = token;
|
||||
// token = token->next;
|
||||
// }
|
||||
// }
|
|
@ -0,0 +1,42 @@
|
|||
#include "lexer_tools.h"
|
||||
|
||||
bool is_keyword(char *word)
|
||||
{
|
||||
return (
|
||||
!strcmp(word, "if") || !strcmp(word, "else") || !strcmp(word, "elif")
|
||||
|| !strcmp(word, "fi") || !strcmp(word, "then") || !strcmp(word, "!")
|
||||
|| !strcmp(word, "do") || !strcmp(word, "done") || !strcmp(word, "for")
|
||||
|| !strcmp(word, "while") || !strcmp(word, "until")
|
||||
|| !strcmp(word, "case") || !strcmp(word, "esac"));
|
||||
}
|
||||
|
||||
enum token_type get_keyword(char *word)
|
||||
{
|
||||
if (!strcmp(word, "if"))
|
||||
return TOKEN_IF;
|
||||
if (!strcmp(word, "else"))
|
||||
return TOKEN_ELSE;
|
||||
if (!strcmp(word, "elif"))
|
||||
return TOKEN_ELIF;
|
||||
if (!strcmp(word, "fi"))
|
||||
return TOKEN_FI;
|
||||
if (!strcmp(word, "then"))
|
||||
return TOKEN_THEN;
|
||||
if (!strcmp(word, "!"))
|
||||
return TOKEN_NOT;
|
||||
if (!strcmp(word, "do"))
|
||||
return TOKEN_DO;
|
||||
if (!strcmp(word, "done"))
|
||||
return TOKEN_DONE;
|
||||
if (!strcmp(word, "for"))
|
||||
return TOKEN_FOR;
|
||||
if (!strcmp(word, "while"))
|
||||
return TOKEN_WHILE;
|
||||
if (!strcmp(word, "until"))
|
||||
return TOKEN_UNTIL;
|
||||
if (!strcmp(word, "case"))
|
||||
return TOKEN_CASE;
|
||||
if (!strcmp(word, "esac"))
|
||||
return TOKEN_ESAC;
|
||||
return TOKEN_ERROR;
|
||||
}
|
|
@ -0,0 +1,461 @@
|
|||
#include "lexer.h"
|
||||
|
||||
#include "../bsh.h"
|
||||
#include "lexer_tools.h"
|
||||
|
||||
extern struct shell *shell;
|
||||
|
||||
struct lexer_token *lexer_token_free(struct lexer_token *token)
|
||||
{
|
||||
free(token->value);
|
||||
free(token);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lexer *lexer_create(char *input)
|
||||
{
|
||||
struct lexer *lexer = calloc(1, sizeof(struct lexer));
|
||||
lexer->input = input;
|
||||
lexer->tail = NULL;
|
||||
lexer->head = NULL;
|
||||
lexer->tokens = NULL;
|
||||
return lexer;
|
||||
}
|
||||
|
||||
struct lexer_token *lexer_peek(struct lexer *lexer)
|
||||
{
|
||||
return lexer->head;
|
||||
}
|
||||
|
||||
struct lexer_token *lexer_pop(struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *token = lexer->head;
|
||||
lexer->head = lexer->head->next;
|
||||
return token;
|
||||
}
|
||||
|
||||
void lexer_append(struct lexer *lexer, struct lexer_token *token)
|
||||
{
|
||||
token->next = NULL;
|
||||
if (lexer->tail)
|
||||
{
|
||||
lexer->tail->next = token;
|
||||
lexer->tail = token;
|
||||
}
|
||||
else
|
||||
{
|
||||
lexer->tokens = token;
|
||||
lexer->tail = token;
|
||||
}
|
||||
}
|
||||
|
||||
void lexer_free(struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *token = lexer->tokens;
|
||||
while (token)
|
||||
{
|
||||
struct lexer_token *next = token->next;
|
||||
lexer_token_free(token);
|
||||
token = next;
|
||||
}
|
||||
struct lexer_alias *alias = lexer->alias_list;
|
||||
while (alias)
|
||||
{
|
||||
struct lexer_alias *next = alias->next;
|
||||
free(alias->name);
|
||||
struct lexer_token *token = alias->value;
|
||||
while (token)
|
||||
{
|
||||
struct lexer_token *next = token->next;
|
||||
lexer_token_free(token);
|
||||
token = next;
|
||||
}
|
||||
free(alias);
|
||||
alias = next;
|
||||
}
|
||||
lexer->alias_list = NULL;
|
||||
lexer->head = NULL;
|
||||
lexer->tail = NULL;
|
||||
free(lexer);
|
||||
}
|
||||
|
||||
static bool is_separator(char c)
|
||||
{
|
||||
return (c == ';' || c == '\n');
|
||||
}
|
||||
|
||||
static enum token_type get_separator(char c)
|
||||
{
|
||||
if (c == ';')
|
||||
return TOKEN_SEMICOLON;
|
||||
if (c == '\n')
|
||||
return TOKEN_NEWLINE;
|
||||
return TOKEN_ERROR;
|
||||
}
|
||||
|
||||
static bool is_quote(char c)
|
||||
{
|
||||
return (c == '\'' || c == '\"' || c == '`');
|
||||
}
|
||||
|
||||
static enum token_type get_quote(char c)
|
||||
{
|
||||
if (c == '\'')
|
||||
return TOKEN_WORD_SINGLE_QUOTE;
|
||||
if (c == '\"')
|
||||
return TOKEN_WORD_DOUBLE_QUOTE;
|
||||
if (c == '`')
|
||||
return TOKEN_BACKTICK;
|
||||
return TOKEN_ERROR;
|
||||
}
|
||||
|
||||
static void create_word_and_append(char *word, int word_pos, bool *in_cmd,
|
||||
struct lexer *lexer,
|
||||
enum token_type *word_type)
|
||||
{
|
||||
if (!word)
|
||||
return;
|
||||
word[word_pos] = 0;
|
||||
struct lexer_alias *alias = get_alias(word);
|
||||
if (alias && !lexer->alias)
|
||||
{
|
||||
lexer_append_alias(lexer, alias);
|
||||
free(word);
|
||||
return;
|
||||
}
|
||||
if (*word_type == TOKEN_WORD
|
||||
&& (!strcmp(word, "alias") || !strcmp(word, "unalias")))
|
||||
{
|
||||
lexer->alias_prev = lexer->tail;
|
||||
create_and_append_token(
|
||||
lexer, !strcmp(word, "alias") ? TOKEN_ALIAS : TOKEN_UNALIAS, NULL);
|
||||
lexer->alias = lexer->tail;
|
||||
free(word);
|
||||
*word_type = TOKEN_WORD;
|
||||
return;
|
||||
}
|
||||
if (*word_type == TOKEN_WORD && (!strcmp(word, "in"))
|
||||
&& ((!lexer->in_for && lexer->found_for) || lexer->found_case))
|
||||
{
|
||||
create_and_append_token(lexer, TOKEN_IN, NULL);
|
||||
if (lexer->found_for)
|
||||
lexer->in_for = true;
|
||||
free(word);
|
||||
return;
|
||||
}
|
||||
struct lexer_token *token = calloc(1, sizeof(struct lexer_token));
|
||||
token->type = is_keyword(word) && !lexer->alias
|
||||
&& (!(*in_cmd) || lexer->found_case
|
||||
|| (lexer->found_for && !strcmp(word, "do")))
|
||||
? get_keyword(word)
|
||||
: *word_type;
|
||||
if (token->type >= TOKEN_WORD && !lexer->found_case)
|
||||
*in_cmd = true;
|
||||
if (token->type == TOKEN_FOR)
|
||||
lexer->found_for = true;
|
||||
if (token->type == TOKEN_CASE)
|
||||
lexer->found_case = true;
|
||||
if (token->type == TOKEN_ESAC)
|
||||
lexer->found_case = false;
|
||||
token->value = word;
|
||||
word = NULL;
|
||||
word_pos = 0;
|
||||
lexer_append(lexer, token);
|
||||
}
|
||||
|
||||
static bool is_pipe(char c, char next)
|
||||
{
|
||||
return (c == '|' && next != '|');
|
||||
}
|
||||
|
||||
static bool is_redir(char c1)
|
||||
{
|
||||
return (c1 == '<' || c1 == '>');
|
||||
}
|
||||
|
||||
static char *get_redir(char c1, char c2)
|
||||
{
|
||||
char *res = calloc(3, sizeof(char));
|
||||
if (c1 == '<')
|
||||
{
|
||||
res[0] = '<';
|
||||
if (c2 == '&' || c2 == '>')
|
||||
res[1] = c2;
|
||||
}
|
||||
if (c1 == '>')
|
||||
{
|
||||
res[0] = '>';
|
||||
if (c2 == '&' || c2 == '>' || c2 == '|')
|
||||
res[1] = c2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool is_special(char c)
|
||||
{
|
||||
return (c == '(' || c == ')' || c == '{' || c == '}' || c == '$');
|
||||
}
|
||||
|
||||
static enum token_type get_special(char c)
|
||||
{
|
||||
if (c == '(')
|
||||
return TOKEN_PARENTHESIS_OPEN;
|
||||
if (c == ')')
|
||||
return TOKEN_PARENTHESIS_CLOSE;
|
||||
if (c == '{')
|
||||
return TOKEN_BRACE_OPEN;
|
||||
if (c == '}')
|
||||
return TOKEN_BRACE_CLOSE;
|
||||
if (c == '$')
|
||||
return TOKEN_DOLLAR;
|
||||
return TOKEN_ERROR;
|
||||
}
|
||||
|
||||
static bool is_word_alphanum(char *word, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
if (!((word[i] >= 'a' && word[i] <= 'z')
|
||||
|| (word[i] >= 'A' && word[i] <= 'Z')
|
||||
|| (word[i] >= '0' && word[i] <= '9') || word[i] == '_'))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void word_lexer(struct lexer *lexer, char *input, bool *in_cmd,
|
||||
enum token_type *word_type)
|
||||
{
|
||||
int j = 0;
|
||||
char *word = NULL;
|
||||
int word_pos = 0;
|
||||
while (input[j])
|
||||
{
|
||||
if (input[j] == '\\')
|
||||
{
|
||||
word = realloc(word, (word_pos + 3) * sizeof(char));
|
||||
word[word_pos++] = input[j++];
|
||||
if (input[j] == 0)
|
||||
break;
|
||||
word[word_pos++] = input[j++];
|
||||
if (input[j] == 0)
|
||||
break;
|
||||
}
|
||||
if ((*word_type == TOKEN_WORD && is_separator(input[j]))
|
||||
|| (is_pipe(input[j], input[j + 1]) && *word_type == TOKEN_WORD))
|
||||
{
|
||||
if (word)
|
||||
{
|
||||
create_word_and_append(word, word_pos, in_cmd, lexer,
|
||||
word_type);
|
||||
word = NULL;
|
||||
word_pos = 0;
|
||||
}
|
||||
create_and_append_token(
|
||||
lexer,
|
||||
is_separator(input[j]) ? get_separator(input[j]) : TOKEN_PIPE,
|
||||
NULL);
|
||||
if (is_separator(input[j]))
|
||||
{
|
||||
if (lexer->alias != NULL && lexer->alias->next != lexer->tail)
|
||||
{
|
||||
if (lexer->alias->type == TOKEN_ALIAS)
|
||||
process_alias(lexer->alias_prev, lexer->alias, lexer);
|
||||
else
|
||||
process_unalias(lexer->alias_prev, lexer->alias, lexer);
|
||||
}
|
||||
else if (lexer->alias)
|
||||
{
|
||||
if (lexer->alias_prev)
|
||||
{
|
||||
lexer_token_free(lexer->alias_prev->next);
|
||||
lexer->alias_prev->next = lexer->tail;
|
||||
}
|
||||
else
|
||||
{
|
||||
lexer_token_free(lexer->alias);
|
||||
lexer->tokens = lexer->tail;
|
||||
}
|
||||
}
|
||||
if (input[j] == '\n')
|
||||
{
|
||||
struct lexer_alias *alias = lexer->alias_list;
|
||||
while (alias)
|
||||
{
|
||||
struct lexer_alias *next = alias->next;
|
||||
alias->next = shell->alias_list;
|
||||
shell->alias_list = alias;
|
||||
alias = next;
|
||||
}
|
||||
lexer->alias_list = NULL;
|
||||
}
|
||||
lexer->alias = NULL;
|
||||
lexer->in_for = false;
|
||||
lexer->found_for = false;
|
||||
}
|
||||
*in_cmd = false;
|
||||
}
|
||||
else if (*word_type == TOKEN_WORD
|
||||
&& ((input[j] == '&' && input[j + 1] == '&')
|
||||
|| (input[j] == '|' && input[j + 1] == '|')))
|
||||
{
|
||||
if (word)
|
||||
{
|
||||
create_word_and_append(word, word_pos, in_cmd, lexer,
|
||||
word_type);
|
||||
word = NULL;
|
||||
word_pos = 0;
|
||||
}
|
||||
create_and_append_token(
|
||||
lexer, input[j] == '&' ? TOKEN_AND : TOKEN_OR, NULL);
|
||||
j++;
|
||||
}
|
||||
else if (*word_type == TOKEN_WORD && is_special(input[j]))
|
||||
{
|
||||
if (input[j] == '}' && lexer->in_variable)
|
||||
{
|
||||
word = realloc(word, (word_pos + 2) * sizeof(char));
|
||||
word[word_pos++] = input[j];
|
||||
lexer->in_variable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (word && (input[j] != '$' || input[j + 1] == '('))
|
||||
{
|
||||
create_word_and_append(word, word_pos, in_cmd, lexer,
|
||||
word_type);
|
||||
word = NULL;
|
||||
word_pos = 0;
|
||||
}
|
||||
if (input[j] == '$')
|
||||
{
|
||||
if (input[j + 1] == '(')
|
||||
{
|
||||
*in_cmd = false;
|
||||
j++;
|
||||
create_and_append_token(lexer, TOKEN_SUBSTITUTION_OPEN,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
word = realloc(word, (word_pos + 3) * sizeof(char));
|
||||
word[word_pos++] = input[j];
|
||||
if (input[j + 1] == '{' || input[j + 1] == '$')
|
||||
{
|
||||
word[word_pos++] = input[++j];
|
||||
lexer->in_variable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (input[j] == '{' || input[j] == '(')
|
||||
*in_cmd = false;
|
||||
create_and_append_token(lexer, get_special(input[j]), NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (*word_type == TOKEN_WORD && is_redir(input[j]))
|
||||
{
|
||||
if (word)
|
||||
{
|
||||
word[word_pos] = 0;
|
||||
if (is_int(word))
|
||||
{
|
||||
create_and_append_token(lexer, TOKEN_IONUMBER, word);
|
||||
}
|
||||
else
|
||||
create_word_and_append(word, word_pos, in_cmd, lexer,
|
||||
word_type);
|
||||
word = NULL;
|
||||
word_pos = 0;
|
||||
}
|
||||
create_and_append_token(lexer, TOKEN_REDIR,
|
||||
get_redir(input[j], input[j + 1]));
|
||||
if (input[j + 1] != 0)
|
||||
j++;
|
||||
}
|
||||
else if (*word_type == TOKEN_WORD && input[j] == '='
|
||||
&& (!lexer->tail || lexer->tail->type != TOKEN_ASSIGNMENT_WORD)
|
||||
&& is_word_alphanum(word, word_pos))
|
||||
{
|
||||
if (word)
|
||||
{
|
||||
create_word_and_append(word, word_pos, in_cmd, lexer,
|
||||
word_type);
|
||||
word = NULL;
|
||||
word_pos = 0;
|
||||
lexer->tail->type = TOKEN_ASSIGNMENT_WORD;
|
||||
}
|
||||
}
|
||||
else if (is_quote(input[j])
|
||||
&& (*word_type == get_quote(input[j])
|
||||
|| *word_type == TOKEN_WORD))
|
||||
{
|
||||
if (word)
|
||||
{
|
||||
create_word_and_append(word, word_pos, in_cmd, lexer,
|
||||
word_type);
|
||||
word = NULL;
|
||||
word_pos = 0;
|
||||
}
|
||||
if (lexer->alias)
|
||||
{
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
if (*word_type == TOKEN_WORD && input[j] != '`')
|
||||
*word_type = get_quote(input[j]);
|
||||
else if (*word_type == TOKEN_WORD && input[j] == '`')
|
||||
{
|
||||
create_and_append_token(lexer, TOKEN_BACKTICK, NULL);
|
||||
}
|
||||
else if (get_quote(input[j]) == *word_type)
|
||||
{
|
||||
*word_type = TOKEN_WORD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
word = realloc(word, (word_pos + 2) * sizeof(char));
|
||||
word[word_pos++] = input[j];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
if (word)
|
||||
{
|
||||
create_word_and_append(word, word_pos, in_cmd, lexer, word_type);
|
||||
word = NULL;
|
||||
word_pos = 0;
|
||||
}
|
||||
free(input);
|
||||
}
|
||||
|
||||
void lexer_build(struct lexer *lexer)
|
||||
{
|
||||
bool in_cmd = false;
|
||||
char **words = split_in_words(lexer->input);
|
||||
enum token_type word_type = TOKEN_WORD;
|
||||
for (int i = 0; words[i]; i++)
|
||||
{
|
||||
word_lexer(lexer, words[i], &in_cmd, &word_type);
|
||||
create_and_append_token(lexer, TOKEN_SPACE, NULL);
|
||||
}
|
||||
if (word_type != TOKEN_WORD)
|
||||
{
|
||||
fprintf(stderr, "Error: quote <%c> is not terminated.\n",
|
||||
word_type == TOKEN_WORD_SINGLE_QUOTE ? '\'' : '\"');
|
||||
shell->return_code = 2;
|
||||
shell->exit = true;
|
||||
}
|
||||
create_and_append_token(lexer, TOKEN_EOF, NULL);
|
||||
process_spaces(lexer);
|
||||
process_export(lexer);
|
||||
if (shell->verbose)
|
||||
lexer_print(lexer);
|
||||
free(words);
|
||||
lexer->head = lexer->tokens;
|
||||
}
|
||||
|
||||
void lexer_go_back(struct lexer *lexer, struct lexer_token *token)
|
||||
{
|
||||
lexer->head = token;
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
#ifndef LEXER_H
|
||||
#define LEXER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum token_type
|
||||
{
|
||||
TOKEN_ERROR,
|
||||
TOKEN_IF,
|
||||
TOKEN_ELSE,
|
||||
TOKEN_ELIF,
|
||||
TOKEN_FI,
|
||||
TOKEN_THEN,
|
||||
TOKEN_DO,
|
||||
TOKEN_DONE,
|
||||
TOKEN_WHILE,
|
||||
TOKEN_UNTIL,
|
||||
TOKEN_FOR,
|
||||
TOKEN_IN,
|
||||
TOKEN_AND,
|
||||
TOKEN_OR,
|
||||
TOKEN_SEMICOLON,
|
||||
TOKEN_NEWLINE,
|
||||
TOKEN_REDIR,
|
||||
TOKEN_IONUMBER,
|
||||
TOKEN_PIPE,
|
||||
TOKEN_NOT,
|
||||
TOKEN_ASSIGNMENT_WORD,
|
||||
TOKEN_PARENTHESIS_OPEN,
|
||||
TOKEN_SUBSTITUTION_OPEN,
|
||||
TOKEN_BACKTICK,
|
||||
TOKEN_PARENTHESIS_CLOSE,
|
||||
TOKEN_BRACE_OPEN,
|
||||
TOKEN_BRACE_CLOSE,
|
||||
TOKEN_DOLLAR,
|
||||
TOKEN_CASE,
|
||||
TOKEN_ESAC,
|
||||
TOKEN_WORD,
|
||||
TOKEN_WORD_DOUBLE_QUOTE,
|
||||
TOKEN_WORD_SINGLE_QUOTE,
|
||||
TOKEN_EOF,
|
||||
TOKEN_SPACE,
|
||||
TOKEN_ALIAS,
|
||||
TOKEN_UNALIAS
|
||||
};
|
||||
|
||||
struct lexer_token
|
||||
{
|
||||
enum token_type type;
|
||||
char *value;
|
||||
struct lexer_token *next;
|
||||
};
|
||||
|
||||
struct lexer_alias
|
||||
{
|
||||
char *name;
|
||||
struct lexer_token *value;
|
||||
struct lexer_alias *next;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief free an allocated token.
|
||||
*
|
||||
* @param token the token to free.
|
||||
*/
|
||||
struct lexer_token *lexer_token_free(struct lexer_token *token);
|
||||
|
||||
/**
|
||||
* @var lexer::input
|
||||
* Member 'input' contains the input string.
|
||||
* @var lexer::tokens
|
||||
* Member 'tokens' contains the head of the list of tokens.
|
||||
* @var lexer::tail
|
||||
* Member 'tail' contains the last token of the list.
|
||||
*/
|
||||
struct lexer
|
||||
{
|
||||
char *input;
|
||||
struct lexer_token *tokens;
|
||||
struct lexer_token *tail;
|
||||
struct lexer_token *head;
|
||||
bool in_for;
|
||||
bool in_variable;
|
||||
bool found_for;
|
||||
bool found_case;
|
||||
struct lexer_token *alias;
|
||||
struct lexer_token *alias_prev;
|
||||
struct lexer_alias *alias_list;
|
||||
};
|
||||
|
||||
/**
|
||||
** @brief Allocate and init a new lexer.
|
||||
** @param input the string to use as input stream.
|
||||
*/
|
||||
struct lexer *lexer_create(char *input);
|
||||
|
||||
/**
|
||||
** @brief Fill the token list by creating all the tokens from
|
||||
** the given string.
|
||||
**
|
||||
** @param lexer an empty lexer.
|
||||
*/
|
||||
void lexer_build(struct lexer *lexer);
|
||||
|
||||
/**
|
||||
** @brief Return the next token without consume it.
|
||||
**
|
||||
** \return the next token from the input stream
|
||||
** @param lexer the lexer to lex from
|
||||
*/
|
||||
struct lexer_token *lexer_peek(struct lexer *lexer);
|
||||
|
||||
/**
|
||||
** @brief Return and consume the next token from the input stream.
|
||||
**
|
||||
** \return the next token from the input stream
|
||||
** @param lexer the lexer to lex from
|
||||
*/
|
||||
struct lexer_token *lexer_pop(struct lexer *lexer);
|
||||
|
||||
/**
|
||||
** @brief Append a new token to the token_list of the lexer.
|
||||
**
|
||||
** @param lexer the lexer.
|
||||
** @param token the token to append.
|
||||
*/
|
||||
void lexer_append(struct lexer *lexer, struct lexer_token *token);
|
||||
|
||||
/**
|
||||
** @brief Free a lexer, all the tokens and tokens values.
|
||||
**
|
||||
** @param lexer the lexer.
|
||||
*/
|
||||
void lexer_free(struct lexer *lexer);
|
||||
|
||||
/**
|
||||
* @brief Set token as head of the token_list.
|
||||
*
|
||||
* @param lexer the lexer.
|
||||
* @param token the new head of the token_list.
|
||||
*/
|
||||
void lexer_go_back(struct lexer *lexer, struct lexer_token *token);
|
||||
|
||||
/**
|
||||
* @brief Print each token in the token_list.
|
||||
*
|
||||
* @param lexer a lexer.
|
||||
*/
|
||||
void lexer_print(struct lexer *lexer);
|
||||
|
||||
#endif // !LEXER_H
|
|
@ -0,0 +1,56 @@
|
|||
#include "lexer.h"
|
||||
|
||||
static char *get_token_string(enum token_type type)
|
||||
{
|
||||
char *token_string[] = { "ERROR",
|
||||
"IF",
|
||||
"ELSE",
|
||||
"ELIF",
|
||||
"FI",
|
||||
"THEN",
|
||||
"DO",
|
||||
"DONE",
|
||||
"WHILE",
|
||||
"UNTIL",
|
||||
"FOR",
|
||||
"IN",
|
||||
"&&",
|
||||
"||",
|
||||
"SEMICOLON",
|
||||
"NEWLINE",
|
||||
"REDIR",
|
||||
"IONUMBER",
|
||||
"PIPE",
|
||||
"NOT",
|
||||
"ASSIGNMENT_WORD",
|
||||
"PARENTHESIS_OPEN",
|
||||
"SUBSTITUTION",
|
||||
"BACKTICK",
|
||||
"PARENTHESIS_CLOSE",
|
||||
"BRACE_OPEN",
|
||||
"BRACE_CLOSE",
|
||||
"DOLLAR",
|
||||
"CASE",
|
||||
"ESAC",
|
||||
"WORD",
|
||||
"WORD_DOUBLE_QUOTE",
|
||||
"WORD_SINGLE_QUOTE",
|
||||
"EOF",
|
||||
"SPACE" };
|
||||
return token_string[type];
|
||||
}
|
||||
|
||||
void lexer_print(struct lexer *lexer)
|
||||
{
|
||||
printf("lexer output: ");
|
||||
struct lexer_token *token = lexer->tokens;
|
||||
while (token)
|
||||
{
|
||||
if (token->type != TOKEN_REDIR)
|
||||
printf("%s ", get_token_string(token->type));
|
||||
else
|
||||
printf("%s ", token->value);
|
||||
token = token->next;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef LEXER_TOOLS_H
|
||||
#define LEXER_TOOLS_H
|
||||
|
||||
#include "lexer.h"
|
||||
#include "spaces.h"
|
||||
|
||||
void create_and_append_token(struct lexer *lexer, enum token_type type,
|
||||
char *value);
|
||||
|
||||
char **split_in_words(char *input);
|
||||
|
||||
enum token_type get_keyword(char *word);
|
||||
|
||||
bool is_keyword(char *word);
|
||||
|
||||
bool is_int(char *word);
|
||||
|
||||
void process_export(struct lexer *lexer);
|
||||
|
||||
void process_alias(struct lexer_token *prev, struct lexer_token *head,
|
||||
struct lexer *lexer);
|
||||
|
||||
struct lexer_alias *get_alias(char *name);
|
||||
|
||||
void lexer_append_alias(struct lexer *lexer, struct lexer_alias *alias);
|
||||
|
||||
void process_unalias(struct lexer_token *prev, struct lexer_token *head,
|
||||
struct lexer *lexer);
|
||||
|
||||
#endif // !LEXER_TOOLS_H
|
|
@ -0,0 +1,52 @@
|
|||
#include "spaces.h"
|
||||
|
||||
static bool is_word(enum token_type type)
|
||||
{
|
||||
int res =
|
||||
type >= TOKEN_WORD_DOUBLE_QUOTE && type <= TOKEN_WORD_SINGLE_QUOTE;
|
||||
return res;
|
||||
}
|
||||
|
||||
void process_spaces(struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *token = lexer->tokens;
|
||||
while (token
|
||||
&& (token->type == TOKEN_SPACE || token->type == TOKEN_NEWLINE))
|
||||
{
|
||||
struct lexer_token *next = token->next;
|
||||
lexer_token_free(token);
|
||||
token = next;
|
||||
lexer->tokens = token;
|
||||
}
|
||||
while (token && token->type != TOKEN_EOF)
|
||||
{
|
||||
struct lexer_token *next = token->next;
|
||||
if (!next)
|
||||
{
|
||||
token = next;
|
||||
continue;
|
||||
}
|
||||
if (next->type == TOKEN_SPACE)
|
||||
{
|
||||
while (next->type == TOKEN_SPACE)
|
||||
{
|
||||
struct lexer_token *tmp = next->next;
|
||||
token->next = next->next;
|
||||
free(next->value);
|
||||
free(next);
|
||||
next = tmp;
|
||||
}
|
||||
}
|
||||
else if (is_word(token->type) && is_word(next->type)
|
||||
&& next->type == token->type)
|
||||
{
|
||||
token->next = next->next;
|
||||
token->value = realloc(
|
||||
token->value, strlen(token->value) + strlen(next->value) + 2);
|
||||
strcat(token->value, next->value);
|
||||
free(next->value);
|
||||
free(next);
|
||||
}
|
||||
token = token->next;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef SPACES_H
|
||||
#define SPACES_H
|
||||
|
||||
#include "lexer.h"
|
||||
|
||||
/**
|
||||
* @brief Remove TOKEN_SPACE tokens from the token list.
|
||||
*
|
||||
* @param lexer the lexer.
|
||||
*/
|
||||
void process_spaces(struct lexer *lexer);
|
||||
|
||||
#endif // !SPACES_H
|
|
@ -0,0 +1,54 @@
|
|||
#include "lexer_tools.h"
|
||||
|
||||
char **split_in_words(char *input)
|
||||
{
|
||||
char *new = strdup(input);
|
||||
char *save = new;
|
||||
char **words = NULL;
|
||||
int words_nb = 0;
|
||||
char *word = NULL;
|
||||
|
||||
int i = 0;
|
||||
int word_len = 0;
|
||||
char quote = 0;
|
||||
while (input[i])
|
||||
{
|
||||
if (input[i] == '\'' || input[i] == '"')
|
||||
{
|
||||
if (quote == input[i])
|
||||
quote = 0;
|
||||
else if (!quote)
|
||||
quote = input[i];
|
||||
}
|
||||
if ((input[i] == ' ' || input[i] == '\t') && !quote)
|
||||
{
|
||||
if (word_len > 0)
|
||||
{
|
||||
word[word_len] = 0;
|
||||
words = realloc(words, sizeof(char *) * (words_nb + 2));
|
||||
words[words_nb] = word;
|
||||
words_nb++;
|
||||
word = NULL;
|
||||
word_len = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
word = realloc(word, sizeof(char) * (word_len + 2));
|
||||
word[word_len++] = input[i++];
|
||||
}
|
||||
}
|
||||
if (word_len > 0)
|
||||
{
|
||||
word[word_len] = 0;
|
||||
words = realloc(words, sizeof(char *) * (words_nb + 2));
|
||||
words[words_nb] = word;
|
||||
words_nb++;
|
||||
}
|
||||
if (!words)
|
||||
words = realloc(words, sizeof(char *));
|
||||
words[words_nb] = NULL;
|
||||
free(save);
|
||||
return words;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#include "loop_stack.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* if (continue) -> ast for else -> normal exec
|
||||
for check if CONTINUE -> get var list and index else -> new for add for in
|
||||
stack end of loop unstack val unset continue
|
||||
*/
|
||||
|
||||
int push_loop(struct shell *sh, struct ast *ast)
|
||||
{
|
||||
struct loop_stack *new = calloc(1, sizeof(struct loop_stack));
|
||||
if (!new)
|
||||
return 1;
|
||||
new->loop = ast;
|
||||
new->next = sh->loop_stack;
|
||||
sh->loop_stack = new;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ast *get_ast_loop(struct shell *sh)
|
||||
{
|
||||
if (sh->loop_stack)
|
||||
return sh->loop_stack->loop;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pop_loop(struct shell *sh)
|
||||
{
|
||||
struct loop_stack *tmp = sh->loop_stack;
|
||||
if (tmp)
|
||||
{
|
||||
sh->loop_stack = tmp->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void free_loop(struct shell *sh)
|
||||
{
|
||||
while (sh->loop_stack)
|
||||
{
|
||||
pop_loop(sh);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef LOOP_STACK_H
|
||||
#define LOOP_STACK_H
|
||||
|
||||
#include "../bsh.h"
|
||||
#include "ast.h"
|
||||
|
||||
int push_loop(struct shell *sh, struct ast *ast);
|
||||
struct ast *get_ast_loop(struct shell *sh);
|
||||
void pop_loop(struct shell *sh);
|
||||
void free_loop(struct shell *sh);
|
||||
|
||||
#endif /* !LOOP_STACK_H */
|
|
@ -0,0 +1,43 @@
|
|||
#include "ast.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct ast *ast_new(enum ast_type type)
|
||||
{
|
||||
struct ast *new = calloc(1, sizeof(struct ast));
|
||||
new->type = type;
|
||||
return new;
|
||||
}
|
||||
|
||||
void ast_free(struct ast *ast)
|
||||
{
|
||||
if (ast == NULL)
|
||||
return;
|
||||
|
||||
if (ast->type == AST_COMMAND || ast->type == AST_FOR
|
||||
|| ast->type == AST_ASSIGNMENT || ast->type == AST_CASE
|
||||
|| ast->type == AST_CASE_SWITCH)
|
||||
{
|
||||
free(ast->value);
|
||||
free(ast->enclosure);
|
||||
}
|
||||
else if (ast->type == AST_REDIR)
|
||||
{
|
||||
int i = 0;
|
||||
while (ast->value[i])
|
||||
free(ast->value[i++]);
|
||||
free(ast->value);
|
||||
free(ast->enclosure);
|
||||
}
|
||||
|
||||
ast_free(ast->left_child);
|
||||
ast->left_child = NULL;
|
||||
|
||||
ast_free(ast->right_child);
|
||||
ast->right_child = NULL;
|
||||
|
||||
ast_free(ast->condition);
|
||||
ast->condition = NULL;
|
||||
|
||||
free(ast);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef AST_H
|
||||
#define AST_H
|
||||
|
||||
enum ast_type
|
||||
{
|
||||
AST_COMMAND = 0,
|
||||
AST_LIST, // < ('\n')* and_or ((';'|'\n') ('\n')* and_or)* [(';'|'\n')
|
||||
// ('\n')*]
|
||||
AST_IF,
|
||||
AST_FOR,
|
||||
AST_WHILE,
|
||||
AST_UNTIL,
|
||||
AST_CASE,
|
||||
AST_PIPE,
|
||||
AST_OR,
|
||||
AST_AND,
|
||||
AST_NOT,
|
||||
AST_REDIR,
|
||||
AST_FUNC,
|
||||
AST_EOF,
|
||||
AST_ASSIGNMENT,
|
||||
AST_CMD_SUBSTITUTION,
|
||||
AST_SUBSHELL,
|
||||
AST_CASE_SWITCH
|
||||
};
|
||||
|
||||
enum quotes
|
||||
{
|
||||
Q_NONE = 0,
|
||||
Q_DOUBLE,
|
||||
Q_SINGLE,
|
||||
Q_BACKTICK
|
||||
};
|
||||
|
||||
struct ast
|
||||
{
|
||||
enum ast_type type;
|
||||
char **value;
|
||||
char *var_name;
|
||||
enum quotes *enclosure;
|
||||
struct ast *left_child;
|
||||
struct ast *right_child;
|
||||
struct ast *condition;
|
||||
};
|
||||
|
||||
/**
|
||||
** \brief Allocate a new ast with the given type
|
||||
*/
|
||||
struct ast *ast_new(enum ast_type type);
|
||||
|
||||
/**
|
||||
** \brief Recursively free the given ast
|
||||
*/
|
||||
void ast_free(struct ast *ast);
|
||||
|
||||
#endif /* !AST_H */
|
|
@ -0,0 +1,37 @@
|
|||
#include "parser.h"
|
||||
|
||||
enum parser_status parse_funcdec(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
|
||||
*ast = ast_new(AST_FUNC);
|
||||
|
||||
// Try WORD
|
||||
if (tok->type != TOKEN_WORD)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
|
||||
(*ast)->var_name = tok->value;
|
||||
lexer_pop(lexer); // token WORD
|
||||
|
||||
// Try (
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_PARENTHESIS_OPEN)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token (
|
||||
|
||||
// Try )
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_PARENTHESIS_CLOSE)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token )
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
|
||||
enum parser_status status_shell_cmd = parse_shell_command(ast, lexer);
|
||||
if (status_shell_cmd == PARSER_ERROR)
|
||||
return handle_parser_error(status_shell_cmd, ast);
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
#include "parser.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int evaluate_ast(struct ast *ast);
|
||||
|
||||
enum parser_status handle_parser_error(enum parser_status status,
|
||||
struct ast **res)
|
||||
{
|
||||
ast_free(*res);
|
||||
*res = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
static int display_parser_error(struct ast **res)
|
||||
{
|
||||
warnx("Parser: unexpected token");
|
||||
ast_free(*res);
|
||||
*res = NULL;
|
||||
shell->return_code = 2;
|
||||
return 2;
|
||||
}
|
||||
|
||||
struct string_array_with_quotes merge_values(char **values_1, char **values_2,
|
||||
enum quotes *q_1, enum quotes *q_2)
|
||||
{
|
||||
int length_1 = 0;
|
||||
while (values_1 && values_1[length_1] != NULL)
|
||||
length_1++;
|
||||
|
||||
int length_2 = 0;
|
||||
while (values_2 && values_2[length_2] != NULL)
|
||||
length_2++;
|
||||
|
||||
values_1 = realloc(values_1, (length_1 + 1 + length_2) * sizeof(char *));
|
||||
q_1 = realloc(q_1, (length_1 + length_2) * sizeof(enum quotes));
|
||||
for (int i = length_1; i < length_2 + length_1; i++)
|
||||
{
|
||||
values_1[i] = values_2[i - length_1];
|
||||
q_1[i] = q_2[i - length_1];
|
||||
}
|
||||
values_1[length_1 + length_2] = NULL;
|
||||
|
||||
struct string_array_with_quotes res = { values_1, q_1 };
|
||||
return res;
|
||||
}
|
||||
|
||||
static enum parser_status add_eof_node(struct ast **ast)
|
||||
{
|
||||
struct ast *cur = *ast;
|
||||
while (cur && cur->left_child && cur->type == AST_LIST)
|
||||
{
|
||||
cur = cur->left_child;
|
||||
}
|
||||
if (!cur || cur->type != AST_LIST)
|
||||
return PARSER_ERROR;
|
||||
|
||||
cur->left_child = ast_new(AST_EOF);
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
int parse_input(char *input, struct ast **res)
|
||||
{
|
||||
struct lexer *lex = lexer_create(input);
|
||||
lexer_build(lex);
|
||||
|
||||
if (shell->exit)
|
||||
{
|
||||
lexer_free(lex);
|
||||
shell->exit = false;
|
||||
return 2;
|
||||
}
|
||||
|
||||
struct ast *ast = ast_new(AST_LIST);
|
||||
|
||||
// Try EOF
|
||||
struct lexer_token *end = lexer_peek(lex);
|
||||
if (end->type == TOKEN_EOF || end->type == TOKEN_NEWLINE)
|
||||
{
|
||||
ast_free(ast);
|
||||
lexer_free(lex);
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
// Try compound_list EOF
|
||||
if (parse_compound_list(&ast, lex) == PARSER_OK)
|
||||
{
|
||||
struct lexer_token *next = lexer_peek(lex);
|
||||
if (next->type == TOKEN_EOF || next->type == TOKEN_NEWLINE)
|
||||
{
|
||||
if (add_eof_node(&ast) == PARSER_ERROR)
|
||||
return display_parser_error(&ast);
|
||||
|
||||
if (shell->pretty_print)
|
||||
pretty_print(ast);
|
||||
if (!res)
|
||||
{
|
||||
int res_eval = evaluate_ast(ast);
|
||||
shell->return_code = res_eval;
|
||||
ast_free(ast);
|
||||
lexer_free(lex);
|
||||
}
|
||||
else
|
||||
{
|
||||
*res = ast;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
lexer_free(lex);
|
||||
return display_parser_error(&ast);
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include "../bsh.h"
|
||||
#include "../lexer/lexer.h"
|
||||
#include "ast.h"
|
||||
|
||||
enum parser_status
|
||||
{
|
||||
PARSER_OK = 0,
|
||||
PARSER_ERROR
|
||||
};
|
||||
|
||||
struct string_array_with_quotes
|
||||
{
|
||||
char **value;
|
||||
enum quotes *q;
|
||||
};
|
||||
|
||||
extern struct shell *shell;
|
||||
|
||||
enum parser_status handle_parser_error(enum parser_status status,
|
||||
struct ast **res);
|
||||
|
||||
struct string_array_with_quotes merge_values(char **values_1, char **values_2,
|
||||
enum quotes *q_1,
|
||||
enum quotes *q_2);
|
||||
|
||||
void pretty_print(struct ast *ast);
|
||||
|
||||
/**
|
||||
** @brief Check if input grammar rule is respected
|
||||
** >> input: compound_list EOF | compound_list 'newline' | 'newline' | EOF;
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
int parse_input(char *input, struct ast **res);
|
||||
|
||||
/**
|
||||
** @brief Check if compound_list grammar rule is respected
|
||||
** >> compound_list: and_or (';' and_or)* [';'];
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_compound_list(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if simple_command grammar rule is respected
|
||||
** >> simple_command: WORD+
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_simple_command(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if shell_command grammar rule is respected
|
||||
** >> shell_command: '{' compound_list '}'
|
||||
** | rule_for
|
||||
** | rule_while
|
||||
** | rule_until
|
||||
** | rule_if
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_shell_command(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if command grammar rule is respected
|
||||
** >> command: simple_command | shell_command
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_command(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if pipeline grammar rule is respected
|
||||
** >> pipeline: command ('|' ('newline')* command)*
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_pipeline(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if and_or grammar rule is respected
|
||||
** >> and_or: pipeline (('&&'|'||') ('newline')* pipeline)*
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_and_or(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if rule_if grammar rule is respected
|
||||
** >> rule_if: If compound_list Then compound_list [else_clause] Fi
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_rule_if(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if else_clause grammar rule is respected
|
||||
** >> else_clause: Else compound_list | Elif compound_list Then compound_list
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_else_clause(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if rule_case grammar rule is respected
|
||||
** >> rule_case: Case WORD ('newline')* 'in' ('newline')* [case_clause] Esac
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_rule_case(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if case_clause grammar rule is respected
|
||||
** >> case_clause: case_item (';;' ('newline')* case_item)* [;;] ('newline')*
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_case_clause(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if case_item grammar rule is respected
|
||||
** >> case_item: ['('] WORD ('|' WORD)* ')' ('newline')* [ compound_list ]
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_case_item(struct ast **ast, struct lexer *l);
|
||||
|
||||
/**
|
||||
** @brief Check if redirection grammar rule is respected
|
||||
** >> redirection: [IONUMBER] '>' WORD
|
||||
** | [IONUMBER] '<' WORD
|
||||
** | [IONUMBER] '>&' WORD
|
||||
** | [IONUMBER] '<&' WORD
|
||||
** | [IONUMBER] '>>' WORD
|
||||
** | [IONUMBER] '<>' WORD
|
||||
** | [IONUMBER] '>|' WORD
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_redirection(struct ast **ast, struct lexer *lexer);
|
||||
|
||||
/**
|
||||
** @brief Check if rule_while grammar rule is respected
|
||||
** >> rule_while: While compound_list do_group
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_rule_while(struct ast **ast, struct lexer *lexer);
|
||||
|
||||
/**
|
||||
** @brief Check if rule_until grammar rule is respected
|
||||
** >> rule_until: Until compound_list do_group
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_rule_until(struct ast **ast, struct lexer *lexer);
|
||||
|
||||
/**
|
||||
** @brief Check if do_group grammar rule is respected
|
||||
** >> do_group: Do compound_list Done
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_do_group(struct ast **ast, struct lexer *lexer);
|
||||
|
||||
/**
|
||||
** @brief Check if for grammar rule is respected
|
||||
** >> for: For WORD ([';']|[('newline')* 'in' (WORD)* (';'|'newline')])
|
||||
** ('newline')* do_group
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_for(struct ast **ast, struct lexer *lexer);
|
||||
|
||||
/**
|
||||
** @brief Check if funcdec grammar rule is respected
|
||||
** >> funcdec: WORD '(' ')' ('newline')* shell_command
|
||||
**
|
||||
** @param ast the general ast to update
|
||||
** @param lexer the lexer to read tokens from
|
||||
** @return enum parser_status - current parser status
|
||||
**/
|
||||
enum parser_status parse_funcdec(struct ast **ast, struct lexer *lexer);
|
||||
|
||||
#endif // !PARSER_H
|
|
@ -0,0 +1,498 @@
|
|||
#include "parser.h"
|
||||
|
||||
enum parser_status parse_redirection(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
char *fd = NULL;
|
||||
char *type = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
// Try [IONUMBER]
|
||||
if (tok->type == TOKEN_IONUMBER)
|
||||
{
|
||||
len = strlen(tok->value);
|
||||
fd = realloc(fd, len + 1);
|
||||
fd = strcpy(fd, tok->value);
|
||||
fd[len] = '\0';
|
||||
lexer_pop(lexer);
|
||||
}
|
||||
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_REDIR)
|
||||
{
|
||||
free(fd);
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
}
|
||||
|
||||
len = strlen(tok->value);
|
||||
type = calloc(len + 1, sizeof(char));
|
||||
type = strcpy(type, tok->value);
|
||||
type[len] = '\0';
|
||||
char **redir_value = NULL;
|
||||
*ast = ast_new(AST_REDIR);
|
||||
if (fd)
|
||||
{
|
||||
redir_value = calloc(3, sizeof(char *));
|
||||
redir_value[0] = fd;
|
||||
redir_value[1] = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
redir_value = calloc(2, sizeof(char *));
|
||||
redir_value[0] = type;
|
||||
}
|
||||
(*ast)->value = redir_value;
|
||||
enum quotes *enclosure = calloc(2, sizeof(enum quotes));
|
||||
enclosure[0] = Q_SINGLE;
|
||||
enclosure[1] = Q_SINGLE;
|
||||
(*ast)->enclosure = enclosure;
|
||||
|
||||
lexer_pop(lexer);
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_WORD && tok->type != TOKEN_WORD_SINGLE_QUOTE
|
||||
&& tok->type != TOKEN_WORD_DOUBLE_QUOTE)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
|
||||
struct ast *ast_word = ast_new(AST_COMMAND);
|
||||
char **word_value = calloc(2, sizeof(char *));
|
||||
word_value[0] = tok->value;
|
||||
enum quotes *word_q = calloc(1, sizeof(enum quotes));
|
||||
if (tok->type == TOKEN_WORD)
|
||||
word_q[0] = Q_NONE;
|
||||
else if (tok->type == TOKEN_WORD_SINGLE_QUOTE)
|
||||
word_q[0] = Q_SINGLE;
|
||||
else if (tok->type == TOKEN_WORD_DOUBLE_QUOTE)
|
||||
word_q[0] = Q_DOUBLE;
|
||||
|
||||
ast_word->value = word_value;
|
||||
ast_word->enclosure = word_q;
|
||||
(*ast)->right_child = ast_word;
|
||||
|
||||
lexer_pop(lexer);
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if simple_command grammar rule is respected
|
||||
* >> prefix: ASSIGNMENT_WORD | redirection
|
||||
*
|
||||
* @param ast the general ast to update
|
||||
* @param lexer the lexer to read tokens from
|
||||
* @return enum parser_status - current parser status
|
||||
*/
|
||||
static enum parser_status parse_prefix(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
// Try ASSIGNMENT_WORD
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_ASSIGNMENT_WORD)
|
||||
{
|
||||
*ast = ast_new(AST_ASSIGNMENT);
|
||||
(*ast)->var_name = tok->value;
|
||||
|
||||
lexer_pop(lexer);
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
// Try redirection
|
||||
enum parser_status status_redir = parse_redirection(ast, lexer);
|
||||
if (status_redir == PARSER_ERROR)
|
||||
return handle_parser_error(status_redir, ast);
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
static enum parser_status parse_element(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
// Try WORD
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_WORD || tok->type == TOKEN_WORD_DOUBLE_QUOTE
|
||||
|| tok->type == TOKEN_WORD_SINGLE_QUOTE)
|
||||
{
|
||||
*ast = ast_new(AST_COMMAND);
|
||||
char **value = calloc(2, sizeof(char *));
|
||||
value[0] = tok->value;
|
||||
|
||||
enum quotes *enclosure = calloc(1, sizeof(enum quotes));
|
||||
if (tok->type == TOKEN_WORD)
|
||||
enclosure[0] = Q_NONE;
|
||||
else if (tok->type == TOKEN_WORD_DOUBLE_QUOTE)
|
||||
enclosure[0] = Q_DOUBLE;
|
||||
else
|
||||
enclosure[0] = Q_SINGLE;
|
||||
|
||||
(*ast)->value = value;
|
||||
(*ast)->enclosure = enclosure;
|
||||
|
||||
lexer_pop(lexer);
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
// Try redirection
|
||||
enum parser_status status_redir = parse_redirection(ast, lexer);
|
||||
if (status_redir == PARSER_ERROR)
|
||||
return handle_parser_error(status_redir, ast);
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_simple_command(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
bool first_prefix = true;
|
||||
struct lexer_token *save_tok = lexer_peek(lexer);
|
||||
bool is_assignment = false;
|
||||
|
||||
// Try (prefix)*
|
||||
struct ast *cur_prefix = NULL;
|
||||
while (true)
|
||||
{
|
||||
struct ast *ast_prefix = NULL;
|
||||
|
||||
// Try prefix
|
||||
enum parser_status status_prefix = parse_prefix(&ast_prefix, lexer);
|
||||
if (status_prefix == PARSER_ERROR)
|
||||
{
|
||||
lexer_go_back(lexer, save_tok);
|
||||
ast_free(ast_prefix);
|
||||
break;
|
||||
}
|
||||
|
||||
//? If we saw a variable and we get something else after other than a
|
||||
//? word, then the assignment is incorrect but we go on and it will be
|
||||
//? catched while executing
|
||||
//! Should maybe be an error clause if is_assignment is already set to
|
||||
//! true
|
||||
is_assignment = false;
|
||||
|
||||
if (ast_prefix->type == AST_ASSIGNMENT)
|
||||
is_assignment = true;
|
||||
|
||||
if (first_prefix)
|
||||
{
|
||||
*ast = ast_prefix;
|
||||
first_prefix = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ast_prefix->left_child = cur_prefix->right_child;
|
||||
cur_prefix->right_child = ast_prefix;
|
||||
}
|
||||
cur_prefix = ast_prefix;
|
||||
save_tok = lexer_peek(lexer);
|
||||
}
|
||||
|
||||
save_tok = lexer_peek(lexer);
|
||||
|
||||
// Try (element)+
|
||||
bool first_element = true;
|
||||
bool first_is_command = false;
|
||||
while (true)
|
||||
{
|
||||
struct ast *ast_element = NULL;
|
||||
enum parser_status status_element = parse_element(&ast_element, lexer);
|
||||
if (status_element == PARSER_ERROR)
|
||||
{
|
||||
lexer_go_back(lexer, save_tok);
|
||||
ast_free(ast_element);
|
||||
break;
|
||||
}
|
||||
save_tok = lexer_peek(lexer);
|
||||
|
||||
// Test if element is WORD and parse it as argument of the previous
|
||||
// command
|
||||
if (ast_element->type == AST_COMMAND)
|
||||
{
|
||||
//? following words are consider in variable assignment
|
||||
if (is_assignment)
|
||||
{
|
||||
struct string_array_with_quotes res =
|
||||
merge_values(cur_prefix->value, ast_element->value,
|
||||
cur_prefix->enclosure, ast_element->enclosure);
|
||||
cur_prefix->value = res.value;
|
||||
cur_prefix->enclosure = res.q;
|
||||
ast_free(ast_element);
|
||||
|
||||
first_is_command = true; //! To be sure about
|
||||
continue;
|
||||
}
|
||||
|
||||
struct ast *last_command = NULL;
|
||||
if ((first_element && first_prefix) || first_is_command)
|
||||
{
|
||||
first_is_command = true;
|
||||
if (cur_prefix == NULL)
|
||||
{
|
||||
cur_prefix = ast_element;
|
||||
first_element = false;
|
||||
continue;
|
||||
}
|
||||
last_command = cur_prefix;
|
||||
}
|
||||
else
|
||||
last_command = cur_prefix->right_child;
|
||||
|
||||
//? Merge char **value and enum quotes *enclosure from last_command
|
||||
// and ast_element
|
||||
struct string_array_with_quotes res =
|
||||
merge_values(last_command->value, ast_element->value,
|
||||
last_command->enclosure, ast_element->enclosure);
|
||||
last_command->value = res.value;
|
||||
last_command->enclosure = res.q;
|
||||
ast_free(ast_element);
|
||||
}
|
||||
else
|
||||
{
|
||||
is_assignment = false;
|
||||
if ((first_element && first_prefix) || first_is_command)
|
||||
{
|
||||
*ast = ast_element;
|
||||
if (first_is_command)
|
||||
(*ast)->left_child = cur_prefix;
|
||||
}
|
||||
else
|
||||
{
|
||||
ast_element->left_child = cur_prefix->right_child;
|
||||
cur_prefix->right_child = ast_element;
|
||||
}
|
||||
first_is_command = false;
|
||||
cur_prefix = ast_element;
|
||||
}
|
||||
first_element = false;
|
||||
}
|
||||
|
||||
if (first_prefix)
|
||||
{
|
||||
//? first_element needs to be false, we are in (prefix)* (element)+
|
||||
if (first_element)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
}
|
||||
if (first_is_command)
|
||||
*ast = cur_prefix;
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_shell_command(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *save_tok = lexer_peek(lexer);
|
||||
|
||||
// Try {
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_BRACE_OPEN)
|
||||
{
|
||||
lexer_pop(lexer); // token {
|
||||
|
||||
// Try compound_list
|
||||
struct ast *ast_list = ast_new(AST_LIST);
|
||||
enum parser_status status_compound_list =
|
||||
parse_compound_list(&ast_list, lexer);
|
||||
if (status_compound_list == PARSER_OK)
|
||||
{
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_BRACE_CLOSE)
|
||||
{
|
||||
if (ast != NULL && *ast != NULL)
|
||||
(*ast)->left_child = ast_list;
|
||||
else
|
||||
{
|
||||
*ast = ast_new(AST_FUNC);
|
||||
(*ast)->var_name = NULL;
|
||||
(*ast)->left_child = ast_list;
|
||||
}
|
||||
|
||||
lexer_pop(lexer); // token }
|
||||
return PARSER_OK;
|
||||
}
|
||||
}
|
||||
ast_free(ast_list);
|
||||
|
||||
lexer_go_back(lexer, save_tok);
|
||||
}
|
||||
|
||||
// Try ( or $(
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_PARENTHESIS_OPEN
|
||||
|| tok->type == TOKEN_SUBSTITUTION_OPEN || tok->type == TOKEN_BACKTICK)
|
||||
{
|
||||
struct lexer_token *tok_parenthesis = tok;
|
||||
lexer_pop(lexer); // token (
|
||||
|
||||
// Try compound_list
|
||||
struct ast *ast_list = ast_new(AST_LIST);
|
||||
enum parser_status status_compound_list =
|
||||
parse_compound_list(&ast_list, lexer);
|
||||
if (status_compound_list == PARSER_OK)
|
||||
{
|
||||
tok = lexer_peek(lexer);
|
||||
if ((tok_parenthesis->type != TOKEN_BACKTICK
|
||||
&& tok->type == TOKEN_PARENTHESIS_CLOSE)
|
||||
|| (tok_parenthesis->type == TOKEN_BACKTICK
|
||||
&& tok->type == TOKEN_BACKTICK))
|
||||
{
|
||||
if (ast != NULL && *ast != NULL)
|
||||
(*ast)->left_child = ast_list;
|
||||
else
|
||||
{
|
||||
*ast =
|
||||
ast_new(tok_parenthesis->type == TOKEN_SUBSTITUTION_OPEN
|
||||
|| tok->type == TOKEN_BACKTICK
|
||||
? AST_CMD_SUBSTITUTION
|
||||
: AST_SUBSHELL);
|
||||
(*ast)->left_child = ast_list;
|
||||
}
|
||||
|
||||
lexer_pop(lexer); // token )
|
||||
return PARSER_OK;
|
||||
}
|
||||
}
|
||||
ast_free(ast_list);
|
||||
|
||||
lexer_go_back(lexer, save_tok);
|
||||
}
|
||||
|
||||
// Try rule_for
|
||||
enum parser_status status_command = parse_for(ast, lexer);
|
||||
if (status_command == PARSER_OK)
|
||||
return PARSER_OK;
|
||||
|
||||
lexer_go_back(lexer, save_tok);
|
||||
|
||||
// Try rule_while
|
||||
status_command = parse_rule_while(ast, lexer);
|
||||
if (status_command == PARSER_OK)
|
||||
return PARSER_OK;
|
||||
|
||||
lexer_go_back(lexer, save_tok);
|
||||
|
||||
// Try rule_until
|
||||
status_command = parse_rule_until(ast, lexer);
|
||||
if (status_command == PARSER_OK)
|
||||
return PARSER_OK;
|
||||
|
||||
lexer_go_back(lexer, save_tok);
|
||||
|
||||
// Try rule_case
|
||||
status_command = parse_rule_case(ast, lexer);
|
||||
if (status_command == PARSER_OK)
|
||||
return PARSER_OK;
|
||||
|
||||
lexer_go_back(lexer, save_tok);
|
||||
|
||||
// Try rule_if
|
||||
status_command = parse_rule_if(ast, lexer);
|
||||
if (status_command == PARSER_OK)
|
||||
return PARSER_OK;
|
||||
|
||||
lexer_go_back(lexer, save_tok);
|
||||
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
}
|
||||
|
||||
enum parser_status parse_command(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
enum parser_status status;
|
||||
|
||||
// Save of current state of lexer because of | in grammar
|
||||
struct lexer_token *save_tok = lexer_peek(lexer);
|
||||
|
||||
// Try fundec
|
||||
struct ast *ast_fundec = NULL;
|
||||
if ((status = parse_funcdec(&ast_fundec, lexer)) == PARSER_OK)
|
||||
{
|
||||
*ast = ast_fundec;
|
||||
bool first_redir = true;
|
||||
struct ast *cur_redir = NULL;
|
||||
|
||||
// Try (redirection)*
|
||||
while (true)
|
||||
{
|
||||
struct lexer_token *save_tok = lexer_peek(lexer);
|
||||
|
||||
// Try redirection
|
||||
struct ast *ast_redir = NULL;
|
||||
enum parser_status status_redir =
|
||||
parse_redirection(&ast_redir, lexer);
|
||||
if (status_redir == PARSER_ERROR)
|
||||
{
|
||||
lexer_go_back(lexer, save_tok);
|
||||
break;
|
||||
}
|
||||
|
||||
if (first_redir)
|
||||
{
|
||||
ast_redir->left_child = *ast;
|
||||
*ast = ast_redir;
|
||||
first_redir = false;
|
||||
cur_redir = *ast;
|
||||
}
|
||||
else
|
||||
{
|
||||
ast_redir->left_child = cur_redir->right_child;
|
||||
cur_redir->right_child = ast_redir;
|
||||
cur_redir = cur_redir->right_child;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
ast_free(ast_fundec);
|
||||
|
||||
lexer_go_back(lexer, save_tok);
|
||||
|
||||
// Try simple_command
|
||||
struct ast *ast_simple_command = NULL;
|
||||
if ((status = parse_simple_command(&ast_simple_command, lexer))
|
||||
== PARSER_OK)
|
||||
{
|
||||
*ast = ast_simple_command;
|
||||
return status;
|
||||
}
|
||||
ast_free(ast_simple_command);
|
||||
|
||||
// Go back to lexer's state before simple_command exec
|
||||
lexer_go_back(lexer, save_tok);
|
||||
|
||||
// Try shell_command
|
||||
struct ast *ast_shell_command = NULL;
|
||||
if ((status = parse_shell_command(&ast_shell_command, lexer)) == PARSER_OK)
|
||||
{
|
||||
*ast = ast_shell_command;
|
||||
bool first_redir = true;
|
||||
struct ast *cur_redir = NULL;
|
||||
|
||||
// Try (redirection)*
|
||||
while (true)
|
||||
{
|
||||
struct lexer_token *save_tok = lexer_peek(lexer);
|
||||
|
||||
// Try redirection
|
||||
struct ast *ast_redir = NULL;
|
||||
enum parser_status status_redir =
|
||||
parse_redirection(&ast_redir, lexer);
|
||||
if (status_redir == PARSER_ERROR)
|
||||
{
|
||||
lexer_go_back(lexer, save_tok);
|
||||
break;
|
||||
}
|
||||
|
||||
if (first_redir)
|
||||
{
|
||||
ast_redir->left_child = *ast;
|
||||
*ast = ast_redir;
|
||||
first_redir = false;
|
||||
cur_redir = *ast;
|
||||
}
|
||||
else
|
||||
{
|
||||
ast_redir->left_child = cur_redir->right_child;
|
||||
cur_redir->right_child = ast_redir;
|
||||
cur_redir = cur_redir->right_child;
|
||||
}
|
||||
}
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
ast_free(ast_shell_command);
|
||||
|
||||
return PARSER_ERROR;
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
#include "parser.h"
|
||||
|
||||
enum parser_status parse_rule_if(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
// Check If
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_IF)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token IF
|
||||
|
||||
*ast = ast_new(AST_IF);
|
||||
|
||||
// Check compound_list (condition)
|
||||
struct ast *ast_condition = ast_new(AST_LIST);
|
||||
enum parser_status status_compound_list =
|
||||
parse_compound_list(&ast_condition, lexer);
|
||||
// If status is ERROR, assignment is still legal to free everything
|
||||
(*ast)->condition = ast_condition;
|
||||
if (status_compound_list == PARSER_ERROR)
|
||||
return handle_parser_error(status_compound_list, ast);
|
||||
|
||||
// Check Then
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_THEN)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token THEN
|
||||
|
||||
// Check compound_list (true block)
|
||||
struct ast *ast_true_block = ast_new(AST_LIST);
|
||||
status_compound_list = parse_compound_list(&ast_true_block, lexer);
|
||||
(*ast)->left_child = ast_true_block;
|
||||
if (status_compound_list == PARSER_ERROR)
|
||||
return handle_parser_error(status_compound_list, ast);
|
||||
|
||||
// Check First(else_clause) = {Else, Elif}
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_ELSE || tok->type == TOKEN_ELIF)
|
||||
{
|
||||
// Check else_clause
|
||||
struct ast *ast_false_block = NULL;
|
||||
status_compound_list = parse_else_clause(&ast_false_block, lexer);
|
||||
(*ast)->right_child = ast_false_block;
|
||||
if (status_compound_list == PARSER_ERROR)
|
||||
return handle_parser_error(status_compound_list, ast);
|
||||
}
|
||||
|
||||
// Check Fi
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_FI)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token FI
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_else_clause(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
// Double check First(else_clause), should always be correct but safety
|
||||
// first
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
enum parser_status status = PARSER_OK;
|
||||
if (tok->type == TOKEN_ELSE)
|
||||
{
|
||||
lexer_pop(lexer); // token ELSE
|
||||
|
||||
// Check compound_list
|
||||
*ast = ast_new(AST_LIST);
|
||||
status = parse_compound_list(ast, lexer);
|
||||
if (status == PARSER_ERROR)
|
||||
return handle_parser_error(status, ast);
|
||||
}
|
||||
else if (tok->type == TOKEN_ELIF)
|
||||
{
|
||||
lexer_pop(lexer); // token ELIF
|
||||
(*ast) = ast_new(AST_IF);
|
||||
|
||||
// Check compound_list
|
||||
struct ast *ast_elif_condition = ast_new(AST_LIST);
|
||||
status = parse_compound_list(&ast_elif_condition, lexer);
|
||||
(*ast)->condition = ast_elif_condition;
|
||||
if (status == PARSER_ERROR)
|
||||
return handle_parser_error(status, ast);
|
||||
|
||||
// Check Then
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_THEN)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token THEN
|
||||
|
||||
// Check compound_list (true block)
|
||||
struct ast *ast_true_block = ast_new(AST_LIST);
|
||||
status = parse_compound_list(&ast_true_block, lexer);
|
||||
(*ast)->left_child = ast_true_block;
|
||||
if (status == PARSER_ERROR)
|
||||
return handle_parser_error(status, ast);
|
||||
|
||||
// Check First(else_clause) = {Else, Elif}
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_ELSE || tok->type == TOKEN_ELIF)
|
||||
{
|
||||
struct ast *ast_false_block = NULL;
|
||||
status = parse_else_clause(&ast_false_block, lexer);
|
||||
(*ast)->right_child = ast_false_block;
|
||||
if (status == PARSER_ERROR)
|
||||
return handle_parser_error(status, ast);
|
||||
}
|
||||
}
|
||||
else
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_rule_case(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
|
||||
// Try Case
|
||||
if (tok->type != TOKEN_CASE)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token Case
|
||||
|
||||
// Try WORD
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_WORD && tok->type != TOKEN_WORD_DOUBLE_QUOTE
|
||||
&& tok->type != TOKEN_WORD_SINGLE_QUOTE)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
char **val = calloc(2, sizeof(char *));
|
||||
enum quotes *enclosure = calloc(1, sizeof(enum quotes));
|
||||
enclosure[0] = tok->type - TOKEN_WORD;
|
||||
val[0] = tok->value;
|
||||
lexer_pop(lexer); // token WORD
|
||||
|
||||
*ast = ast_new(AST_CASE);
|
||||
(*ast)->value = val;
|
||||
(*ast)->enclosure = enclosure;
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
|
||||
// Try in
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_IN)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token in
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
|
||||
// Check First(case_clause) = First(case_item) = {(, WORD}
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_PARENTHESIS_OPEN || tok->type == TOKEN_WORD
|
||||
|| tok->type == TOKEN_WORD_SINGLE_QUOTE
|
||||
|| tok->type == TOKEN_WORD_DOUBLE_QUOTE)
|
||||
{
|
||||
// Check case_clause
|
||||
struct ast *ast_content_block = NULL;
|
||||
enum parser_status status_case_clause =
|
||||
parse_case_clause(&ast_content_block, lexer);
|
||||
(*ast)->left_child = ast_content_block;
|
||||
if (status_case_clause == PARSER_ERROR)
|
||||
return handle_parser_error(status_case_clause, ast);
|
||||
}
|
||||
|
||||
// Try Esac
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_ESAC)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token Esac
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_case_clause(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
|
||||
// Try case_item
|
||||
enum parser_status status_case_item = parse_case_item(ast, lexer);
|
||||
if (status_case_item == PARSER_ERROR)
|
||||
return handle_parser_error(status_case_item, ast);
|
||||
|
||||
// Try (';;' ('\n')* case_item)*
|
||||
struct ast *cur_item = *ast;
|
||||
struct lexer_token *save_tok = lexer_peek(lexer);
|
||||
while (true)
|
||||
{
|
||||
tok = lexer_peek(lexer);
|
||||
save_tok = tok;
|
||||
|
||||
// Try ;
|
||||
if (tok->type != TOKEN_SEMICOLON)
|
||||
break;
|
||||
lexer_pop(lexer); // token ;
|
||||
|
||||
// Try ;
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_SEMICOLON)
|
||||
lexer_pop(lexer); // token ;
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
|
||||
// Try case_item
|
||||
enum parser_status status_case_item =
|
||||
parse_case_item(&(cur_item->right_child), lexer);
|
||||
if (status_case_item == PARSER_ERROR)
|
||||
{
|
||||
lexer_go_back(lexer, save_tok);
|
||||
break;
|
||||
}
|
||||
cur_item = cur_item->right_child;
|
||||
}
|
||||
|
||||
tok = lexer_peek(lexer);
|
||||
// Try ;;
|
||||
if (tok->type == TOKEN_SEMICOLON)
|
||||
{
|
||||
lexer_pop(lexer); // token ;
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_SEMICOLON)
|
||||
lexer_pop(lexer); // token ;
|
||||
}
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_case_item(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
|
||||
// Try [(]
|
||||
if (tok->type == TOKEN_PARENTHESIS_OPEN)
|
||||
lexer_pop(lexer); // token (
|
||||
|
||||
// Try WORD
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_WORD && tok->type != TOKEN_WORD_SINGLE_QUOTE
|
||||
&& tok->type != TOKEN_WORD_DOUBLE_QUOTE)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
|
||||
*ast = ast_new(AST_CASE_SWITCH);
|
||||
(*ast)->value = calloc(2, sizeof(char *));
|
||||
(*ast)->value[0] = tok->value;
|
||||
(*ast)->enclosure = calloc(1, sizeof(enum quotes));
|
||||
(*ast)->enclosure[0] = tok->type - TOKEN_WORD;
|
||||
size_t val_len = 1;
|
||||
lexer_pop(lexer); // token WORD
|
||||
|
||||
// Try (| WORD)*
|
||||
while (true)
|
||||
{
|
||||
// Try |
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_PIPE)
|
||||
break;
|
||||
lexer_pop(lexer); // token |
|
||||
|
||||
// Try WORD
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_WORD && tok->type != TOKEN_WORD_SINGLE_QUOTE
|
||||
&& tok->type != TOKEN_WORD_DOUBLE_QUOTE)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
|
||||
val_len++;
|
||||
(*ast)->value = realloc((*ast)->value, (val_len + 1) * sizeof(char *));
|
||||
(*ast)->value[val_len - 1] = tok->value;
|
||||
(*ast)->value[val_len] = NULL;
|
||||
(*ast)->enclosure =
|
||||
realloc((*ast)->enclosure, val_len * sizeof(char *));
|
||||
(*ast)->enclosure[val_len - 1] = tok->type - TOKEN_WORD;
|
||||
lexer_pop(lexer); // token WORD
|
||||
}
|
||||
|
||||
// Try )
|
||||
if (tok->type != TOKEN_PARENTHESIS_CLOSE)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token )
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
|
||||
// Try [compound_list]
|
||||
struct lexer_token *save_tok = lexer_peek(lexer);
|
||||
struct ast *ast_compound_list = ast_new(AST_LIST);
|
||||
enum parser_status status_compound_list =
|
||||
parse_compound_list(&ast_compound_list, lexer);
|
||||
if (status_compound_list == PARSER_ERROR)
|
||||
{
|
||||
ast_free(ast_compound_list);
|
||||
lexer_go_back(lexer, save_tok);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*ast)->left_child = ast_compound_list;
|
||||
}
|
||||
return PARSER_OK;
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
#include "parser.h"
|
||||
|
||||
enum parser_status parse_compound_list(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
|
||||
// Try and_or
|
||||
enum parser_status status_and_or =
|
||||
parse_and_or(&(*ast)->right_child, lexer);
|
||||
if (status_and_or == PARSER_ERROR)
|
||||
return handle_parser_error(status_and_or, ast);
|
||||
|
||||
// Try (';' and_or)*
|
||||
struct ast *cur_list_node = *ast;
|
||||
while (true)
|
||||
{
|
||||
struct lexer_token *save_tok = lexer_peek(lexer);
|
||||
if (save_tok->type != TOKEN_SEMICOLON
|
||||
&& save_tok->type != TOKEN_NEWLINE)
|
||||
break;
|
||||
lexer_pop(lexer);
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
|
||||
struct ast *new_list = ast_new(AST_LIST);
|
||||
enum parser_status status = parse_and_or(&new_list->right_child, lexer);
|
||||
if (status == PARSER_ERROR)
|
||||
{
|
||||
ast_free(new_list);
|
||||
lexer_go_back(lexer, save_tok);
|
||||
break;
|
||||
}
|
||||
|
||||
cur_list_node->left_child = new_list;
|
||||
cur_list_node = cur_list_node->left_child;
|
||||
}
|
||||
|
||||
// Try [';'] and skip it if present
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_SEMICOLON || tok->type == TOKEN_NEWLINE)
|
||||
{
|
||||
lexer_pop(lexer);
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
}
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_pipeline(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
// Try not
|
||||
bool not = false;
|
||||
if (tok->type == TOKEN_NOT)
|
||||
{
|
||||
not = true;
|
||||
*ast = ast_new(AST_NOT);
|
||||
lexer_pop(lexer);
|
||||
}
|
||||
|
||||
// Try command
|
||||
struct ast *last_command = NULL;
|
||||
enum parser_status status_command = parse_command(&last_command, lexer);
|
||||
if (status_command == PARSER_ERROR)
|
||||
{
|
||||
ast_free(*ast);
|
||||
return handle_parser_error(status_command, &last_command);
|
||||
}
|
||||
|
||||
// Try ('|' ('\n')* command)*
|
||||
struct ast *cur_pipe = NULL;
|
||||
bool first = true;
|
||||
while (true)
|
||||
{
|
||||
// Try |
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_PIPE)
|
||||
break;
|
||||
lexer_pop(lexer); // token |
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token \n
|
||||
|
||||
// Try command
|
||||
struct ast *new_command;
|
||||
status_command = parse_command(&new_command, lexer);
|
||||
if (status_command == PARSER_ERROR)
|
||||
{
|
||||
ast_free(new_command);
|
||||
if (first)
|
||||
ast_free(last_command);
|
||||
return handle_parser_error(status_command, ast);
|
||||
}
|
||||
|
||||
struct ast *ast_pipe = ast_new(AST_PIPE);
|
||||
//* Create new pipe and add command found before while loop in left
|
||||
// child
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
ast_pipe->left_child = last_command;
|
||||
if (not )
|
||||
{
|
||||
not = false;
|
||||
(*ast)->left_child = ast_pipe;
|
||||
}
|
||||
else
|
||||
*ast = ast_pipe;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_pipe->right_child = ast_pipe;
|
||||
ast_pipe->left_child = last_command;
|
||||
}
|
||||
cur_pipe = ast_pipe;
|
||||
last_command = new_command;
|
||||
}
|
||||
if (cur_pipe)
|
||||
cur_pipe->right_child = last_command;
|
||||
|
||||
if (ast == NULL)
|
||||
ast = &last_command;
|
||||
else if ((first && !not ) || *ast == NULL)
|
||||
{
|
||||
*ast = last_command;
|
||||
}
|
||||
else if (not &&first)
|
||||
(*ast)->left_child = last_command;
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_and_or(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
// Try pipeline
|
||||
enum parser_status status_pipeline = parse_pipeline(ast, lexer);
|
||||
if (status_pipeline == PARSER_ERROR)
|
||||
return handle_parser_error(status_pipeline, ast);
|
||||
|
||||
// Try (('&&'|'||') ('\n')* pipeline)*
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
bool first = true;
|
||||
struct ast *last_op = NULL;
|
||||
while (true)
|
||||
{
|
||||
// Try ('&&'| '||')
|
||||
tok = lexer_peek(lexer);
|
||||
bool is_and = true;
|
||||
if (tok->type == TOKEN_AND)
|
||||
is_and = true;
|
||||
else if (tok->type == TOKEN_OR)
|
||||
is_and = false;
|
||||
else
|
||||
break;
|
||||
lexer_pop(lexer);
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token '\n'
|
||||
|
||||
// Try pipeline
|
||||
struct ast *new_command = NULL;
|
||||
status_pipeline = parse_pipeline(&new_command, lexer);
|
||||
if (status_pipeline == PARSER_ERROR)
|
||||
{
|
||||
ast_free(new_command);
|
||||
return handle_parser_error(status_pipeline, ast);
|
||||
}
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
struct ast *tmp = *ast;
|
||||
*ast = ast_new(is_and ? AST_AND : AST_OR);
|
||||
last_op = *ast;
|
||||
last_op->left_child = tmp;
|
||||
last_op->right_child = new_command;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct ast *tmp_left_cmd = last_op->right_child;
|
||||
last_op->right_child = ast_new(is_and ? AST_AND : AST_OR);
|
||||
last_op = last_op->right_child;
|
||||
last_op->left_child = tmp_left_cmd;
|
||||
last_op->right_child = new_command;
|
||||
}
|
||||
}
|
||||
return PARSER_OK;
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
#include "parser.h"
|
||||
|
||||
enum parser_status parse_rule_while(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
|
||||
// Try While
|
||||
if (tok->type != TOKEN_WHILE)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token While
|
||||
|
||||
// Try compound_list
|
||||
*ast = ast_new(AST_WHILE);
|
||||
struct ast *ast_condition = ast_new(AST_LIST);
|
||||
enum parser_status status_compound_list =
|
||||
parse_compound_list(&ast_condition, lexer);
|
||||
// If status is ERROR, assignment is still legal to free everything
|
||||
(*ast)->condition = ast_condition;
|
||||
if (status_compound_list == PARSER_ERROR)
|
||||
return handle_parser_error(status_compound_list, ast);
|
||||
|
||||
// Try do_group
|
||||
struct ast *ast_do = NULL;
|
||||
enum parser_status status_do_group = parse_do_group(&ast_do, lexer);
|
||||
(*ast)->left_child = ast_do;
|
||||
if (status_do_group == PARSER_ERROR)
|
||||
return handle_parser_error(status_do_group, ast);
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_rule_until(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
|
||||
// Try Until
|
||||
if (tok->type != TOKEN_UNTIL)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token Until
|
||||
|
||||
// Try compound_list
|
||||
*ast = ast_new(AST_UNTIL);
|
||||
struct ast *ast_condition = ast_new(AST_LIST);
|
||||
enum parser_status status_compound_list =
|
||||
parse_compound_list(&ast_condition, lexer);
|
||||
// If status is ERROR, assignment is still legal to free everything
|
||||
(*ast)->condition = ast_condition;
|
||||
if (status_compound_list == PARSER_ERROR)
|
||||
return handle_parser_error(status_compound_list, ast);
|
||||
|
||||
// Try do_group
|
||||
struct ast *ast_do = NULL;
|
||||
enum parser_status status_do_group = parse_do_group(&ast_do, lexer);
|
||||
(*ast)->left_child = ast_do;
|
||||
if (status_do_group == PARSER_ERROR)
|
||||
return handle_parser_error(status_do_group, ast);
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_do_group(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
|
||||
// Try Do
|
||||
if (tok->type != TOKEN_DO)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token While
|
||||
|
||||
// Try compound_list
|
||||
struct ast *ast_body = ast_new(AST_LIST);
|
||||
enum parser_status status_compound_list =
|
||||
parse_compound_list(&ast_body, lexer);
|
||||
|
||||
*ast = ast_body;
|
||||
if (status_compound_list == PARSER_ERROR)
|
||||
return handle_parser_error(status_compound_list, ast);
|
||||
|
||||
// Try Done
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type != TOKEN_DONE)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token Done
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
||||
|
||||
enum parser_status parse_for(struct ast **ast, struct lexer *lexer)
|
||||
{
|
||||
struct lexer_token *tok = lexer_peek(lexer);
|
||||
|
||||
// Try For
|
||||
if (tok->type != TOKEN_FOR)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
lexer_pop(lexer); // token For
|
||||
|
||||
*ast = ast_new(AST_FOR);
|
||||
char **values = NULL;
|
||||
enum quotes *enclosure = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
// Try WORD
|
||||
tok = lexer_peek(lexer);
|
||||
if (tok->type == TOKEN_WORD || tok->type == TOKEN_WORD_DOUBLE_QUOTE
|
||||
|| tok->type == TOKEN_WORD_SINGLE_QUOTE)
|
||||
{
|
||||
len++;
|
||||
values = realloc(values, (len + 1) * sizeof(char *));
|
||||
values[len - 1] = tok->value;
|
||||
values[len] = NULL;
|
||||
enclosure = realloc(enclosure, len * sizeof(enum quotes));
|
||||
enclosure[len - 1] = tok->type - TOKEN_WORD;
|
||||
|
||||
(*ast)->value = values;
|
||||
(*ast)->enclosure = enclosure;
|
||||
|
||||
lexer_pop(lexer); // token WORD
|
||||
}
|
||||
else
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
|
||||
// Try [';']
|
||||
tok = lexer_peek(lexer);
|
||||
bool semi_colon = false;
|
||||
if (tok->type == TOKEN_SEMICOLON)
|
||||
{
|
||||
semi_colon = true;
|
||||
lexer_pop(lexer); // token ';'
|
||||
}
|
||||
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token '\n'
|
||||
|
||||
// Try in from [('\n')* 'in' (WORD)* (';'|'\n')]
|
||||
bool in = false;
|
||||
if (tok->type == TOKEN_IN)
|
||||
{
|
||||
in = true;
|
||||
lexer_pop(lexer); // token in
|
||||
//? add 'in' in char **values
|
||||
len++;
|
||||
values = realloc(values, (len + 1) * sizeof(char *));
|
||||
values[len - 1] = "in";
|
||||
values[len] = NULL;
|
||||
enclosure = realloc(enclosure, len * sizeof(enum quotes));
|
||||
enclosure[len - 1] = 0;
|
||||
|
||||
(*ast)->value = values;
|
||||
(*ast)->enclosure = enclosure;
|
||||
|
||||
//? already took left part of \ condition
|
||||
if (semi_colon)
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
|
||||
//? try rest of condition
|
||||
// Try WORD*
|
||||
tok = lexer_peek(lexer);
|
||||
while (tok->type == TOKEN_WORD || tok->type == TOKEN_WORD_DOUBLE_QUOTE
|
||||
|| tok->type == TOKEN_WORD_SINGLE_QUOTE)
|
||||
{
|
||||
len++;
|
||||
values = realloc(values, (len + 1) * sizeof(char *));
|
||||
values[len - 1] = tok->value;
|
||||
values[len] = NULL;
|
||||
enclosure = realloc(enclosure, len * sizeof(enum quotes));
|
||||
enclosure[len - 1] = tok->type - TOKEN_WORD;
|
||||
|
||||
(*ast)->value = values;
|
||||
(*ast)->enclosure = enclosure;
|
||||
|
||||
lexer_pop(lexer); // token WORD
|
||||
tok = lexer_peek(lexer);
|
||||
}
|
||||
|
||||
if (tok->type == TOKEN_SEMICOLON)
|
||||
lexer_pop(lexer); // token ';'
|
||||
else if (tok->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token '\n'
|
||||
else
|
||||
return handle_parser_error(PARSER_ERROR, ast);
|
||||
}
|
||||
|
||||
if (in)
|
||||
{
|
||||
// Try ('\n')*
|
||||
while ((tok = lexer_peek(lexer))->type == TOKEN_NEWLINE)
|
||||
lexer_pop(lexer); // token '\n'
|
||||
}
|
||||
|
||||
// Try do_group
|
||||
struct ast *ast_do = NULL;
|
||||
enum parser_status status_do_group = parse_do_group(&ast_do, lexer);
|
||||
(*ast)->left_child = ast_do;
|
||||
if (status_do_group == PARSER_ERROR)
|
||||
return handle_parser_error(status_do_group, ast);
|
||||
|
||||
(*ast)->left_child = ast_do;
|
||||
|
||||
return PARSER_OK;
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
#include "shell_input.h"
|
||||
|
||||
#include "parser.h"
|
||||
#include "var_list.h"
|
||||
|
||||
static int shell_prompt(void)
|
||||
{
|
||||
char *input = NULL;
|
||||
size_t input_len = 0;
|
||||
while (!shell->exit)
|
||||
{
|
||||
if (shell->return_code)
|
||||
fprintf(stderr,
|
||||
"\033[1m\033[31m➜ \033[1m\033[36mbsh \033[1m\033[33m✗ "
|
||||
"\033[0;37m");
|
||||
else
|
||||
fprintf(stderr,
|
||||
"\033[1m\033[32m➜ \033[1m\033[36mbsh \033[1m\033[33m✗ "
|
||||
"\033[0;37m");
|
||||
fflush(stderr);
|
||||
int line = 0;
|
||||
if (getline(&input, &input_len, stdin) < 1)
|
||||
{
|
||||
free(input);
|
||||
input = NULL;
|
||||
input_len = 0;
|
||||
break;
|
||||
}
|
||||
if (!input)
|
||||
{
|
||||
if (!line)
|
||||
fprintf(stderr, "\n");
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(input, "exit\n"))
|
||||
{
|
||||
shell->exit = 1;
|
||||
free(input);
|
||||
continue;
|
||||
}
|
||||
parse_input(input, NULL);
|
||||
free(input);
|
||||
input = NULL;
|
||||
input_len = 0;
|
||||
}
|
||||
return shell->return_code;
|
||||
}
|
||||
|
||||
static char *get_file_content(char *filename)
|
||||
{
|
||||
FILE *file = fopen(filename, "r");
|
||||
if (!file)
|
||||
{
|
||||
fprintf(stderr, "bsh: Can't open %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
fseek(file, 0, SEEK_END);
|
||||
long size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
char *content = calloc(size + 1, sizeof(char));
|
||||
fread(content, 1, size, file);
|
||||
content[size] = 0;
|
||||
fclose(file);
|
||||
return content;
|
||||
}
|
||||
|
||||
static char **copy_args(char **argv)
|
||||
{
|
||||
int i = 0;
|
||||
char **args = NULL;
|
||||
while (argv[i])
|
||||
{
|
||||
args = realloc(args, (i + 2) * sizeof(char *));
|
||||
args[i] = strdup(argv[i]);
|
||||
i++;
|
||||
}
|
||||
shell->nb_args = i - 1;
|
||||
if (args)
|
||||
args[i] = NULL;
|
||||
else
|
||||
args = calloc(1, sizeof(char *));
|
||||
return args;
|
||||
}
|
||||
|
||||
int get_input(int argc, char **argv)
|
||||
{
|
||||
char *input = NULL;
|
||||
size_t input_len = 0;
|
||||
if (argc < 2)
|
||||
{
|
||||
shell->args = calloc(2, sizeof(char *));
|
||||
shell->args[0] = strdup(argv[0]);
|
||||
new_var(shell, shell->args);
|
||||
int c;
|
||||
if (!isatty(STDIN_FILENO))
|
||||
{
|
||||
while (read(STDIN_FILENO, &c, 1) > 0)
|
||||
{
|
||||
if (c == EOF)
|
||||
break;
|
||||
input = realloc(input, (input_len + 2) * sizeof(char));
|
||||
input[input_len++] = c;
|
||||
}
|
||||
}
|
||||
else
|
||||
return shell_prompt();
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 1;
|
||||
if (!strcmp(argv[i], "-c"))
|
||||
{
|
||||
shell->args = calloc(2, sizeof(char *));
|
||||
shell->args[0] = strdup(argv[0]);
|
||||
new_var(shell, shell->args);
|
||||
input = strdup(argv[++i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
input = get_file_content(argv[i]);
|
||||
shell->args = copy_args(argv + 1);
|
||||
new_var(shell, shell->args);
|
||||
}
|
||||
if (!input)
|
||||
return 1;
|
||||
input_len = strlen(input);
|
||||
}
|
||||
if (input)
|
||||
{
|
||||
input[input_len] = '\0';
|
||||
parse_input(input, NULL);
|
||||
free(input);
|
||||
}
|
||||
return shell->return_code;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef SHELL_INPUT_H
|
||||
#define SHELL_INPUT_H
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../bsh.h"
|
||||
|
||||
extern struct shell *shell;
|
||||
|
||||
/**
|
||||
* @brief Get the input for the shell and send it to the lexer
|
||||
*
|
||||
* @param argc number of arguments
|
||||
* @param argv list of arguments
|
||||
* @return int
|
||||
*/
|
||||
int get_input(int argc, char **argv);
|
||||
|
||||
#endif // !SHELL_INPUT_H
|
|
@ -0,0 +1,327 @@
|
|||
#include "var_list.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../bsh.h"
|
||||
|
||||
/*struct var *init_list(void)
|
||||
{
|
||||
struct var *new = calloc(1, sizeof(struct var));
|
||||
if (!new)
|
||||
return NULL;
|
||||
new->name = NULL;
|
||||
new->value = NULL;
|
||||
new->next = NULL;
|
||||
}*/
|
||||
|
||||
/*int add_elt_list(struct shell *sh, char *name, char *value)
|
||||
{
|
||||
struct var *new = calloc(1, sizeof(struct var));
|
||||
if (!new)
|
||||
return 1;
|
||||
new->name = calloc(strlen(name) + 1, sizeof(char));
|
||||
if (!new->name)
|
||||
{
|
||||
free(new);
|
||||
return 1;
|
||||
}
|
||||
strcpy(new->name, name);
|
||||
new->value = calloc(strlen(value) + 1, sizeof(char));
|
||||
if (!new->value)
|
||||
{
|
||||
free(new->name);
|
||||
free(new);
|
||||
return 1;
|
||||
}
|
||||
strcpy(new->value, value);
|
||||
new->next = sh->var_list;
|
||||
sh->var_list = new;
|
||||
return 0;
|
||||
}*/
|
||||
|
||||
void my_itoa(int nb, char *buff)
|
||||
{
|
||||
sprintf(buff, "%d", nb);
|
||||
}
|
||||
|
||||
char *generate_rand(struct shell *sh)
|
||||
{
|
||||
srand(time(NULL));
|
||||
int rnd = rand() % 32768;
|
||||
if (!sh->random_nb)
|
||||
sh->random_nb = calloc(6, sizeof(char));
|
||||
my_itoa(rnd, sh->random_nb);
|
||||
return sh->random_nb;
|
||||
}
|
||||
|
||||
int is_number(char *str)
|
||||
{
|
||||
if (str[0] == '0')
|
||||
return 0;
|
||||
int i = 0;
|
||||
while (str[i] != '\0')
|
||||
{
|
||||
if (str[i] < '0' || str[i++] > '0')
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int is_param(char *str, char *c)
|
||||
{
|
||||
int i = 0;
|
||||
if (is_number(str))
|
||||
return 1;
|
||||
while (str[i] != '\0')
|
||||
{
|
||||
if (str[i++] == *c)
|
||||
return 1 && c[1] == '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int new_var(struct shell *sh, char **arg)
|
||||
{
|
||||
struct var_stack *s = calloc(1, sizeof(struct var));
|
||||
s->var_list = NULL;
|
||||
s->next = sh->var_stack;
|
||||
sh->var_stack = s;
|
||||
int i = 1;
|
||||
char *nb = calloc(21, sizeof(char));
|
||||
int size = 1;
|
||||
char *var = calloc(size, sizeof(char));
|
||||
var[0] = '\0';
|
||||
while (arg[i])
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
size += strlen(arg[i]) + 1;
|
||||
var = realloc(var, size * sizeof(char));
|
||||
strcat(var, arg[i]);
|
||||
if (arg[i + 1])
|
||||
strcat(var, " ");
|
||||
}
|
||||
my_itoa(i, nb);
|
||||
push_elt_list(sh, nb, arg[i++]);
|
||||
}
|
||||
push_elt_list(sh, "0", sh->args[0]);
|
||||
push_int_elt_list(sh, "#", i - 1);
|
||||
push_int_elt_list(sh, "?", 0);
|
||||
push_elt_list(sh, "@", var);
|
||||
push_elt_list(sh, "*", var);
|
||||
push_int_elt_list(sh, "$", sh->pid);
|
||||
push_int_elt_list(sh, "UID", sh->uid);
|
||||
push_elt_list(sh, "OLDPWD", sh->oldpwd);
|
||||
push_elt_list(sh, "IFS", sh->ifs);
|
||||
free(nb);
|
||||
free(var);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int push_elt_list(struct shell *sh, char *name, char *value)
|
||||
{
|
||||
int param = is_param("*@#?$", name);
|
||||
char *new_content = calloc(strlen(value) + 1, sizeof(char));
|
||||
if (!new_content)
|
||||
return 1;
|
||||
strcpy(new_content, value);
|
||||
struct var *tmp = NULL;
|
||||
if (param)
|
||||
tmp = sh->var_stack->var_list;
|
||||
else
|
||||
tmp = sh->var_list;
|
||||
while (tmp && strcmp(tmp->name, name))
|
||||
tmp = tmp->next;
|
||||
if (tmp)
|
||||
{
|
||||
free(tmp->value);
|
||||
tmp->value = new_content;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct var *new = calloc(1, sizeof(struct var));
|
||||
if (!new)
|
||||
return 1;
|
||||
new->name = calloc(strlen(name) + 1, sizeof(char));
|
||||
if (!new->name)
|
||||
{
|
||||
free(new);
|
||||
free(new_content);
|
||||
return 1;
|
||||
}
|
||||
strcpy(new->name, name);
|
||||
new->value = new_content;
|
||||
if (param)
|
||||
{
|
||||
new->next = sh->var_stack->var_list;
|
||||
sh->var_stack->var_list = new;
|
||||
}
|
||||
else
|
||||
{
|
||||
new->next = sh->var_list;
|
||||
sh->var_list = new;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int push_int_elt_list(struct shell *sh, char *name, int val)
|
||||
{
|
||||
char *value = calloc(21, sizeof(char));
|
||||
my_itoa(val, value);
|
||||
int param = is_param("*@#?$", name);
|
||||
char *new_content = calloc(strlen(value) + 1, sizeof(char));
|
||||
if (!new_content)
|
||||
return 1;
|
||||
strcpy(new_content, value);
|
||||
struct var *tmp = NULL;
|
||||
if (param)
|
||||
tmp = sh->var_stack->var_list;
|
||||
else
|
||||
tmp = sh->var_list;
|
||||
while (tmp && strcmp(tmp->name, name))
|
||||
tmp = tmp->next;
|
||||
if (tmp)
|
||||
{
|
||||
free(tmp->value);
|
||||
tmp->value = new_content;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct var *new = calloc(1, sizeof(struct var));
|
||||
if (!new)
|
||||
return 1;
|
||||
new->name = calloc(strlen(name) + 1, sizeof(char));
|
||||
if (!new->name)
|
||||
{
|
||||
free(new);
|
||||
free(new_content);
|
||||
return 1;
|
||||
}
|
||||
strcpy(new->name, name);
|
||||
new->value = new_content;
|
||||
if (param)
|
||||
{
|
||||
new->next = sh->var_stack->var_list;
|
||||
sh->var_stack->var_list = new;
|
||||
}
|
||||
else
|
||||
{
|
||||
new->next = sh->var_list;
|
||||
sh->var_list = new;
|
||||
}
|
||||
}
|
||||
free(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *find_elt_list(struct shell *sh, char *name)
|
||||
{
|
||||
int param = is_param("*@#?$", name);
|
||||
if (!strcmp("RANDOM", name))
|
||||
return generate_rand(sh);
|
||||
struct var *tmp = NULL;
|
||||
if (param)
|
||||
tmp = sh->var_stack->var_list;
|
||||
else
|
||||
tmp = sh->var_list;
|
||||
while (tmp && strcmp(tmp->name, name))
|
||||
tmp = tmp->next;
|
||||
// printf("%s\n", tmp->value);
|
||||
if (tmp)
|
||||
return tmp->value;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free_list_sub(struct var *l)
|
||||
{
|
||||
struct var *list = l;
|
||||
while (list)
|
||||
{
|
||||
struct var *tmp = list;
|
||||
list = list->next;
|
||||
free(tmp->name);
|
||||
free(tmp->value);
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void del_stack(struct shell *sh)
|
||||
{
|
||||
struct var_stack *s = sh->var_stack;
|
||||
sh->var_stack = s->next;
|
||||
free_list_sub(s->var_list);
|
||||
free(s);
|
||||
}
|
||||
|
||||
void free_list(struct shell *sh)
|
||||
{
|
||||
while (sh->var_stack)
|
||||
del_stack(sh);
|
||||
free_list_sub(sh->var_list);
|
||||
}
|
||||
|
||||
int del_name(struct shell *sh, char *name)
|
||||
{
|
||||
struct var *actual = sh->var_list;
|
||||
struct var *previous = sh->var_list;
|
||||
int index = 0;
|
||||
|
||||
while (actual)
|
||||
{
|
||||
if (!strcmp(actual->name, name))
|
||||
{
|
||||
if (index == 0)
|
||||
sh->var_list = actual->next;
|
||||
else
|
||||
previous->next = actual->next;
|
||||
|
||||
free(actual->name);
|
||||
free(actual->value);
|
||||
free(actual);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
index++;
|
||||
previous = actual;
|
||||
actual = actual->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct var *var_list_cpy(struct shell *sh)
|
||||
{
|
||||
struct var *new = NULL;
|
||||
struct var *list = sh->var_list;
|
||||
while (list)
|
||||
{
|
||||
struct var *tmp = new;
|
||||
new = calloc(1, sizeof(struct var));
|
||||
if (!new)
|
||||
return NULL;
|
||||
char *new_content = calloc(strlen(list->value) + 1, sizeof(char));
|
||||
if (!new_content)
|
||||
{
|
||||
free(new);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(new_content, list->value);
|
||||
new->name = calloc(strlen(list->name) + 1, sizeof(char));
|
||||
if (!new->name)
|
||||
{
|
||||
free(new);
|
||||
free(new_content);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(new->name, list->name);
|
||||
new->value = new_content;
|
||||
new->next = tmp;
|
||||
list = list->next;
|
||||
}
|
||||
return new;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef VAR_LIST_H
|
||||
#define VAR_LIST_H
|
||||
|
||||
#include "../bsh.h"
|
||||
|
||||
int push_elt_list(struct shell *sh, char *name, char *value);
|
||||
char *find_elt_list(struct shell *sh, char *name);
|
||||
void free_list(struct shell *sh);
|
||||
int push_int_elt_list(struct shell *sh, char *name, int val);
|
||||
void del_stack(struct shell *sh);
|
||||
void free_list_sub(struct var *list);
|
||||
int new_var(struct shell *sh, char **arg);
|
||||
int del_name(struct shell *sh, char *name);
|
||||
struct var *var_list_cpy(struct shell *sh);
|
||||
// int change_elt_list(struct shell *sh, char *name, char *value);
|
||||
|
||||
#endif /* !VAR_LIST_H */
|
|
@ -0,0 +1,2 @@
|
|||
- name: Basic
|
||||
file: tests/aliases/basic.sh
|
|
@ -0,0 +1,2 @@
|
|||
alias bar=ls
|
||||
bar
|
|
@ -0,0 +1,5 @@
|
|||
for i in ee oo kk; do
|
||||
echo $i
|
||||
break
|
||||
done
|
||||
echo "done"
|
|
@ -0,0 +1,12 @@
|
|||
for i in ee oo uu;
|
||||
echo $i
|
||||
while echo 11; do
|
||||
until grep; do
|
||||
for j in aa bb cc;
|
||||
echo $j
|
||||
break 4
|
||||
done
|
||||
done
|
||||
done
|
||||
done
|
||||
echo "done"
|
|
@ -0,0 +1,12 @@
|
|||
for i in ee oo uu;
|
||||
echo $i
|
||||
while echo 11; do
|
||||
until grep; do
|
||||
for j in aa bb cc;
|
||||
echo $j
|
||||
break 5
|
||||
done
|
||||
done
|
||||
done
|
||||
done
|
||||
echo "done"
|
|
@ -0,0 +1,35 @@
|
|||
- name: exit in case
|
||||
input: a=33; case 22 in ($a) echo oui;; (22) echo non; exit;; esac; echo oui
|
||||
|
||||
- name: basic
|
||||
file: tests/case/basic.sh
|
||||
|
||||
- name: all_case
|
||||
input: "case 'yoyoy' in bruh) echo error;; *) echo ayaya;; esac"
|
||||
|
||||
- name: no_match
|
||||
input: "case 'yoyoy' in bruh) echo error;; efefefef) echo ayaya;; esac; echo 'this is the first line'"
|
||||
|
||||
- name: no_case_clause
|
||||
input: "case 'yoyoy' in esac"
|
||||
|
||||
- name: no_ending_semicolons
|
||||
input: "case 'yoyoy' in bruh) echo error;; yoyoy) echo ayaya; esac"
|
||||
|
||||
- name: case_in_case
|
||||
input: "case test in test) case second in s) echo false;; (second) echo ok;; esac; echo woow!;; esac"
|
||||
|
||||
- name: hard_semicolon
|
||||
input: "case test in bruh) echo error;; test) echo ayaya;; a) echo a; esac"
|
||||
|
||||
- name: if_in_case
|
||||
input: "case test in bruh) echo error;; test) if true; then echo ok; fi;; a) echo a; esac"
|
||||
|
||||
- name: case_in_function
|
||||
file: tests/case/case_in_function.sh
|
||||
|
||||
- name: if_in_case_in_if
|
||||
file: tests/case/if_in_case_in_if.sh
|
||||
|
||||
- name: multiple_condition_case
|
||||
file: tests/case/multiple_condition_case.sh
|
|
@ -0,0 +1,6 @@
|
|||
case test in
|
||||
te)
|
||||
echo wrong;;
|
||||
"test")
|
||||
echo ok ;;
|
||||
esac
|
|
@ -0,0 +1,16 @@
|
|||
fun()
|
||||
{
|
||||
case fun in
|
||||
f)
|
||||
echo nope;;
|
||||
*)
|
||||
echo nice;;
|
||||
esac
|
||||
}
|
||||
|
||||
case call in
|
||||
call)
|
||||
fun;;
|
||||
c)
|
||||
echo what?;;
|
||||
esac
|
|
@ -0,0 +1,13 @@
|
|||
a=true
|
||||
|
||||
if $a
|
||||
then
|
||||
case $a in
|
||||
false)
|
||||
echo false;;
|
||||
(true)
|
||||
echo $a;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo end
|
|
@ -0,0 +1,4 @@
|
|||
case test in
|
||||
te|test|aaaaaaaaa)
|
||||
echo true;;
|
||||
esac
|
|
@ -0,0 +1,59 @@
|
|||
- name: Basic folder
|
||||
input: cd src; pwd
|
||||
|
||||
- name: Multiple folders
|
||||
input: cd src; cd builtins; cd ..; pwd
|
||||
|
||||
- name: Long path
|
||||
input: cd src/../tests/../; pwd
|
||||
|
||||
- name: Long path, doesn't work
|
||||
input: cd src/builtins/Makefile/ ; pwd
|
||||
|
||||
- name: Wrong folder
|
||||
input: cd jenexistepas; pwd
|
||||
|
||||
- name: File
|
||||
input: cd Makefile; pwd
|
||||
|
||||
- name: Folder then a dash
|
||||
input: cd src; cd -; pwd
|
||||
|
||||
- name: Root
|
||||
input: cd ./; pwd
|
||||
|
||||
- name: Root then folder
|
||||
input: cd ./src; pwd
|
||||
|
||||
- name: Root then folder then reroot
|
||||
input: cd ./src; cd ./; pwd
|
||||
|
||||
- name: Root after folder
|
||||
input: cd src/./; pwd
|
||||
|
||||
- name: Root then dash
|
||||
input: cd ./; cd -; pwd
|
||||
|
||||
- name: Root folder then dash
|
||||
input: cd ./src/builtins; cd -; pwd
|
||||
|
||||
- name: Root folder inexistant
|
||||
input: cd ./jenexistepas; pwd
|
||||
|
||||
- name: Root folder is a file
|
||||
input: cd ./Makefile; pwd
|
||||
|
||||
- name: Double root
|
||||
input: cd ././; pwd
|
||||
|
||||
- name: Multiple dash
|
||||
input: cd -----; pwd
|
||||
|
||||
- name: Chained dash
|
||||
input: cd /tmp; cd /root; echo start; cd -; pwd; cd -; pwd; cd -; pwd; cd -; pwd; cd -; pwd; cd -; echo end; pwd
|
||||
|
||||
- name: Go back but too far
|
||||
input: cd ../../../../../../../../../../../../../../tmp; pwd; cd /home; cd -; pwd;
|
||||
|
||||
- name: Go back (absolute) but too far
|
||||
input: cd /../../../../../../../../../../../../../../tmp; pwd; cd /home; cd -; pwd;
|
|
@ -0,0 +1,8 @@
|
|||
- name: Basic
|
||||
file: tests/cmd_substitution/basic.sh
|
||||
|
||||
- name: Backticks
|
||||
file: tests/cmd_substitution/backticks.sh
|
||||
|
||||
- name: With while loops
|
||||
file: tests/cmd_substitution/with_while_loop.sh
|
|
@ -0,0 +1,4 @@
|
|||
if `echo true`; then
|
||||
echo ok
|
||||
fi
|
||||
`echo false`
|
|
@ -0,0 +1,3 @@
|
|||
if $(echo true); then
|
||||
echo ok
|
||||
fi
|
|
@ -0,0 +1,3 @@
|
|||
while $(echo false); do
|
||||
echo nope
|
||||
done
|
|
@ -0,0 +1,26 @@
|
|||
- name: Two commands separated by semicolon
|
||||
input: echo Hello; echo World!;
|
||||
|
||||
- name: Two commands, semicolon at the end
|
||||
input: echo Hello; echo World!;
|
||||
|
||||
- name: Two commands, no space between semicolon and word
|
||||
input: echo "Hello";echo "World!"
|
||||
|
||||
- name: Only semicolon
|
||||
input: ;
|
||||
|
||||
- name: Many commands
|
||||
input: ls;pwd;cat Makefile;echo "Test";
|
||||
|
||||
- name: Two semicolons
|
||||
input: ;;
|
||||
|
||||
- name: One command and two semicolons
|
||||
input: echo;;
|
||||
|
||||
- name: Many semicolons with 2 commands
|
||||
input: echo salut;;;;; pwd;
|
||||
|
||||
- name: Wrong commands and multiple semicolons
|
||||
input: a;a;a;a;;a;a;;;;a;;a;a;a;;a;
|
|
@ -0,0 +1,8 @@
|
|||
- name: 1 break
|
||||
file: tests/break_and_continue/1_break.sh
|
||||
|
||||
- name: 4 break
|
||||
file: tests/break_and_continue/4_break.sh
|
||||
|
||||
- name: 5 break 4 loop
|
||||
file: tests/break_and_continue/5_break_4_loop.sh
|
|
@ -0,0 +1,74 @@
|
|||
- name: Basic words
|
||||
input: echo "Cut, Copy and Paste!"
|
||||
|
||||
- name: Basic -n
|
||||
input: echo -n "Cut, Copy and Paste!"
|
||||
|
||||
- name: Basic words
|
||||
input: echo "-n"
|
||||
|
||||
- name: Multiple Words
|
||||
input: echo "Cut, Copy and Paste!" "Cut, Copy and Paste!"
|
||||
|
||||
- name: Multiple Words 2²
|
||||
input: echo "Cut, Copy and Paste!" "Quentin" "Charles"
|
||||
|
||||
- name: Word without quote
|
||||
input: echo nathan
|
||||
|
||||
- name: Sentence without quote
|
||||
input: echo Je suis une phrase sans quote
|
||||
|
||||
- name: Sentence without quote with option
|
||||
input: echo -n Je suis une phrase sans quote
|
||||
|
||||
- name: Sentence without quote with multiple spaces
|
||||
input: echo Je suis une phrase sans quote
|
||||
|
||||
- name: Sentence without quote with multiple spaces with option
|
||||
input: echo -n Je suis une phrase sans quote
|
||||
|
||||
- name: Sentence without quote with multiple spaces and a lost option
|
||||
input: echo Je suis une -n phrase sans quote
|
||||
|
||||
- name: Echo empty
|
||||
input: echo
|
||||
|
||||
- name: Echo empty with -n
|
||||
input: echo -n
|
||||
|
||||
- name: Echo empty with -n and empty quote
|
||||
input: echo -n ""
|
||||
|
||||
- name: Echo empty quote
|
||||
input: echo ""
|
||||
|
||||
- name: Echo real backlash n
|
||||
input: echo "\n"
|
||||
|
||||
- name: Echo wrong option
|
||||
input: echo -a
|
||||
|
||||
- name: Echo wrong option followed by real one
|
||||
input: echo -a -n
|
||||
|
||||
- name: Echo wrong option preceded by real one
|
||||
input: echo -a -n
|
||||
|
||||
- name: Option in quote with word
|
||||
input: echo "-n salut"
|
||||
|
||||
- name: Option in quote with word and mutliple spaces
|
||||
input: echo "-n salut"
|
||||
|
||||
- name: Option in quote with no other word inside quote
|
||||
input: echo "-n" "salut"
|
||||
|
||||
- name: Option in quote with space inside
|
||||
input: echo "-n " "salut"
|
||||
|
||||
- name: Long argument
|
||||
input: echo Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in
|
||||
|
||||
- name: 10 paragraphs
|
||||
file: tests/echo/very_long.sh
|
|
@ -0,0 +1,19 @@
|
|||
echo "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque quam, varius et nunc nec, malesuada pellentesque risus. Morbi nunc augue, sollicitudin eu tincidunt id, vulputate quis nisl. Etiam quis risus id mauris blandit vestibulum. Suspendisse ullamcorper tortor id sapien iaculis, vel tincidunt orci consequat. Proin rhoncus dui ut accumsan tincidunt. Nullam luctus rutrum dui ut ornare. Fusce elit arcu, faucibus et nisl efficitur, dignissim commodo nibh. Nam imperdiet pretium massa, quis laoreet felis vestibulum maximus. Suspendisse imperdiet lacus vel diam feugiat molestie. Vestibulum et mauris porta est commodo porttitor. Curabitur egestas vitae sapien sit amet dignissim. Ut fringilla cursus urna non accumsan. Nullam vulputate id quam vel finibus. Morbi rutrum tincidunt venenatis. Curabitur lacus lectus, feugiat eget mi semper, pharetra aliquam arcu.
|
||||
|
||||
Sed semper tempor turpis non pretium. In vestibulum neque non risus vulputate ullamcorper. Proin mauris lectus, rhoncus at facilisis id, condimentum non est. Nulla purus velit, accumsan sit amet massa eu, auctor pulvinar lectus. Ut id justo sapien. Integer scelerisque, justo et aliquam malesuada, urna nisl bibendum magna, nec euismod justo leo id augue. Aliquam et velit eget enim pretium euismod. Nunc consectetur vel augue ac luctus. In porttitor vulputate lectus nec ultrices. Mauris elementum lectus id ligula convallis, semper dignissim urna ullamcorper. Praesent convallis nisl in tortor malesuada ornare. Quisque eu nulla finibus, blandit metus eu, porttitor purus. Donec vitae lacus nec turpis semper pellentesque non id tortor. Pellentesque quis libero sagittis, ultrices massa in, scelerisque est. Mauris semper dui turpis.
|
||||
|
||||
In finibus risus ac lorem pulvinar, et congue est eleifend. Curabitur finibus eu ante a rutrum. Sed mollis neque in finibus rhoncus. Praesent blandit maximus velit a ornare. Nunc suscipit ligula eget lacus posuere, vel egestas metus ornare. Vestibulum vel rutrum quam. Ut justo dolor, viverra in rhoncus sed, ultrices eget quam. Proin aliquet nunc sed aliquam tempus. Vivamus accumsan est sed libero aliquet interdum. Aenean rhoncus turpis in nisi vulputate pretium. Quisque vel accumsan nulla. Aliquam euismod massa eget dui semper aliquam. Proin massa nunc, placerat sed ante in, tincidunt convallis metus. Etiam quis sem lacinia velit condimentum lobortis suscipit non enim. Aliquam vel condimentum dui. Nunc eget pharetra odio.
|
||||
|
||||
Nam in purus tincidunt, porttitor turpis sit amet, pharetra velit. Nunc eu tempor ligula. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam a varius quam. Mauris nec cursus nisl. In velit tellus, cursus non mi sit amet, sagittis semper enim. Phasellus elementum purus nibh, vel finibus urna vulputate non. Aenean tortor nunc, faucibus et imperdiet eu, blandit at dui. Suspendisse tempor auctor sodales. Donec hendrerit nibh quis augue consequat, quis dapibus quam faucibus.
|
||||
|
||||
Cras maximus hendrerit velit non porta. In hac habitasse platea dictumst. Aliquam nec consectetur erat. Cras sodales sit amet dolor id congue. Mauris et aliquam risus, nec aliquam nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed sed turpis finibus, feugiat nibh vel, interdum ligula. Donec sit amet nunc rutrum, efficitur erat eu, mattis justo. Phasellus sit amet scelerisque ex. Suspendisse fringilla tempus risus, efficitur aliquet ex tincidunt eget.
|
||||
|
||||
Sed eleifend, odio vitae rutrum convallis, nisl dui mollis magna, a euismod erat elit quis erat. Phasellus convallis vulputate ante, a convallis risus. Curabitur imperdiet quam non aliquam tempor. Integer maximus lacus elit, vehicula pulvinar nunc elementum tincidunt. In tincidunt ante sit amet est aliquet porta. Pellentesque dictum egestas purus, sit amet lobortis ex tincidunt condimentum. Maecenas vulputate orci a diam fermentum, ac rutrum mauris pellentesque. In non blandit justo, in scelerisque sem.
|
||||
|
||||
Quisque lobortis pretium velit, sit amet sollicitudin tortor bibendum in. Integer posuere imperdiet erat, vel euismod mi mollis in. Donec felis nunc, dictum vitae sodales vel, suscipit vitae nisi. Nam convallis non ex ut facilisis. Nunc maximus facilisis arcu eget faucibus. Sed luctus orci sit amet commodo vestibulum. Donec est arcu, porta ac justo at, lobortis interdum felis. Sed finibus ante nec maximus mollis.
|
||||
|
||||
Nam tristique turpis quis congue suscipit. Nam vel tincidunt leo. Sed laoreet lectus nec ipsum dictum sollicitudin. Proin tincidunt sed augue sit amet posuere. Vestibulum vel rutrum nisl. Sed lectus nisl, lobortis eu lobortis vel, bibendum sed enim. Phasellus consectetur neque id tristique maximus. Donec quam sapien, rhoncus id maximus sit amet, bibendum vitae orci. Suspendisse potenti. Phasellus ut varius sapien. Vivamus facilisis in ligula nec accumsan. Phasellus sem neque, dictum et commodo vitae, vulputate nec ipsum. Pellentesque vitae tincidunt lorem.
|
||||
|
||||
Etiam dignissim lectus sed nunc elementum, quis ullamcorper turpis sodales. Pellentesque est sapien, cursus et est ut, posuere scelerisque diam. Nullam non nisi id ipsum elementum varius et ut magna. Donec laoreet quis tellus eu vehicula. Quisque et fermentum ligula. Nam rutrum sollicitudin mollis. Aliquam erat volutpat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse ullamcorper ut felis nec porttitor. Quisque scelerisque quis magna vel facilisis. Sed congue nisl id enim consectetur egestas. Suspendisse semper diam ex, sed vehicula neque maximus at. Maecenas sit amet ex vitae quam tristique vestibulum vel a tellus.
|
||||
|
||||
Praesent ultrices risus ut mauris auctor suscipit. In suscipit finibus massa, non finibus lacus ultricies sed. Vestibulum rutrum feugiat massa, at efficitur sapien volutpat blandit. Nunc eget erat sit amet libero ultricies ornare. Nunc imperdiet, magna a sodales tempor, mauris velit molestie velit, id interdum eros arcu quis odio. Quisque odio urna, auctor sit amet leo id, consequat vestibulum tellus. Etiam vulputate nunc eget turpis convallis iaculis. In nulla sapien, pretium ut sagittis id, eleifend sit amet nibh. Duis feugiat iaculis dapibus. Fusce blandit sapien id convallis faucibus. Phasellus suscipit mauris commodo bibendum condimentum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent aliquam fringilla mi. Sed et diam ipsum. Morbi ac ante id erat pretium congue eget id lectus. Proin laoreet augue eget est finibus, nec volutpat nulla porttitor."
|
|
@ -0,0 +1,29 @@
|
|||
- name: basic variable
|
||||
input: first=test; a="first value is $first"; echo "$a"
|
||||
|
||||
- name: basic variable no expand
|
||||
input: first=test; a='first value is $first'; echo $a
|
||||
|
||||
- name: basic variable expand then no expand
|
||||
input: first=test; a="first value is $first"; echo '$a $first'
|
||||
|
||||
- name: basic variable with weird quotes
|
||||
input: first='test'; a="'first value is $first"; echo '"$a $first'
|
||||
|
||||
- name: basic variable with weird quotes reversed
|
||||
input: first='test'; a="'first value is $first"; echo "'$a $first"
|
||||
|
||||
- name: basic variable with weird quotes escaped
|
||||
input: first='test'; a="'first value is $first"; echo '\"$a $first'
|
||||
|
||||
- name: basic variable with undefined variable
|
||||
input: first='test'; a='first value is $i $first'; echo "$a $first"
|
||||
|
||||
- name: basic variable with quotes in quotes
|
||||
input: first=""; a='first value is $i $first'; echo "$a $first"
|
||||
|
||||
- name: wrong basic variable with quotes in quotes
|
||||
input: first="""; a='first value is $i $first'; echo "$a $first"
|
||||
|
||||
- name: basic variable with undefined variable and weird quotes
|
||||
input: first="$first"; a="'first value is "$first"'"; echo ''$a "$first''
|
|
@ -0,0 +1,17 @@
|
|||
- name: Basic for
|
||||
file: tests/for/basic.sh
|
||||
|
||||
- name: For in for
|
||||
file: tests/for/for_in_for.sh
|
||||
|
||||
- name: For with variables
|
||||
file: tests/for/for_in_variables.sh
|
||||
|
||||
- name: For mixed ';' and '\n'
|
||||
file: tests/for/mixed.sh
|
||||
|
||||
- name: For no args no in
|
||||
file: tests/for/no_args_no_in.sh
|
||||
|
||||
- name: Multiple for
|
||||
file: tests/for/multiple.sh
|
|
@ -0,0 +1,4 @@
|
|||
for i in salut test
|
||||
do
|
||||
echo $i
|
||||
done
|
|
@ -0,0 +1,9 @@
|
|||
for i in salut mec
|
||||
do
|
||||
echo -n "$i "
|
||||
for j in "bonjour" toi
|
||||
do
|
||||
echo -n $j
|
||||
done
|
||||
echo
|
||||
done
|
|
@ -0,0 +1,6 @@
|
|||
a=bonjour
|
||||
b="encore un mot"
|
||||
for i in $a $b
|
||||
do
|
||||
echo $i
|
||||
done
|
|
@ -0,0 +1,3 @@
|
|||
for i in salut toi je suis un for genial; do
|
||||
echo $i
|
||||
done
|
|
@ -0,0 +1,13 @@
|
|||
for a in arg1 arg2 arg3
|
||||
do
|
||||
echo $a
|
||||
done
|
||||
|
||||
for a in a b c d e f g
|
||||
do
|
||||
echo $a
|
||||
done
|
||||
|
||||
for g in in for while; do
|
||||
echo $g
|
||||
done
|
|
@ -0,0 +1,8 @@
|
|||
for salut
|
||||
do
|
||||
echo $salut
|
||||
done
|
||||
echo $?
|
||||
echo $#
|
||||
echo $@
|
||||
echo $*
|
|
@ -0,0 +1,26 @@
|
|||
- name: Basic
|
||||
file: ./tests/functions/basic.sh
|
||||
|
||||
- name: Arguments
|
||||
file: tests/functions/arguments.sh
|
||||
|
||||
- name: Funtion in function
|
||||
file: tests/functions/function_in_function.sh
|
||||
|
||||
- name: Arguments hard
|
||||
file: tests/functions/arguments_hard.sh
|
||||
|
||||
- name: Mutilple calls with simple args
|
||||
file: tests/functions/multiple_calls.sh
|
||||
|
||||
- name: Recursive calls
|
||||
file: tests/functions/recursive_call.sh
|
||||
|
||||
- name: Declaration in if
|
||||
file: tests/functions/declaration_in_if.sh
|
||||
|
||||
- name: Use function in other command
|
||||
file: tests/functions/use_fun_in_other_cmd.sh
|
||||
|
||||
- name: Variable in function
|
||||
file: tests/functions/variable_in_fun.sh
|
|
@ -0,0 +1,8 @@
|
|||
foo()
|
||||
{
|
||||
echo $@
|
||||
echo $#
|
||||
echo $2
|
||||
}
|
||||
|
||||
foo salut je suis un argument
|
|
@ -0,0 +1,9 @@
|
|||
foo()
|
||||
{
|
||||
echo $@
|
||||
}
|
||||
|
||||
foo arguments de la fonction
|
||||
echo $@
|
||||
foo "d'autres" arguments
|
||||
exit 4
|
|
@ -0,0 +1,6 @@
|
|||
foo()
|
||||
{
|
||||
echo "foo"
|
||||
}
|
||||
|
||||
foo
|
|
@ -0,0 +1,8 @@
|
|||
if true
|
||||
then
|
||||
foo() {
|
||||
echo 'I am in foo function'
|
||||
}
|
||||
fi
|
||||
|
||||
foo
|
|
@ -0,0 +1,11 @@
|
|||
foo()
|
||||
{
|
||||
bar()
|
||||
{
|
||||
echo "bar"
|
||||
echo $@
|
||||
}
|
||||
}
|
||||
|
||||
foo salut je suis un argument
|
||||
bar salut
|
|
@ -0,0 +1,9 @@
|
|||
foo()
|
||||
{
|
||||
echo "this is a test"
|
||||
echo $@
|
||||
}
|
||||
|
||||
foo salut
|
||||
foo
|
||||
foo foo foo; foo a
|
|
@ -0,0 +1,11 @@
|
|||
test()
|
||||
{
|
||||
if $@; then
|
||||
test false
|
||||
else
|
||||
echo end
|
||||
fi
|
||||
}
|
||||
|
||||
test true
|
||||
echo "did this work ?"
|
|
@ -0,0 +1,6 @@
|
|||
fun() {
|
||||
echo "fun"
|
||||
}
|
||||
|
||||
echo fun
|
||||
echo; fun
|
|
@ -0,0 +1,11 @@
|
|||
a=12
|
||||
|
||||
fun()
|
||||
{
|
||||
a=42
|
||||
echo "$a"
|
||||
}
|
||||
|
||||
echo $a
|
||||
fun
|
||||
echo $a
|
|
@ -0,0 +1,67 @@
|
|||
import argparse
|
||||
from pathlib import Path
|
||||
from difflib import unified_diff
|
||||
|
||||
import subprocess as sp
|
||||
import yaml
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class TestCase:
|
||||
def __init__(self, name, input = None, file = None):
|
||||
self.name = name
|
||||
self.input = input
|
||||
self.file = file
|
||||
|
||||
def diff(expected: str, actual: str) -> str:
|
||||
expected_lines = expected.splitlines(keepends=True)
|
||||
actual_lines = actual.splitlines(keepends=True)
|
||||
return ''.join(unified_diff(actual_lines, expected_lines, fromfile='actual', tofile='expected'))
|
||||
|
||||
def run_shell(shell: str, stdin: str) -> sp.CompletedProcess:
|
||||
return sp.run([shell], input=stdin, capture_output=True, text=True, env={})
|
||||
|
||||
def perform_check(expected: sp.CompletedProcess, actual: sp.CompletedProcess):
|
||||
assert expected.returncode == actual.returncode, f"EXIT_CODE_ERR: expected {expected.returncode}, got {actual.returncode}"
|
||||
assert expected.stdout == actual.stdout, f"STDOUT_ERR\n{diff(expected.stdout, actual.stdout)}"
|
||||
assert len(expected.stderr) == len(actual.stderr) or (len(expected.stderr) != 0 and len(actual.stderr) != 0), f"STDERR_EMPTY"
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser("bshTestSuite")
|
||||
parser.add_argument('--binary', required=True, type=Path)
|
||||
parser.add_argument('--tests', required=True, nargs='+', default=[])
|
||||
args = parser.parse_args()
|
||||
|
||||
binary_path = args.binary.absolute()
|
||||
|
||||
tests=0
|
||||
errors=0
|
||||
|
||||
for test in args.tests:
|
||||
with open(test, 'r') as file:
|
||||
testsuite = [TestCase(**testcase) for testcase in yaml.safe_load(file)]
|
||||
print(f"\033[6m## \033[0m\033[34m{test}\033[97m")
|
||||
for testcase in testsuite:
|
||||
tests += 1
|
||||
input = testcase.input
|
||||
if (testcase.file):
|
||||
with open(testcase.file, 'r') as file:
|
||||
input = file.read()
|
||||
actual = run_shell(binary_path, input)
|
||||
expected = run_shell("dash", input)
|
||||
try:
|
||||
perform_check(expected, actual)
|
||||
except AssertionError as e:
|
||||
print(f"\033[1m[ \033[31mKO\033[97m ] \033[0m{testcase.name}\n{e}")
|
||||
errors += 1
|
||||
else:
|
||||
print(f"\033[1m[ \033[92mOK\033[97m ] \033[0m{testcase.name}")
|
||||
print("")
|
||||
|
||||
if (errors > 1):
|
||||
s = 's'
|
||||
else:
|
||||
s = ''
|
||||
print(f"\n\033[33m✗ \033[34m{tests}\033[97m tests performed, \033[31m{errors}\033[97m error{s}\033[0m")
|
||||
exit(errors)
|
|
@ -0,0 +1,35 @@
|
|||
- name: basic
|
||||
input: ls | grep Makefile
|
||||
|
||||
- name: double pipe
|
||||
input: ls | grep Makefile | grep Makefile
|
||||
|
||||
- name: expand
|
||||
input: a="Makefile"; ls | grep "$a"
|
||||
|
||||
- name: multiple file
|
||||
input: ls -R src | grep src
|
||||
|
||||
- name: multiple file globing failed
|
||||
input: ls -R src | grep *.c
|
||||
|
||||
- name: multiple file globing
|
||||
input: ls -R src | grep .c
|
||||
|
||||
- name: pipe builtin pass
|
||||
input: echo "test|pass" | grep pass
|
||||
|
||||
- name: pipe builtin pass
|
||||
input: echo "test|pass" | grep failed
|
||||
|
||||
- name: 3 pipes
|
||||
input: cat Makefile | grep "@" | grep "echo"
|
||||
|
||||
- name: most used
|
||||
input: cat /root/.bash_history | cut -d' ' -f1 | sort | uniq -c | tr -s ' ' | cut -c 2- | sort -nr | head
|
||||
|
||||
- name: no builtins
|
||||
input: ls | grep -E "*.c"
|
||||
|
||||
- name: mutltiple no builtins
|
||||
input: ls | grep "s" | grep "bui"
|
|
@ -0,0 +1 @@
|
|||
pyyaml
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue