working setup
This commit is contained in:
commit
b9d4d1552c
7 changed files with 345 additions and 0 deletions
2
.clangd
Normal file
2
.clangd
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
CompileFlags:
|
||||||
|
Add: ["-xc"]
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
bin/
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "src/linked-list"]
|
||||||
|
path = src/linked-list
|
||||||
|
url = ssh://forgejo@git.dario48.site/Dario48/linked-list.git
|
||||||
19
Makefile
Normal file
19
Makefile
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
CC=/bin/clang
|
||||||
|
BINDIR=bin
|
||||||
|
|
||||||
|
main=$(BINDIR)/main
|
||||||
|
|
||||||
|
all: setup $(main) libs
|
||||||
|
|
||||||
|
setup:
|
||||||
|
mkdir -p $(BINDIR)
|
||||||
|
git submodule update
|
||||||
|
|
||||||
|
$(main): src/main.c src/linked-list/linked_list.c
|
||||||
|
$(CC) $(CXXFLAGS) $(CFLAGS) src/main.c -o $(main)
|
||||||
|
|
||||||
|
libs:
|
||||||
|
|
||||||
|
.PHONY: clean all setup
|
||||||
|
clean:
|
||||||
|
rm -fr bin/
|
||||||
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Posix C SHell, a posix shell impementation in C
|
||||||
1
src/linked-list
Submodule
1
src/linked-list
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 7343039dc975ebbd3c6660527da0f3780ff432bc
|
||||||
318
src/main.c
Normal file
318
src/main.c
Normal file
|
|
@ -0,0 +1,318 @@
|
||||||
|
#include <bits/types/idtype_t.h>
|
||||||
|
#include <bits/types/siginfo_t.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wait.h>
|
||||||
|
|
||||||
|
#include "linked-list/linked_list.c"
|
||||||
|
#if DEBUG
|
||||||
|
#define LOG(...) printf(__VA_ARGS__)
|
||||||
|
#define LOG_WRAP(x, ...) x(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define LOG(...)
|
||||||
|
#define LOG_WRAP(x, ...)
|
||||||
|
#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; \
|
||||||
|
}
|
||||||
|
// clang-format off
|
||||||
|
CREATE_LIST(string_array, char*)
|
||||||
|
CREATE_TOARRAY(string_array, char*, %s)
|
||||||
|
CREATE_LIST(string, char)
|
||||||
|
CREATE_TOARRAY(string, char, %c)
|
||||||
|
// clang-format on
|
||||||
|
struct variable {
|
||||||
|
char* name;
|
||||||
|
char* value;
|
||||||
|
};
|
||||||
|
// clang-format off
|
||||||
|
#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__)
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
variable_list variables = {.head = NULL};
|
||||||
|
|
||||||
|
const char* getorsetenv(const char* name, const char* val) {
|
||||||
|
char* get = getenv(name);
|
||||||
|
if (get) return get;
|
||||||
|
setenv(name, val, 1);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _find_variable(variable_list_node* node, void* args) {
|
||||||
|
struct variable* arg = (struct variable*)args;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
if (!name) return NULL;
|
||||||
|
return getenv(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Bool is_subshell = 0;
|
||||||
|
|
||||||
|
#define throw(ret, ...) \
|
||||||
|
{ \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
return ret; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHECK(var, val) var& val
|
||||||
|
|
||||||
|
#define ESCAPE 0x1
|
||||||
|
#define SET_ESCAPE situation ^= ESCAPE
|
||||||
|
#define IS_ESCAPED CHECK(situation, ESCAPE)
|
||||||
|
|
||||||
|
#define QUOTED 0x2
|
||||||
|
#define SET_QUOTED situation ^= QUOTED
|
||||||
|
#define IS_QUOTED CHECK(situation, QUOTED)
|
||||||
|
|
||||||
|
#define DOUBLE_QUOTED 0x4
|
||||||
|
#define SET_DOUBLE_QUOTED situation ^= DOUBLE_QUOTED
|
||||||
|
#define IS_DOUBLE_QUOTED CHECK(situation, DOUBLE_QUOTED)
|
||||||
|
|
||||||
|
#define VAR 0x8
|
||||||
|
#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; \
|
||||||
|
}
|
||||||
|
char* resolve(const char* str) {
|
||||||
|
char situation = 0;
|
||||||
|
|
||||||
|
string buf = {.head = NULL};
|
||||||
|
|
||||||
|
string var_buffer = {.head = NULL};
|
||||||
|
for (const char* current = str; *current; current++) {
|
||||||
|
LOG("situation_pre: %i, current: %p = %c", situation, current,
|
||||||
|
*current);
|
||||||
|
switch (*current) {
|
||||||
|
case '\\':
|
||||||
|
if (IS_QUOTED) {
|
||||||
|
append_string(&buf, '\\');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (IS_VAR)
|
||||||
|
SET_VAR;
|
||||||
|
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"
|
||||||
|
);
|
||||||
|
SET_QUOTED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
if (IS_VAR) SET_VAR;
|
||||||
|
if (IS_ESCAPED || IS_QUOTED) append_string(&buf, '"');
|
||||||
|
SET_DOUBLE_QUOTED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '$':
|
||||||
|
if (IS_ESCAPED || IS_QUOTED)
|
||||||
|
append_string(&buf, '$');
|
||||||
|
else if (IS_VAR) {
|
||||||
|
if (*(current - 1) == '$') {
|
||||||
|
for (pid_t id = (!is_subshell) ? getpid() : getppid();
|
||||||
|
id > 0; id = id / 10)
|
||||||
|
append_string(&buf, (id % 10) + 48);
|
||||||
|
} else {
|
||||||
|
RESOLVE_VAR;
|
||||||
|
SET_VAR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SET_VAR;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
else
|
||||||
|
append_string(&buf, *current);
|
||||||
|
}
|
||||||
|
if (IS_ESCAPED) SET_ESCAPE;
|
||||||
|
END:
|
||||||
|
LOG(", string: { ");
|
||||||
|
LOG_WRAP(print_string, &buf, "'%c', ");
|
||||||
|
LOG(" }, situation: %i\n", situation);
|
||||||
|
}
|
||||||
|
if (IS_VAR) RESOLVE_VAR;
|
||||||
|
append_string(&buf, 0);
|
||||||
|
LOG("string: { ");
|
||||||
|
LOG_WRAP(print_string, &buf, "'%c', ");
|
||||||
|
LOG(" }\n");
|
||||||
|
char* parsed = string_to_array(&buf);
|
||||||
|
LOG("parsed_internal: \"%s\"\n", parsed);
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prompt() {
|
||||||
|
return fprintf(stderr, "%s", resolve(getorsetenv("PS1", "\\$ ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
char* input() {
|
||||||
|
char* line = NULL;
|
||||||
|
size_t n = 0;
|
||||||
|
if (getline(&line, &n, stdin) < 0 && ferror(stdin)) {
|
||||||
|
free(line);
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
int exec(char* line) {
|
||||||
|
LOG("line: %s\n", line);
|
||||||
|
_Bool todo = 1;
|
||||||
|
string_array args = {.head = NULL};
|
||||||
|
string arg = {.head = NULL};
|
||||||
|
char* orig = line;
|
||||||
|
for (; *line != '\n'; line++) {
|
||||||
|
LOG("line: %c, \n", *line);
|
||||||
|
if (*line == ' ') {
|
||||||
|
if (todo) {
|
||||||
|
orig[line - orig] = 0;
|
||||||
|
todo = 0;
|
||||||
|
} else {
|
||||||
|
append_string(&arg, 0);
|
||||||
|
append_string_array(&args, string_to_array(&arg));
|
||||||
|
}
|
||||||
|
} else if (!todo) {
|
||||||
|
append_string(&arg, *line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG("line: %c, \n", *line);
|
||||||
|
if (todo) {
|
||||||
|
orig[line - orig] = 0;
|
||||||
|
todo = 0;
|
||||||
|
} else {
|
||||||
|
append_string(&arg, 0);
|
||||||
|
append_string_array(&args, string_to_array(&arg));
|
||||||
|
}
|
||||||
|
push_string_array(&args, orig);
|
||||||
|
append_string_array(&args, NULL);
|
||||||
|
LOG("args: { ");
|
||||||
|
LOG_WRAP(print_string_array, &args, "%s, ");
|
||||||
|
LOG(" }");
|
||||||
|
char** strargs = string_array_to_array(&args);
|
||||||
|
for (int i = 0; strargs[i]; i++) {
|
||||||
|
LOG("strargs[%i] %s", 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue