You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

479 lines
13 KiB

#include "error.h"
#include "run.h"
#include "main.h"
#include "util.h"
#include <dirent.h>
int get_alias(Alias *alias, State *state, char *name);
int add_alias(State* state, char *name, char *substitution);
int del_alias(State *state, char *name);
int repl(State* state, FILE *input);
GrowingArray get_growing_array(size_t num_elements, size_t size_elem) {
GrowingArray arr;
arr.inner = calloc(num_elements, size_elem);
arr.space = num_elements;
arr.elements = 0;
return arr;
}
int get_alias(Alias *alias, State *state, char *name) {
for (int i = 0; i < state->num_aliases; i++) {
if (!strcmp(name, state->aliases[i].name)) {
*alias = state->aliases[i];
return ER_SUCCESS;
}
}
return ER_FAILURE;
}
int get_array_element(ArrayElement **ret, GrowingArray* array, char* key) {
for (int i = 0; i < array->elements; i++) {
if (!strcmp(key, array->inner[i].key)) {
*ret = array->inner + i;
return ER_SUCCESS;
}
}
return ER_NOT_EXISTS;
}
int check_array_space(GrowingArray* array) {
int resize = 0;
if (array->elements > MIN_ARRAY_SIZE) {
if (array->elements >= array->space - 2) {
resize = 2 * array->space;
} else if (array->elements < (array->space / 3)) {
resize = array->space / 2;
}
}
if (resize) {
void *new = reallocarray(array->inner, resize,
sizeof(ArrayElement));
if (new) {
array->space = resize;
array->inner = new;
return ER_SUCCESS;
} else {
return ER_FAILURE;
}
} else {
return ER_SUCCESS;
}
}
int set_array_element(GrowingArray* array, char* key, void* value,
size_t valsize) {
ArrayElement *arr;
if (!get_array_element(&arr, array, key)) {
free(arr->value);
arr->value = dualloc(value, valsize);
return ER_SUCCESS;
} else {
check_array_space(array);
void *val = dualloc(value, valsize);
void *nkey = dualloc(key, (strlen(key) + 1) * sizeof(char));
if (value && key) {
array->inner[array->elements].value = val;
array->inner[array->elements].key = nkey;
array->elements++;
return ER_SUCCESS;
}
}
return ER_FAILURE;
}
int del_array_element(GrowingArray* array, char* key) {
ArrayElement *elem;
int err = get_array_element(&elem, array, key);
if (err) {
return err;
} else {
bool move = false;
for (int i = 0; i < array->elements; i++) {
if (move) {
if (i == array->elements - 1) {
memset(&array->inner[i], 0, sizeof(ArrayElement));
} else {
array->inner[i] = array->inner[i+1];
}
}
if (!strcmp(array->inner[i].key, key)) {
if (!move) {
free(array->inner[i].key);
free(array->inner[i].value);
move = true;
// move
if (i == array->elements - 1) {
memset(&array->inner[i], 0, sizeof(ArrayElement));
} else {
array->inner[i] = array->inner[i+1];
}
}
}
}
array->elements--;
return ER_SUCCESS;
}
return ER_FAILURE;
}
int add_alias(State* state, char* name, char* substitution) {
Alias alias;
strip_char(name, ' ');
alias.name = calloc(strlen(name) + 1, sizeof(char));
alias.substitution = calloc(strlen(substitution) + 1, sizeof(char));
strcpy(alias.name, name);
strcpy(alias.substitution, substitution);
Alias existing;
if (get_alias(&existing, state, name)) {
// add it if it doesnt exist
state->aliases[state->num_aliases] = alias;
state->num_aliases++;
} else {
del_alias(state, name);
free(alias.name);
free(alias.substitution);
add_alias(state, name, substitution);
}
return ER_SUCCESS;
}
int del_alias(State *state, char *name) {
// read the list until the alias is found, then
// shuffle all the alias definitions down by one
bool move = false;
for (int i = 0; i < state->num_aliases; i++) {
if (move) {
if (i < state->num_aliases -1) {
state->aliases[i] = state->aliases[i + 1];
} else {
memset(&state->aliases[i], 0, sizeof(Alias));
}
} else {
if (!strcmp(name, state->aliases[i].name)) {
move = true;
}
}
}
if (move) {
state->num_aliases--;
return ER_SUCCESS;
} else {
return ER_FAILURE;
}
}
int list_aliases(State *state) {
for (int i = 0; i < state->num_aliases; i++) {
Alias alias = state->aliases[i];
fprintf(state->output, "alias %s=%s\n", alias.name, alias.substitution);
}
return ER_SUCCESS;
}
int list_variables(State *state) {
for (int i = 0; i < state->variables.elements; i++) {
char *key = state->variables.inner[i].key;
char *value = state->variables.inner[i].value;
fprintf(state->output, "%s=%s\n", key, value);
}
return ER_SUCCESS;
}
void shutdown(State *state) {
for (int i = 0; i < state->num_aliases; i++) {
free(state->aliases[i].name);
free(state->aliases[i].substitution);
}
free(state->ps1);
free(state);
exit(0);
}
int check_builtins(State *state, int num_args, char *args[]) {
char *builtins[NUM_BUILTINS] = {
"cd",
"alias",
"unalias"
};
if (!strcmp(args[0], "cd")) {
if (num_args == 1) {
chdir(getenv("HOME"));
return ER_SUCCESS;
}
if (num_args != 2) {
return ER_FAILURE;
}
chdir(args[1]);
}
if (!strcmp(args[0], "set")) {
if (num_args >= 2) {
char *joined;
int err = join_string(&joined, num_args -1, args + 1, " ");
char **split;
int num;
err = split_string(&split, &num, joined, '=');
if (num == 2) {
char * key = split[0];
char * value = split[1];
set_array_element(&state->variables, key, value,
(strlen(value) + 1) * sizeof(char));
return ER_SUCCESS;
} else {
return ER_FAILURE;
}
}
}
if (!strcmp(args[0], "listvars")) {
list_variables(state);
return ER_SUCCESS;
}
if (!strcmp(args[0], "drop")) {
if (num_args == 2) {
char *key = args[1];
int err = del_array_element(&state->variables, key);
if (err) {
return ER_FAILURE;
} else {
return ER_SUCCESS;
}
}
return ER_FAILURE;
}
if (!strcmp(args[0], "alias")) {
Alias alias;
// list aliases
if (args[1] == 0) {
list_aliases(state);
return ER_SUCCESS;
}
// add new alias
if (num_args >= 2) {
if (num_args == 2) {
if (!get_alias(&alias, state, args[1])) {
fprintf(state->output, "alias %s=%s\n", alias.name,
alias.substitution);
return ER_SUCCESS;
}
}
if (num_args >= 2) {
char *substitution;
char **new_split;
int err = join_string(&substitution, num_args - 1,
args + 1, " ");
if (err) {
return err;
}
int eqcount = 0;
int num_parts;
err = split_string(&new_split, &num_parts, substitution, '=');
// exit if alias contains an =
if (num_parts != 2 || err) {
return ER_FAILURE;
}
err = add_alias(state, new_split[0], new_split[1]);
return err;
}
}
}
if (!strcmp(args[0], "unalias")) {
Alias alias;
if (num_args == 2) {
if (!del_alias(state, args[1])) {
return ER_SUCCESS;
}
}
}
if (!strcmp(args[0], "run")) {
if (num_args == 2) {
FILE *file = fopen(args[1], "r");
if (!file) {
return ER_FAILURE;
}
bool temp_repl_state = state->interactive;
state->interactive = false;
int err;
if ((err = repl(state, file))) {
perror("Config file error");
return err;
}
state->interactive = temp_repl_state;
fclose(file);
return ER_SUCCESS;
}
}
if (!strcmp(args[0], "exit")) {
if (num_args == 1) {
shutdown(state);
return ER_FAILURE;
}
}
return ER_NOT_EXISTS;
}
int check_aliases(State *state, char **substitution, char *name) {
Alias alias;
for (int i = 0; i < state->num_aliases; i++) {
if (!get_alias(&alias, state, name)) {
*substitution = alias.substitution;
return ER_SUCCESS;
}
}
return ER_FAILURE;
}
int run_command(State *state, int argc, char *argv[]) {
pid_t child;
if (argc == 0) {
return ER_FAILURE;
}
char *substitution;
if (!check_aliases(state, &substitution, argv[0])) {
strip_char(argv[0], ' ');
argv[0] = reallocarray(argv[0], strlen(substitution) + 1, sizeof(char));
strcpy(argv[0], substitution);
char *joined;
char **new_argv;
int len;
join_string(&joined, argc, argv, " ");
split_string(&new_argv, &len, joined, ' ');
//run_command(state, len, new_argv);
execute(stdin, stdout, new_argv, &child);
waitpid(child, NULL, 0);
return ER_SUCCESS;
for (int i = 0 ; i < len; i ++) {
free(new_argv[i]);
}
free(new_argv);
}
if (check_builtins(state, argc, argv) == ER_NOT_EXISTS) {
execute(stdin, stdout, argv, &child);
waitpid(child, NULL, 0);
}
return ER_SUCCESS;
}
int repl(State* state, FILE *input) {
while (true) {
char *line;
if (state->interactive) {
fprintf(state->output, "%s", state->ps1);
}
if (readline(input, &line)) {
return ER_SUCCESS;
}
char **sep_string;
int num_words;
split_string(&sep_string, &num_words, line, ' ');
run_command(state, num_words, sep_string);
for (int i = 0; i < num_words; i++) {
free(sep_string[i]);
}
free(sep_string);
free(line);
}
}
int startup(State *state, int argc, char **argv) {
state->output = stdout;
state->ps1 = calloc(3, sizeof(char));
strcpy(state->ps1, "$ ");
state->variables = get_growing_array(INITIAL_NUM_VARIABLES, sizeof(char*));
char *config_home;
char *config = calloc(500, sizeof(char));
if ((config_home = getenv("XDG_CONFIG_HOME"))) {
strcpy(config, config_home);
int len = strlen(config_home);
strcpy(config + len, "/chicken/chickenrc");
} else {
config_home = getenv("HOME");
strcpy(config, config_home);
int len = strlen(config_home);
strcpy(config + len, "/.chickenrc");
}
printf("%s\n", config);
int err;
FILE* rc = fopen(config, "r");
if (rc) {
state->interactive = false;
state->input = rc;
if ((err = repl(state, rc))) {
perror("Config file error");
return err;
}
}
fclose(rc);
if (argc == 2) {
FILE *script_text = fopen(argv[1], "r");
if (script_text) {
state->code = script_text;
} else {
perror("Failed to load script");
return ER_FAILURE;
}
}
return ER_SUCCESS;
}
int main(int argc, char** argv) {
State *state = calloc(1, sizeof(State));
state->aliases = calloc(INITIAL_NUM_ALIASES, sizeof(struct Alias));
state->num_aliases = 0;
int err = startup(state, argc, argv);
if (err) {
return err;
}
if (state->code) {
state->interactive = false;
repl(state, state->code);
} else {
state->interactive = true;
print_chicken(state);
repl(state, stdin);
}
return ER_SUCCESS;
}