--- /dev/null
+Pitfalls and things not to assume
+-----------------------------------
+
+Posix
+------
+
+This is not POSIX compliant. It is not intended to be, since this
+contorts the shell into working in a certain manner, and in practice,
+/bin/sh usually means /bin/bash.
+
+Subcommands
+------------
+
+Subcommands may very well look like the subshell "$()" / "`" operator
+in bash. They are not.
+
+Commands are resolved in dependency order until no more replacements are
+needed. This is contrary to how bash, zsh, etc work; they literally
+spawn another shell to handle anything within. To explain with graphs:
+
+bash: echo $(echo hello $(echo world) ho)
+
+echo $
+ echo hello $ ho
+ echo world
+
+xsh: echo {echo hello {echo world} ho}
+
+echo {echo hello $ ho}
+ echo world
+echo $
+ echo hello world ho
+
+This behavior has a few importan6 things to note about it.
+ 1) Subcommands inherit the environment of the shell, not
+ a "parent" subcommand.
+ 2) Subcommands do not spawn another shell.
+
+Variables
+----------
+
+Variables (starting with the $ character) are resolved after subcommands
+and double-quoted strings.
+Consider the following:
+
+= NAME 42
+echo "${echo NAME}"
+
+So first, echo NAME is evaluated. The contents of the double-quoted string
+are then evaluated, leaving a string named "$NAME". This is then interpreted to
+be a variable, resulting in the output 42.
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+int builtin_chdir(char* nam, char** argv, char** stdout) {
+ assert(nam);
+ assert(argv[0]);
+
+ int ret = 0;
+
+ if (argv[1] == NULL) {
+ // No path was provided in cd, so just go $HOME
+ } else if (argv[1]) {
+ ret = chdir(argv[1]);
+ }
+
+ if (ret == -1)
+ perror("cd");
+
+ return ret;
+}
return pid;
}
+typedef int (*builtin_fn_t)(char*, char**, char**);
+
+int builtin_chdir(char* nam, char** argv, char** stdout);
+
+typedef struct {
+ char name[64];
+ builtin_fn_t func;
+} builtin_info_t;
+
+builtin_info_t builtin_info[] = {
+ { "cd", builtin_chdir },
+ { "", NULL },
+};
+
+int check_builtin(char* name) {
+ for (int i = 0; builtin_info[i].func != NULL; i++) {
+ if (!strcmp(name, builtin_info[i].name)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
void execute(ast_t* tree, char** stdout) {
// Important note; this function is only for fully resolved trees of commands.
// If any unresolved subshells or groups exist, this function is undefined.
argv[tree->size] = NULL;
prog = argv[0];
- pid_t pid = fork_and_execvp(prog, argv, stdout);
- int wstatus;
- pid = waitpid(pid, &wstatus, 0);
+ int builtin_chk = check_builtin(prog);
+ if (builtin_chk != -1) {
+ builtin_info[builtin_chk].func(prog, argv, stdout);
+ } else {
+ pid_t pid = fork_and_execvp(prog, argv, stdout);
+ int wstatus;
+ pid = waitpid(pid, &wstatus, 0);
+ }
}
void ast_resolve_subs(ast_t* ast, int master) {