working setup

This commit is contained in:
Dario48 2025-12-06 12:10:51 +01:00
commit b9d4d1552c
7 changed files with 345 additions and 0 deletions

2
.clangd Normal file
View file

@ -0,0 +1,2 @@
CompileFlags:
Add: ["-xc"]

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
bin/

3
.gitmodules vendored Normal file
View 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
View 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
View file

@ -0,0 +1 @@
Posix C SHell, a posix shell impementation in C

1
src/linked-list Submodule

@ -0,0 +1 @@
Subproject commit 7343039dc975ebbd3c6660527da0f3780ff432bc

318
src/main.c Normal file
View 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;
}
}