biiiig update folks
This commit is contained in:
parent
52d87dea1b
commit
6ec1cc78f7
4 changed files with 682 additions and 135 deletions
2
.clangd
2
.clangd
|
|
@ -1,2 +1,2 @@
|
|||
CompileFlags:
|
||||
Add: ["-xc"]
|
||||
Add: ["-xc", "-std=gnu23"]
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -1,5 +1,5 @@
|
|||
CC=$(shell which clang)
|
||||
CFLAGS:=$(CFLAGS)
|
||||
CFLAGS:=$(CFLAGS) --std=gnu23
|
||||
BINDIR=bin
|
||||
|
||||
main=$(BINDIR)/main
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 7343039dc975ebbd3c6660527da0f3780ff432bc
|
||||
Subproject commit 83210aa292843c67e768f03ba3390efe388505bd
|
||||
811
src/main.c
811
src/main.c
|
|
@ -1,76 +1,221 @@
|
|||
#include <bits/types/idtype_t.h>
|
||||
#include <bits/types/siginfo_t.h>
|
||||
// vim:fileencoding=utf-8:foldmethod=marker
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <wait.h>
|
||||
|
||||
#include "linked-list/linked_list.c"
|
||||
|
||||
// Options {{{
|
||||
// 90% non implementate, qui per usi futuri
|
||||
struct options {
|
||||
_Bool a;
|
||||
_Bool C;
|
||||
_Bool e;
|
||||
_Bool f;
|
||||
_Bool h;
|
||||
_Bool n;
|
||||
_Bool u;
|
||||
_Bool v;
|
||||
_Bool x;
|
||||
};
|
||||
|
||||
struct options optStatus = {0};
|
||||
// }}}
|
||||
|
||||
// Logging {{{
|
||||
|
||||
// semplice funzione di logging, niente di speciale
|
||||
#if DEBUG
|
||||
#define LOG(...) printf(__VA_ARGS__)
|
||||
#define LOG(...) \
|
||||
do { \
|
||||
printf(__VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
// funzione che esegue lo snippet di codice solo qualora il logging sia
|
||||
// abilitato
|
||||
#define LOG_WRAP(x, ...) x(__VA_ARGS__)
|
||||
// opposto di LOG_WRAP
|
||||
#define LOG_WRAP_REVERSE(x, ...)
|
||||
#else
|
||||
#define LOG(...)
|
||||
#define LOG_WRAP(x, ...)
|
||||
#define LOG_WRAP_REVERSE(x, ...) x(__VA_ARGS__)
|
||||
#endif
|
||||
// }}}
|
||||
|
||||
// List extensions {{{
|
||||
|
||||
// estensione alla lista che va a creare una funzione toarray e lo struct
|
||||
// associato
|
||||
#define CREATE_TOARRAY(name, type, format) \
|
||||
struct name##_array_args { \
|
||||
_Bool backwards; \
|
||||
int len; \
|
||||
type* array; \
|
||||
}; \
|
||||
int _##name##_to_array /*a commment is needed to make the syntax highliter \
|
||||
work after this*/ \
|
||||
(name##_node * node, void* vargs) { \
|
||||
struct name##_array_args* args = (struct name##_array_args*)vargs; \
|
||||
LOG("len: %i, backwards: %b, val: " #format \
|
||||
", node: %p, node_next: %p\n", \
|
||||
args->len, args->backwards, node->val, node, node->next); \
|
||||
\
|
||||
if (!args->backwards) { \
|
||||
args->len++; \
|
||||
if (!node->next) { \
|
||||
args->backwards = 1; \
|
||||
args->array = malloc(args->len * sizeof(type)); \
|
||||
if (!args->array) return -1; \
|
||||
} \
|
||||
} else { \
|
||||
args->len--; \
|
||||
args->array[args->len - 1] = node->val; \
|
||||
free(node); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
type* name##_to_array(name* str) { \
|
||||
struct name##_array_args args = {.backwards = 0, .len = 1}; \
|
||||
if (foreach_twopass_##name(str, _##name##_to_array, &args)) \
|
||||
return NULL; \
|
||||
str->head = NULL; \
|
||||
return args.array; \
|
||||
}
|
||||
// }}}
|
||||
|
||||
// Strings {{{
|
||||
// clang-format off
|
||||
// clang-format va a dare problemi con il %
|
||||
CREATE_LIST(string_array, char*);
|
||||
CREATE_TOARRAY(string_array, char*, %s);
|
||||
CREATE_LIST(string, char);
|
||||
CREATE_TOARRAY(string, char, %c);
|
||||
// clang-format on
|
||||
|
||||
//}}}
|
||||
|
||||
// Alias type {{{
|
||||
|
||||
// clang-format off
|
||||
// uno struct per gli alias, contiene il nome e
|
||||
// o una funzione che va a eseguire (per esempio exit, che va a eseguire __exit())
|
||||
// o un comando e argomenti da aggiungere (per esempio `alias ll="ls -l"
|
||||
// clang-format on
|
||||
|
||||
struct alias {
|
||||
char* name;
|
||||
char* command;
|
||||
char** args;
|
||||
int (*fn)(char**);
|
||||
};
|
||||
|
||||
// tipo non stampabile facilmente, rimossa temporanemante la funzione di log
|
||||
#undef LOG
|
||||
#define LOG(...)
|
||||
CREATE_LIST(alias_list, struct alias)
|
||||
CREATE_TOARRAY(alias_list, struct alias, '\0')
|
||||
#if DEBUG
|
||||
#undef LOG
|
||||
#define LOG(...) \
|
||||
do { \
|
||||
printf(__VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define CREATE_TOARRAY(name, type, format) \
|
||||
struct name##_array_args { \
|
||||
_Bool backwards; \
|
||||
int len; \
|
||||
type* array; \
|
||||
}; \
|
||||
int _##name##_to_array(name##_node* node, void* vargs) { \
|
||||
struct name##_array_args* args = (struct name##_array_args*)vargs; \
|
||||
LOG("len: %i, backwards: %b, val: " #format \
|
||||
", node: %p, node_next: %p\n", \
|
||||
args->len, args->backwards, node->val, node, node->next); \
|
||||
\
|
||||
if (!args->backwards) { \
|
||||
args->len++; \
|
||||
if (!node->next) { \
|
||||
args->backwards = 1; \
|
||||
args->array = malloc(args->len * sizeof(type)); \
|
||||
if (!args->array) return -1; \
|
||||
} \
|
||||
} else { \
|
||||
args->len--; \
|
||||
args->array[args->len - 1] = node->val; \
|
||||
free(node); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
type* name##_to_array(name* str) { \
|
||||
struct name##_array_args args = {.backwards = 0, .len = 1}; \
|
||||
if (foreach_twopass_##name(str, _##name##_to_array, &args)) \
|
||||
return NULL; \
|
||||
return args.array; \
|
||||
}
|
||||
alias_list aliases = {.head = NULL};
|
||||
|
||||
// clang-format off
|
||||
CREATE_LIST(string_array, char*)
|
||||
CREATE_TOARRAY(string_array, char*, %s)
|
||||
CREATE_LIST(string, char)
|
||||
CREATE_TOARRAY(string, char, %c)
|
||||
int _findalias(alias_list_node* node, void* vargs) {
|
||||
alias_list_node** args = (alias_list_node**)vargs; // va a fare il casting del pointer
|
||||
char *n, *a; // va a creare delle variabili temporanee
|
||||
for (n = node->val.name, a = (*args)->val.name; *n && *a; n++, a++) {
|
||||
if (*n != *a) return 0; // e a confrontarle
|
||||
}
|
||||
|
||||
if (*n ^ *a) return 0; // in case siamo arrivati alla fine dell'array invece di avere trovato il nome giusto
|
||||
|
||||
*args = node; // impostiamo gli argomenti al nodo con nome giusto
|
||||
return 1; // ritorniamo 1 così da interrompere l'esecuzione del ciclo foreach
|
||||
}
|
||||
|
||||
// funzione che va a trovare un alias
|
||||
alias_list_node* findalias(char* name) {
|
||||
struct alias val = {.name = name}; // va a creare un alias con nome come passato
|
||||
alias_list_node node = {.val = val}; // va a creare un node
|
||||
alias_list_node* ptr = &node; // e un pointer per contenerlo
|
||||
if (foreach_alias_list(&aliases, *_findalias, &ptr)) return ptr; // se trovato viene ritornato
|
||||
return NULL; // senno ritorna null
|
||||
}
|
||||
// clang-format on
|
||||
int last_exit = 0;
|
||||
//}}}
|
||||
|
||||
// Builtin aliases {{{
|
||||
|
||||
// funzione exit come definita da POSIX
|
||||
int __exit(char** args) {
|
||||
if (args[1]) exit(atoi(args[1]));
|
||||
exit(last_exit);
|
||||
}
|
||||
struct alias exit_alias = {
|
||||
.name = "exit",
|
||||
.fn = __exit,
|
||||
};
|
||||
|
||||
// ancora non implementata, out of scope
|
||||
int set(char** args) {
|
||||
if (args[1]) {
|
||||
} else {
|
||||
return printf(
|
||||
"a: %b,\n"
|
||||
"C: %b,\n"
|
||||
"e: %b,\n"
|
||||
"f: %b,\n"
|
||||
"h: %b,\n"
|
||||
"n: %b,\n"
|
||||
"u: %b,\n"
|
||||
"v: %b,\n"
|
||||
"x: %b,\n",
|
||||
optStatus.a, optStatus.C, optStatus.e, optStatus.f, optStatus.h,
|
||||
optStatus.n, optStatus.u, optStatus.v, optStatus.x
|
||||
);
|
||||
}
|
||||
}
|
||||
struct alias set_alias = {
|
||||
.name = "set",
|
||||
.fn = set,
|
||||
};
|
||||
|
||||
//}}}
|
||||
|
||||
// Variable type {{{
|
||||
struct variable {
|
||||
char* name;
|
||||
char* value;
|
||||
};
|
||||
// clang-format off
|
||||
|
||||
// tipo non stampabile facilmente, rimossa temporanemante la funzione di log
|
||||
#undef LOG
|
||||
#define LOG(...)
|
||||
CREATE_LIST(variable_list, struct variable)
|
||||
CREATE_TOARRAY(variable_list, struct variable, '\0')
|
||||
#if DEBUG
|
||||
#undef LOG
|
||||
#define LOG(...) printf(__VA_ARGS__)
|
||||
#define LOG(...) \
|
||||
do { \
|
||||
printf(__VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
#endif
|
||||
// clang-format on
|
||||
variable_list variables = {.head = NULL};
|
||||
|
||||
const char* getorsetenv(const char* name, const char* val) {
|
||||
|
|
@ -80,130 +225,263 @@ const char* getorsetenv(const char* name, const char* val) {
|
|||
return val;
|
||||
}
|
||||
|
||||
// implementazione equivalente alla ricerca di alias
|
||||
int _find_variable(variable_list_node* node, void* args) {
|
||||
struct variable* arg = (struct variable*)args;
|
||||
struct variable* arg = *((struct variable**)args);
|
||||
LOG("checking %s against %s\n", node->val.name, arg->name);
|
||||
if (strlen(node->val.name) == strlen(arg->name)) {
|
||||
for (int i = 0; node->val.name[i] && arg->name[i]; i++) {
|
||||
if (node->val.name[i] != arg->name[i]) return 0;
|
||||
}
|
||||
arg->value = node->val.value;
|
||||
*arg = node->val;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
char* find_variable(char* name) {
|
||||
struct variable ret = {.name = name};
|
||||
if (foreach_variable_list(&variables, _find_variable, &ret))
|
||||
return ret.value;
|
||||
LOG("searching for %s\n", name);
|
||||
if (!name) return NULL;
|
||||
struct variable tmp = {.name = name};
|
||||
struct variable* ret = &tmp;
|
||||
if (foreach_variable_list(&variables, _find_variable, &ret)) {
|
||||
LOG("found %s", ret->value);
|
||||
return ret->value;
|
||||
}
|
||||
LOG("falling back to getenv\n");
|
||||
return getenv(name);
|
||||
}
|
||||
|
||||
// come il find_variable ma senza fallback
|
||||
char* find_local_variable(char* name) {
|
||||
LOG("searching for local %s\n", name);
|
||||
if (!name) return NULL;
|
||||
struct variable tmp = {.name = name};
|
||||
struct variable* ret = &tmp;
|
||||
if (foreach_variable_list(&variables, _find_variable, &ret))
|
||||
return ret->value;
|
||||
LOG("not found\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// trova e rimpiazza una variabile
|
||||
int find_and_replace_local_variable(char* name, char* replacement) {
|
||||
LOG("searching for local %s\n", name);
|
||||
if (!name) return 1;
|
||||
struct variable tmp = {.name = name};
|
||||
struct variable* ret = &tmp;
|
||||
if (foreach_variable_list(&variables, _find_variable, &ret)) {
|
||||
free(ret->value);
|
||||
ret->value = replacement;
|
||||
return 0;
|
||||
}
|
||||
LOG("not found\n");
|
||||
return 1;
|
||||
}
|
||||
//}}}
|
||||
|
||||
_Bool is_subshell = 0;
|
||||
|
||||
// Text processing {{{
|
||||
#define throw(ret, ...) \
|
||||
{ \
|
||||
do { \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
return ret; \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
#define CHECK(var, val) var& val
|
||||
|
||||
#define ESCAPE 0x1
|
||||
#define ESCAPE 1
|
||||
#define SET_ESCAPE situation ^= ESCAPE
|
||||
#define IS_ESCAPED CHECK(situation, ESCAPE)
|
||||
|
||||
#define QUOTED 0x2
|
||||
#define QUOTED 2
|
||||
#define SET_QUOTED situation ^= QUOTED
|
||||
#define IS_QUOTED CHECK(situation, QUOTED)
|
||||
|
||||
#define DOUBLE_QUOTED 0x4
|
||||
#define DOUBLE_QUOTED 4
|
||||
#define SET_DOUBLE_QUOTED situation ^= DOUBLE_QUOTED
|
||||
#define IS_DOUBLE_QUOTED CHECK(situation, DOUBLE_QUOTED)
|
||||
|
||||
#define VAR 0x8
|
||||
#define VAR 8
|
||||
#define SET_VAR situation ^= VAR
|
||||
#define IS_VAR CHECK(situation, VAR)
|
||||
#define RESOLVE_VAR \
|
||||
{ \
|
||||
char* varname = string_to_array(&var_buffer); \
|
||||
char* content = find_variable(varname); \
|
||||
if (content) { \
|
||||
for (; *content; content++) { \
|
||||
append_string(&buf, *content); \
|
||||
} \
|
||||
} \
|
||||
free(varname); \
|
||||
SET_VAR; \
|
||||
}
|
||||
|
||||
#define RESOLVE_VAR() \
|
||||
do { \
|
||||
append_string(&var_buffer, 0); /* si aggiunge un terminator */ \
|
||||
char* varname = string_to_array(&var_buffer); \
|
||||
char* content = find_variable(varname); /* cerchiamo la variabile */ \
|
||||
if (content) { /* se la troviamo */ \
|
||||
for (; *content; content++) { \
|
||||
append_string( \
|
||||
&buf, *content \
|
||||
); /* aggiungiamo il suo valore al buffer */ \
|
||||
} \
|
||||
} \
|
||||
free(varname); \
|
||||
SET_VAR; \
|
||||
} while (0)
|
||||
|
||||
#define DUMP_UNINTERRUPTED() \
|
||||
do { \
|
||||
append_string(&uninterrupted, 0); /* aggiungiamo un terminatore */ \
|
||||
char* dump_tofree = string_to_array(&uninterrupted); \
|
||||
LOG("dump_tofree: %s", dump_tofree); \
|
||||
if (dump_tofree) { \
|
||||
if (dump) { /* se siamo dopo un '=' */ \
|
||||
LOG(", dump: %s\n", dump); \
|
||||
struct variable tmpvar = {.name = dump, .value = dump_tofree}; \
|
||||
append_variable_list( \
|
||||
&variables, tmpvar \
|
||||
); /* aggiungiamo la variabile */ \
|
||||
dump = NULL; \
|
||||
} else { \
|
||||
LOG("\n"); \
|
||||
for (char* tmp = dump_tofree; *dump; \
|
||||
dump++) /* aggiungiamo al buffer normale */ \
|
||||
append_string(&buf, *tmp); \
|
||||
free(dump_tofree); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
char* resolve(const char* str) {
|
||||
char situation = 0;
|
||||
|
||||
string buf = {.head = NULL};
|
||||
char* dump = NULL;
|
||||
|
||||
string var_buffer = {.head = NULL};
|
||||
string uninterrupted = {.head = NULL};
|
||||
|
||||
// switch case di ogni carattere speciale definito da posix supportato
|
||||
for (const char* current = str; *current; current++) {
|
||||
LOG("situation_pre: %i, current: %p = %c", situation, current,
|
||||
*current);
|
||||
switch (*current) {
|
||||
case '#':
|
||||
if (IS_VAR)
|
||||
RESOLVE_VAR();
|
||||
else
|
||||
DUMP_UNINTERRUPTED();
|
||||
if (IS_QUOTED) {
|
||||
append_string(&buf, '#');
|
||||
break;
|
||||
}
|
||||
if (IS_DOUBLE_QUOTED) {
|
||||
append_string(&buf, '#');
|
||||
break;
|
||||
}
|
||||
if (IS_ESCAPED) {
|
||||
append_string(&buf, '#');
|
||||
break;
|
||||
}
|
||||
goto FIN;
|
||||
case '\\':
|
||||
if (IS_VAR)
|
||||
RESOLVE_VAR();
|
||||
else
|
||||
DUMP_UNINTERRUPTED();
|
||||
if (IS_QUOTED) {
|
||||
append_string(&buf, '\\');
|
||||
break;
|
||||
}
|
||||
if (IS_VAR)
|
||||
SET_VAR;
|
||||
else if (IS_ESCAPED)
|
||||
if (IS_DOUBLE_QUOTED) {
|
||||
switch (*(++current)) {
|
||||
case '$':
|
||||
case '`':
|
||||
case '"':
|
||||
case '\\':
|
||||
append_string(&buf, *current);
|
||||
default:
|
||||
append_string(&buf, '\\');
|
||||
current--;
|
||||
}
|
||||
break;
|
||||
} else if (IS_ESCAPED)
|
||||
append_string(&buf, '\\');
|
||||
SET_ESCAPE;
|
||||
goto END;
|
||||
|
||||
case '\'':
|
||||
if (IS_VAR) SET_VAR;
|
||||
if (IS_QUOTED && IS_ESCAPED)
|
||||
throw(
|
||||
NULL,
|
||||
"attempt to escape a \"'\" while inside quotes, this "
|
||||
"goes against posix standard"
|
||||
);
|
||||
if (IS_VAR)
|
||||
RESOLVE_VAR();
|
||||
else
|
||||
DUMP_UNINTERRUPTED();
|
||||
SET_QUOTED;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
if (IS_VAR) SET_VAR;
|
||||
if (IS_VAR)
|
||||
RESOLVE_VAR();
|
||||
else
|
||||
DUMP_UNINTERRUPTED();
|
||||
if (IS_ESCAPED || IS_QUOTED) append_string(&buf, '"');
|
||||
SET_DOUBLE_QUOTED;
|
||||
break;
|
||||
|
||||
case '$':
|
||||
DUMP_UNINTERRUPTED();
|
||||
if (IS_ESCAPED || IS_QUOTED)
|
||||
append_string(&buf, '$');
|
||||
else if (IS_VAR) {
|
||||
if (*(current - 1) == '$') {
|
||||
for (pid_t id = (!is_subshell) ? getpid() : getppid();
|
||||
for (pid_t id = (!is_subshell)
|
||||
? getpid()
|
||||
: getppid(); // non ancora in uso
|
||||
id > 0; id = id / 10)
|
||||
append_string(&buf, (id % 10) + 48);
|
||||
} else {
|
||||
RESOLVE_VAR;
|
||||
RESOLVE_VAR();
|
||||
SET_VAR;
|
||||
}
|
||||
} else {
|
||||
SET_VAR;
|
||||
}
|
||||
break;
|
||||
|
||||
case '=':
|
||||
if (IS_QUOTED || IS_DOUBLE_QUOTED || IS_ESCAPED) {
|
||||
if (IS_VAR) RESOLVE_VAR();
|
||||
append_string(&buf, *current);
|
||||
} else {
|
||||
dump = string_to_array(&uninterrupted);
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
if (IS_ESCAPED) break;
|
||||
|
||||
case ' ':
|
||||
case ';':
|
||||
case ',':
|
||||
case '.':
|
||||
if (IS_VAR) RESOLVE_VAR;
|
||||
default:
|
||||
if (IS_VAR)
|
||||
append_string(&var_buffer, *current);
|
||||
RESOLVE_VAR();
|
||||
else
|
||||
DUMP_UNINTERRUPTED();
|
||||
append_string(&buf, *current);
|
||||
break;
|
||||
default:
|
||||
if ((*current >= 'A' && *current <= 'Z') ||
|
||||
(*current >= 'a' && *current <= 'z') ||
|
||||
(*current >= '0' && *current <= '9'))
|
||||
|
||||
if (IS_VAR)
|
||||
append_string(&var_buffer, *current);
|
||||
else
|
||||
append_string(&uninterrupted, *current);
|
||||
else {
|
||||
if (IS_VAR)
|
||||
SET_VAR;
|
||||
else
|
||||
DUMP_UNINTERRUPTED();
|
||||
append_string(&buf, *current);
|
||||
}
|
||||
}
|
||||
if (IS_ESCAPED) SET_ESCAPE;
|
||||
END:
|
||||
|
|
@ -211,7 +489,9 @@ char* resolve(const char* str) {
|
|||
LOG_WRAP(print_string, &buf, "'%c', ");
|
||||
LOG(" }, situation: %i\n", situation);
|
||||
}
|
||||
if (IS_VAR) RESOLVE_VAR;
|
||||
if (IS_VAR) RESOLVE_VAR();
|
||||
FIN:
|
||||
DUMP_UNINTERRUPTED();
|
||||
append_string(&buf, 0);
|
||||
LOG("string: { ");
|
||||
LOG_WRAP(print_string, &buf, "'%c', ");
|
||||
|
|
@ -220,42 +500,150 @@ char* resolve(const char* str) {
|
|||
LOG("parsed_internal: \"%s\"\n", parsed);
|
||||
return parsed;
|
||||
}
|
||||
//}}}
|
||||
|
||||
int prompt() {
|
||||
return fprintf(stderr, "%s", resolve(getorsetenv("PS1", "\\$ ")));
|
||||
}
|
||||
// Io {{{
|
||||
// print della prompt inpostand anche la variabile se non ancora impostata
|
||||
int prompt() { return fprintf(stderr, "%s", getorsetenv("PS1", "$ ")); }
|
||||
|
||||
// semplice funzione di imput
|
||||
FILE* inputFile;
|
||||
char* input() {
|
||||
char* line = NULL;
|
||||
size_t n = 0;
|
||||
if (getline(&line, &n, stdin) < 0 && ferror(stdin)) {
|
||||
if (getline(&line, &n, inputFile) <= 0) {
|
||||
free(line);
|
||||
return NULL;
|
||||
};
|
||||
return line;
|
||||
}
|
||||
|
||||
int exec(char* line) {
|
||||
LOG("line: %s\n", line);
|
||||
// clear dell'imput quando viene premuto in interactive mod
|
||||
void clearInput(int sig) {
|
||||
signal(sig, SIG_IGN);
|
||||
LOG_WRAP(char c; do {} while, 0);
|
||||
fseek(stdin, 0, SEEK_END);
|
||||
fprintf(stderr, "\033[2K");
|
||||
fprintf(stderr, "\r");
|
||||
prompt();
|
||||
signal(sig, clearInput);
|
||||
}
|
||||
|
||||
//}}}
|
||||
|
||||
// Execute {{{
|
||||
|
||||
// whitespace checker
|
||||
_Bool isjustwhite(char* str) {
|
||||
for (; *str; str++)
|
||||
if (*str != ' ' && *str != '\n') return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int _exec(char* line);
|
||||
int __exec(char** argv, char* command, char* fromalias);
|
||||
// exec() = __exec in un caso e _exec nell'altro
|
||||
#define FIRST(X, ...) X
|
||||
#define exec(...) \
|
||||
_Generic(FIRST(__VA_ARGS__), char**: __exec, default: _exec)(__VA_ARGS__)
|
||||
|
||||
int __exec(char** argv, char* command, char* fromalias) {
|
||||
if (fromalias) {
|
||||
for (char *c = command, *f = fromalias; *c && *f; c++, f++) {
|
||||
if (*c != *f) {
|
||||
goto ALIAS;
|
||||
}
|
||||
}
|
||||
goto SKIP;
|
||||
}
|
||||
// se dobbiamo trovare l'alias
|
||||
ALIAS:
|
||||
alias_list_node* node = findalias(command);
|
||||
if (!node) goto SKIP;
|
||||
|
||||
if (node->val.fn) {
|
||||
return (*node->val.fn)(argv); // se troviamo un builtin lo eseguiamo
|
||||
}
|
||||
|
||||
// troviamo lunghezza degli argomenti
|
||||
int argc;
|
||||
for (argc = 0; (node->val.args)[argc++];);
|
||||
char** new_argv = malloc(argc);
|
||||
// li spostiamo tutti nella nuova argv
|
||||
memcpy(new_argv, argv, argc);
|
||||
|
||||
// rifacciamo lo stesso con gli argomenti passati nella cli
|
||||
int old_argc;
|
||||
for (old_argc = -1; argv[++old_argc];);
|
||||
new_argv = realloc(new_argv, argc + old_argc);
|
||||
memcpy(new_argv + argc, argv + 1, old_argc);
|
||||
return exec(new_argv, node->val.command, command);
|
||||
|
||||
SKIP:
|
||||
LOG("\n");
|
||||
if (isjustwhite(command)) return 0; // in caso sia solo \n e ' '
|
||||
if (optStatus.x) {
|
||||
// print dei commandi
|
||||
printf("%s ", command);
|
||||
for (char** tmp = argv; *tmp; tmp++) {
|
||||
printf("%s ", *tmp);
|
||||
};
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// vfork del processo
|
||||
pid_t proc = vfork();
|
||||
|
||||
// se non ѐ andato a buon fine ritorna errore
|
||||
if (proc == -1) return -1;
|
||||
|
||||
if (proc) {
|
||||
// se siamo il parent
|
||||
siginfo_t infop;
|
||||
int ret = waitid(
|
||||
P_PID, proc, &infop, WEXITED | WSTOPPED
|
||||
); /* "aspettiamo" il processo (solo per ottenere il return, in realtà
|
||||
vfork aspetta di default) */
|
||||
if (ret) return -1; // se non ci riusciamo, errore
|
||||
last_exit = infop.si_status;
|
||||
if (infop.si_status)
|
||||
fprintf(
|
||||
stderr, "Process returned with error code %i\n", infop.si_status
|
||||
);
|
||||
LOG("\n\n");
|
||||
return 0;
|
||||
} else {
|
||||
// se siamo il child process
|
||||
int ret = execvp(command, argv); // execvp
|
||||
if (ret == -1)
|
||||
fprintf(stderr, "errno: %i\n", errno); // se non ha funzionato
|
||||
_exit(ret);
|
||||
}
|
||||
}
|
||||
|
||||
// separazione di argouments e command
|
||||
void split(char* line, char*** strargs_ret, char** orig_ret) {
|
||||
_Bool todo = 1;
|
||||
string_array args = {.head = NULL};
|
||||
string arg = {.head = NULL};
|
||||
char* orig = line;
|
||||
for (; *line != '\n'; line++) {
|
||||
LOG("line: %c, \n", *line);
|
||||
for (; *line != '\n' && *line; line++) {
|
||||
LOG("main.split.for: line: %c, ", *line);
|
||||
if (*line == ' ') {
|
||||
if (todo) {
|
||||
orig[line - orig] = 0;
|
||||
todo = 0;
|
||||
} else {
|
||||
LOG_WRAP(print_string, &arg, "main.split.for.arg: %c\n");
|
||||
append_string(&arg, 0);
|
||||
append_string_array(&args, string_to_array(&arg));
|
||||
}
|
||||
} else if (!todo) {
|
||||
LOG("appending; \n");
|
||||
append_string(&arg, *line);
|
||||
}
|
||||
}
|
||||
LOG("line: %c, \n", *line);
|
||||
LOG("main.split: line: %c, ", *line);
|
||||
if (todo) {
|
||||
orig[line - orig] = 0;
|
||||
todo = 0;
|
||||
|
|
@ -267,52 +655,211 @@ int exec(char* line) {
|
|||
append_string_array(&args, NULL);
|
||||
LOG("args: { ");
|
||||
LOG_WRAP(print_string_array, &args, "%s, ");
|
||||
LOG(" }");
|
||||
LOG(" }\n");
|
||||
char** strargs = string_array_to_array(&args);
|
||||
for (int i = 0; strargs[i]; i++) {
|
||||
LOG("strargs[%i] %s", i, strargs[i]);
|
||||
LOG("strargs[%i] %s\n", i, strargs[i]);
|
||||
}
|
||||
pid_t proc = vfork();
|
||||
if (proc == -1) return -1;
|
||||
if (proc) {
|
||||
siginfo_t infop;
|
||||
int ret = waitid(P_PID, proc, &infop, WEXITED | WSTOPPED);
|
||||
if (ret) return -1;
|
||||
if (infop.si_status)
|
||||
fprintf(
|
||||
stderr, "Process returned with error code %i\n", infop.si_status
|
||||
);
|
||||
return 0;
|
||||
} else {
|
||||
int ret = execvp(orig, strargs);
|
||||
if (ret == -1) fprintf(stderr, "errno: %i\n", errno);
|
||||
_exit(ret);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
LOG("sizeof(string_node): %i, sizeof(string): %i, "
|
||||
"sizeof(string_array_args): %i\n",
|
||||
sizeof(string_node), sizeof(string), sizeof(struct string_array_args));
|
||||
LOG("sizeof(string_array_node): %i, sizeof(string_array): %i, "
|
||||
"sizeof(string_array_array_args): %i\n",
|
||||
sizeof(string_array_node), sizeof(string_array),
|
||||
sizeof(struct string_array_array_args));
|
||||
LOG("sizeof(variable): %i, sizeof(variable_list): %i, "
|
||||
"sizeof(variable_list_node): %i, sizeof(variable_list_array_args): "
|
||||
"%i\n",
|
||||
sizeof(struct variable), sizeof(variable_list),
|
||||
sizeof(variable_list_node), sizeof(struct variable_list_array_args));
|
||||
LOG("test, resolve \"\\$\", \"%s\"\n", resolve("\\$ "));
|
||||
for (;;) {
|
||||
prompt();
|
||||
char* line = input();
|
||||
LOG("input: %s", line);
|
||||
if (!line) return errno;
|
||||
char* parsed = resolve(line);
|
||||
LOG("parsed: %s", parsed);
|
||||
if (!parsed) return -1;
|
||||
int ret = exec(parsed);
|
||||
if (ret) return ret;
|
||||
}
|
||||
*strargs_ret = strargs;
|
||||
*orig_ret = orig;
|
||||
}
|
||||
int _exec(char* line) {
|
||||
LOG("line: %s\n", line);
|
||||
char **strargs, *orig;
|
||||
split(line, &strargs, &orig);
|
||||
return exec(strargs, orig, NULL);
|
||||
}
|
||||
//}}}
|
||||
|
||||
// Enums {{{
|
||||
|
||||
typedef enum {
|
||||
File,
|
||||
FakeFile,
|
||||
Stdin,
|
||||
} Mode;
|
||||
|
||||
typedef enum {
|
||||
Normal,
|
||||
ScriptArgs,
|
||||
} ParseMode;
|
||||
|
||||
// }}}
|
||||
|
||||
// Convarsions {{{
|
||||
char* itoa(int integer) {
|
||||
if (!integer) return "0";
|
||||
string tmp;
|
||||
for (; integer; integer /= 10) append_string(&tmp, integer % 10 + '0');
|
||||
return string_to_array(&tmp);
|
||||
}
|
||||
// }}}
|
||||
|
||||
_Bool interactive = false;
|
||||
int main(int argc, char** argv) {
|
||||
LOG("set arg0 to %s\n", argv[0]);
|
||||
struct variable arg0 = {.name = "0", .value = argv[0]};
|
||||
append_variable_list(&variables, arg0);
|
||||
inputFile = stdin;
|
||||
Mode mode = Stdin;
|
||||
ParseMode parsemode = Normal;
|
||||
|
||||
_Bool c = false;
|
||||
_Bool s = false;
|
||||
|
||||
// Initialize builtins {{{
|
||||
|
||||
append_alias_list(&aliases, exit_alias);
|
||||
append_alias_list(&aliases, set_alias);
|
||||
|
||||
// }}}
|
||||
|
||||
// Handle argouments {{{
|
||||
if (argc - 1) {
|
||||
int i;
|
||||
for (i = 1; i < argc && parsemode == Normal; i++) {
|
||||
LOG("i: %i, argv[i]: %s\n", i, argv[i]);
|
||||
switch (*(argv[i])) {
|
||||
case '-':
|
||||
char c;
|
||||
if ((c = argv[i][1])) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
if (s)
|
||||
throw(
|
||||
1,
|
||||
"only specify either -s or -c, not both"
|
||||
);
|
||||
c = true;
|
||||
++i;
|
||||
LOG("i: %i, argv[i](command): %s\n", i,
|
||||
argv[i]);
|
||||
inputFile =
|
||||
fmemopen(argv[i], strlen(argv[i]), "r");
|
||||
if (!inputFile)
|
||||
throw(
|
||||
errno,
|
||||
"failed to execute commands provided "
|
||||
"by -c"
|
||||
);
|
||||
mode = FakeFile;
|
||||
parsemode = ScriptArgs;
|
||||
|
||||
break;
|
||||
case 's':
|
||||
if (c)
|
||||
throw(
|
||||
1,
|
||||
"only specify either -s or -c, not both"
|
||||
);
|
||||
s = true;
|
||||
break;
|
||||
case 'i':
|
||||
interactive = true;
|
||||
break;
|
||||
case 'a':
|
||||
optStatus.a = true;
|
||||
break;
|
||||
case 'n':
|
||||
optStatus.n = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
parsemode = ScriptArgs;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
struct stat info = {0};
|
||||
if (stat(argv[i], &info)) return errno;
|
||||
if ((info.st_mode & S_IXUSR /*owner has execution*/ &&
|
||||
info.st_uid == getuid()) ||
|
||||
(info.st_mode & S_IXGRP /*group has execution*/ &&
|
||||
info.st_gid == getgid()) ||
|
||||
(info.st_mode & S_IXOTH /*other has execution*/)) {
|
||||
inputFile = fopen(argv[i], "r");
|
||||
if (!inputFile)
|
||||
throw(errno, "failed to open file provided");
|
||||
} else
|
||||
throw(1, "file specified is not executable");
|
||||
mode = File;
|
||||
parsemode = ScriptArgs;
|
||||
find_and_replace_local_variable("0", argv[i]);
|
||||
}
|
||||
}
|
||||
if (i < argc && mode == Stdin) {
|
||||
struct stat info = {0};
|
||||
if (stat(argv[i], &info)) return errno;
|
||||
if ((info.st_mode & S_IXUSR /*owner has execution*/ &&
|
||||
info.st_uid == getuid()) ||
|
||||
(info.st_mode & S_IXGRP /*group has execution*/ &&
|
||||
info.st_gid == getgid()) ||
|
||||
(info.st_mode & S_IXOTH /*other has execution*/)) {
|
||||
inputFile = fopen(argv[i], "r");
|
||||
if (!inputFile) throw(errno, "failed to open file provided");
|
||||
}
|
||||
mode = File;
|
||||
find_and_replace_local_variable("0", argv[i]);
|
||||
i++;
|
||||
}
|
||||
for (int j = 1; i < argc; i++, j++) {
|
||||
struct variable argn = {.name = itoa(j), .value = argv[i]};
|
||||
append_variable_list(&variables, argn);
|
||||
}
|
||||
}
|
||||
|
||||
if (getgid() != getegid() || getuid() != geteuid())
|
||||
interactive = false; // come definito da posix
|
||||
if (interactive) {
|
||||
do {
|
||||
char* filename = getenv("ENV");
|
||||
if (!filename) break;
|
||||
FILE* backupfile = inputFile;
|
||||
|
||||
char* line = input();
|
||||
LOG("input: %s\n", line);
|
||||
if (!line) {
|
||||
if (feof(inputFile))
|
||||
__exit(NULL);
|
||||
else
|
||||
return errno;
|
||||
}
|
||||
char* parsed = resolve(line);
|
||||
LOG("parsed: %s\n", parsed);
|
||||
if (!parsed) return -1;
|
||||
int ret = exec(parsed);
|
||||
if (ret) return ret;
|
||||
inputFile = backupfile;
|
||||
} while (0);
|
||||
}
|
||||
// }}}
|
||||
if (interactive) {
|
||||
signal(SIGINT, clearInput);
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
signal(SIGTERM, SIG_IGN);
|
||||
};
|
||||
signal(SIGTTIN, SIG_IGN);
|
||||
signal(SIGTTOU, SIG_IGN);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
|
||||
// Main loop {{{
|
||||
for (;;) {
|
||||
if (mode == Stdin) prompt();
|
||||
char* line = input();
|
||||
LOG("input: %s\n", line);
|
||||
if (!line) {
|
||||
if (feof(inputFile))
|
||||
__exit(NULL);
|
||||
else
|
||||
return errno;
|
||||
}
|
||||
char* parsed = resolve(line);
|
||||
LOG("parsed: %s\n", parsed);
|
||||
if (!parsed) return -1;
|
||||
if (interactive || !optStatus.n) {
|
||||
int ret = exec(parsed);
|
||||
if (ret) return ret;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue