Linux shell written in Odin.
package main

import "linux"
import "ncure"
import "core:strings"
import "core:path"

defaultCallProc :: proc(self: ^Call, globalData: ^GlobalData) {
// 	ncure.printf("%s\n", self^);

	// Check if command is in hashtable
	// TODO: command is a slice that can be of multiple tokens
	commandPath, isHashed := globalData.path_hash[self.command[0].str];
	if !isHashed {
		commandPath = self.command[0].str;

		// Check that file exists, is a file, and is executable
		exists := path.is_file(commandPath);
		ncure.println(commandPath);
		if !exists {
			ncure.write_string(ncure.ForegroundColor.Red, "Error: File doesn't exist");
			ncure.newLine();
			return;
		}
	}

	amt := 1;
	if self.hasSubcommand || !self.help {
		amt += 1;
	}
	args := make([dynamic]cstring, amt, len(self.params) + amt);
	args[0] = strings.clone_to_cstring(commandPath);
	if self.hasSubcommand do args[1] = strings.clone_to_cstring(self.subcommand[0].str); // TODO
	else if !self.help do args[1] = strings.clone_to_cstring("default");

	defer delete(args);
	builder := strings.make_builder();
	for i in 0..<len(self.params) {
		param := &self.params[i];
		if param.name != nil {
			strings.write_string(&builder, param.name.str);
			strings.write_byte(&builder, '=');
			append(&args, strings.clone_to_cstring(strings.to_string(builder)));
			strings.reset_builder(&builder);
		}

		valueString, _ := strings.replace_all(param.value.str, "\"", "");
		strings.write_string(&builder, valueString); // TODO
		append(&args, strings.clone_to_cstring(strings.to_string(builder)));
		strings.reset_builder(&builder);
	}
	append(&args, nil);

	commandPath_cstr := strings.clone_to_cstring(commandPath);
	defer delete(commandPath_cstr);
	executeProgram(globalData, commandPath_cstr, &args[0]);
}

executeProgram :: proc(globalData: ^GlobalData, path: cstring, args: ^cstring) {
	pid, wpid: linux.pid_t;
	status: int;

	pid = linux.fork();
	if pid == 0 {
		// Child process
		// Set terminal stop and interactive attention signals to default
		linux.signal(linux.SIGINT, linux.SIG_DFL);
		linux.signal(linux.SIGTSTP, linux.SIG_DFL);
		// Ignore SIGHUP to allow program to continue
		// running even if the controlling terminal/paled closes.
// 		linux.signal(linux.SIGHUP, linux.SIG_IGN);

		result := linux._unix_execvp(path, args);
		if result == -1 {
			// TODO: Error
		}
	} else if pid < 0 {
		// TODO: Forking error
	} else {
		// Parent - Paled
		for {
			wpid = linux.waitpid(pid, &status, linux.WUNTRACED);
			if linux.WIFEXITED(status) || linux.WIFSIGNALED(status) do break;
		}
	}
}