]> Trent Huber's Code - thus.git/commitdiff
Finish TODOs, add positional parameters
authorTrent Huber <trentmhuber@gmail.com>
Sun, 20 Jul 2025 08:16:14 +0000 (04:16 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Sun, 20 Jul 2025 08:16:14 +0000 (04:16 -0400)
13 files changed:
src/builtins.c
src/config.h
src/history.c
src/input.c
src/job.c
src/job.h
src/main.c
src/options.c
src/options.h
src/parse.c
src/run.c
src/utils.c
src/utils.h

index 5ec4be3a68939d60cc59af5240d6864aa8b49e13..9112d67c5d8687849792812b0b6ba3a64acbb8b9 100644 (file)
@@ -6,33 +6,51 @@
 #include <sys/errno.h>
 #include <termios.h>
 #include <unistd.h>
+#include <sys/stat.h>
 
 #include "job.h"
 #include "stack.h"
 #include "term.h"
 #include "utils.h"
 
-#define BUILTINSIG(name) int name(char **tokens)
+#define BUILTINSIG(name) int name(int argc, char **argv)
 
 struct builtin {
    char *name;
    BUILTINSIG((*func));
 };
 
-BUILTINSIG(cd) { // TODO: Affect $PWD$ env var
-   if (!tokens[1]) return 1;
-   if (chdir(tokens[1]) != -1) return 0;
-   note("Unable to change directory to `%s'", tokens[1]);
-   return 1;
+BUILTINSIG(cd) {
+   char *fullpath;
+
+   if (argv[1]) {
+       if (!(fullpath = realpath(argv[1], NULL))) {
+           note("Could not resolve path name");
+           return 1;
+       }
+   } else fullpath = home;
+   if (chdir(fullpath) == -1) {
+       note("Unable to change directory to `%s'", argv[1]);
+       return 1;
+   }
+   if (setenv("PWD", fullpath, 1) == -1)
+       note("Unable to change $PWD$ to `%s'", fullpath);
+   if (fullpath != home) free(fullpath);
+   return 0;
 }
 
 BUILTINSIG(fg) {
    long jobid;
    struct job *job;
 
-   if (tokens[1]) {
+   if (sigaction(SIGCHLD, &sigdfl, NULL) == -1) {
+       note("Unable to acquire lock on the job stack");
+       return 1;
+   }
+
+   if (argv[1]) {
        errno = 0;
-       if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno
+       if ((jobid = strtol(argv[1], NULL, 10)) == LONG_MAX && errno
            || jobid <= 0) {
            note("Invalid process group id");
            return 1;
@@ -46,6 +64,12 @@ BUILTINSIG(fg) {
        note("No processes to bring into the foreground");
        return 1;
    }
+
+   if (sigaction(SIGCHLD, &sigchld, NULL) == -1) {
+       note("Unable to install SIGCHLD handler");
+       return 1;
+   }
+
    if (!setfg(*job)) return 1;
    waitfg(*job);
 
@@ -56,9 +80,14 @@ BUILTINSIG(bg) {
    long jobid;
    struct job *job;
 
-   if (tokens[1]) {
+   if (sigaction(SIGCHLD, &sigdfl, NULL) == -1) {
+       note("Unable to acquire lock on the job stack");
+       return 1;
+   }
+
+   if (argv[1]) {
        errno = 0;
-       if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno
+       if ((jobid = strtol(argv[1], NULL, 10)) == LONG_MAX && errno
            || jobid <= 0) {
            note("Invalid job id");
            return 1;
@@ -91,6 +120,11 @@ BUILTINSIG(bg) {
    }
    job->type = BACKGROUND;
 
+   if (sigaction(SIGCHLD, &sigchld, NULL) == -1) {
+       note("Unable to install SIGCHLD handler");
+       return 1;
+   }
+
    return 0;
 }
 
@@ -104,25 +138,58 @@ struct builtin builtins[] = {
    BUILTIN(NULL),
 };
 
+static int inpath(char *dir, char *filename) {
+   char *filepath;
+   struct stat estat;
+
+   if (stat(filepath = catpath(dir, filename), &estat) != -1) {
+       if (estat.st_mode & S_IXUSR) {
+           puts(filepath);
+           putchar('\r');
+           return 1;
+       }
+   } else if (errno != ENOENT) note("Unable to check if `%s' exists", filepath);
+   return 0;
+}
+
 BUILTINSIG(which) {
    struct builtin *builtin;
+   char *path, *dir, *p;
    
-   if (!tokens[1]) return 1;
+   if (!argv[1]) return 1;
    for (builtin = builtins; builtin->func; ++builtin)
-       if (strcmp(tokens[1], builtin->name) == 0) {
-           puts("Built-in command");
+       if (strcmp(argv[1], builtin->name) == 0) {
+           puts("Built-in command\r");
            return 0;
        }
-   // TODO: Find command in PATH
+
+   if (!(path = getenv("PATH"))) {
+       note("Unable to examine $PATH$");
+       return 1;
+   }
+   if (!(path = p = strdup(path))) {
+       note("Unable to duplicate $PATH$");
+       return 1;
+   }
+   do {
+       if (!(dir = p)) break;
+       if ((p = strchr(dir, ':'))) *p++ = '\0';
+   } while (!inpath(dir, argv[1]));
+   free(path);
+   if (dir) return 0;
+
+   printf("%s not found\r\n", argv[1]);
    return 1;
 }
 
 int isbuiltin(char **args, int *statusp) {
    struct builtin *builtinp;
+   size_t n;
 
    for (builtinp = builtins; builtinp->func; ++builtinp)
        if (strcmp(*args, builtinp->name) == 0) {
-           *statusp = builtinp->func(args);
+           for (n = 0; args[n]; ++n);
+           *statusp = builtinp->func(n, args);
            return 1;
        }
    return 0;
index add21bf7e76ba1913272b9a63c08535231779b4c..97e9448a1c4ad6e23df96d4666c4a76f4a50518c 100644 (file)
@@ -1,3 +1,5 @@
+#define DEFAULTPROMPT ">"
+
 #define HISTORYFILE ".ashhistory"
 #define INTERACTIVEFILE ".ashinteractive"
 #define LOGINFILE ".ashlogin"
index 121b9a61e89a67ee1d07da0d6aff3431e200c9db..cc48df9b41050910e965c1ca2a4df18e1deec167 100644 (file)
@@ -14,7 +14,7 @@ INITSTACK(history, historyarr, 1);
 void readhistory(void) {
    FILE *file;
 
-   if (!(file = fopen(prependhome(HISTORYFILE), "r"))) {
+   if (!(file = fopen(catpath(home, HISTORYFILE), "r"))) {
        if (errno == ENOENT) return;
        fatal("Unable to open history file for reading");
    }
@@ -30,7 +30,7 @@ void readhistory(void) {
 void writehistory(void) {
    FILE *file;
 
-   if (!(file = fopen(prependhome(HISTORYFILE), "w"))) {
+   if (!(file = fopen(catpath(home, HISTORYFILE), "w"))) {
        note("Unable to open history file for writing");
        return;
    }
index a7d01e3f93285f440b24e0d692dc7b59b68da943..fbe0ee1b7c128d7ac629bfeef3a5b2c17738ab15 100644 (file)
@@ -15,9 +15,7 @@
 #include "stack.h"
 #include "utils.h"
 
-#define PROMPT "> " // TODO: Have prompt be an environment variable
-
-enum character {
+enum {
    CTRLC = '\003',
    CTRLD,
    BACKSPACE = '\010',
@@ -84,7 +82,7 @@ char *config(char *name) {
    if (!origscript) {
        origscript = script;
        origstr = string;
-       script = prependhome(name);
+       script = catpath(home, name);
    }
 
    if (!(result = scriptinput())) {
@@ -96,23 +94,26 @@ char *config(char *name) {
    return result;
 }
 
-static void waitbgsig(int sig) {
-   (void)sig;
-   waitbg();
+static size_t prompt(void) {
+   char *p;
+
+   if (!(p = getenv("PROMPT")) && setenv("PROMPT", p = DEFAULTPROMPT, 1) == -1)
+       note("Unable to update $PROMPT$ environment variable");
+   printf("%s ", p);
+   return strlen(p) + 1;
 }
 
 INPUT(userinput) {
    char *cursor, *end;
+   size_t promptlen;
    unsigned int c;
    int i;
 
-   signal(SIGCHLD, waitbgsig); // TODO: Use sigaction for portability
-
    end = cursor = buffer;
    *history.t = *buffer = '\0';
    history.c = history.t;
    while (buffer == end) {
-       fputs(PROMPT, stdout);
+       promptlen = prompt();
        while ((c = getchar()) != '\r') switch (c) {
        default:
            if (c >= ' ' && c <= '~') {
@@ -132,11 +133,10 @@ INPUT(userinput) {
            return buffer;
        case CTRLD:
            puts("^D\r");
-           signal(SIGCHLD, SIG_DFL); // XXX
            return NULL;
        case CLEAR:
            fputs("\033[H\033[J", stdout);
-           fputs(PROMPT, stdout);
+           prompt();
            fputs(buffer, stdout);
            continue;
        case ESCAPE:
@@ -156,7 +156,7 @@ INPUT(userinput) {
                    if (history.c == (c == UP ? history.b : history.t)) continue;
 
                    putchar('\r');
-                   for (i = end - buffer + strlen(PROMPT); i > 0; --i) putchar(' ');
+                   for (i = end - buffer + promptlen; i > 0; --i) putchar(' ');
                    putchar('\r');
 
                    if (strcmp((char *)history.c, buffer) != 0)
@@ -165,7 +165,7 @@ INPUT(userinput) {
                    strcpy(buffer, (char *)history.c);
                    end = cursor = buffer + strlen(buffer);
 
-                   fputs(PROMPT, stdout);
+                   prompt();
                    fputs(buffer, stdout);
                    break;
                case LEFT:
@@ -196,13 +196,10 @@ INPUT(userinput) {
        }
        puts("\r");
    }
-   fpurge(stdout);
    push(&history, buffer);
 
    *end++ = ';';
    *end = '\0';
 
-   signal(SIGCHLD, SIG_DFL); // XXX
-
    return buffer;
 }
index 7c9bb090faf96ab4ffd19a3a11a7f7bafb861439..a412e890126a493803b7d0dd212ad99900d9b604 100644 (file)
--- a/src/job.c
+++ b/src/job.c
@@ -15,6 +15,8 @@
 static struct job jobarr[MAXJOBS + 1];
 INITSTACK(jobs, jobarr, 0);
 struct termios raw, canonical;
+struct sigaction sigchld, sigdfl, sigign;
+static int fgstatus;
 
 void *findjob(pid_t jobid) {
    if (jobs.b == jobs.t) return NULL;
@@ -52,22 +54,12 @@ int setfg(struct job job) {
 }
 
 int waitfg(struct job job) {
-   int status, pgid, result;
+   while (waitpid(job.id, NULL, 0) != -1);
+   errno = 0;
 
-   do {
-       errno = 0;
-       waitpid(job.id, &status, WUNTRACED);
-   } while (errno == EINTR);
-   if (!errno && !WIFSTOPPED(status)) do {
-       errno = 0;
-       while (waitpid(-job.id, NULL, 0) != -1);
-   } while (errno == EINTR);
-   result = errno != ECHILD ? errno : 0;
-
-   // TODO: Use sigaction >:(
-   if ((pgid = getpgid(0)) == -1 || signal(SIGTTOU, SIG_IGN) == SIG_ERR
-       || tcsetpgrp(STDIN_FILENO, pgid) == -1
-       || signal(SIGTTOU, SIG_DFL) == SIG_ERR) {
+   if (sigaction(SIGTTOU, &sigign, NULL) == -1
+       || tcsetpgrp(STDIN_FILENO, getpgrp()) == -1
+       || sigaction(SIGTTOU, &sigdfl, NULL) == -1) {
        note("Unable to reclaim foreground; terminating");
        deinitialize();
        exit(EXIT_FAILURE);
@@ -75,38 +67,34 @@ int waitfg(struct job job) {
    if (tcgetattr(STDIN_FILENO, &job.config) == -1)
        note("Unable to save termios config of job %d", job.id);
    setconfig(&raw);
-   if (result) return result;
 
-   if (WIFSIGNALED(status)) {
-       result = WTERMSIG(status);
-       puts("\r");
-   } else if (WIFSTOPPED(status)) {
-       result = WSTOPSIG(status);
-       job.type = SUSPENDED;
-       if (push(&jobs, &job)) return result;
+   if (WIFEXITED(fgstatus)) return WEXITSTATUS(fgstatus);
+   else if (WIFSIGNALED(fgstatus)) return WTERMSIG(fgstatus);
+   job.type = SUSPENDED;
+   if (!push(&jobs, &job)) {
        note("Unable to add job %d to list; too many jobs\r\n"
-             "Press any key to continue", job.id);
+            "Press any key to continue", job.id);
        getchar();
        if (setfg(job)) return waitfg(job);
        note("Manual intervention required for job %d", job.id);
-   } else if (WIFEXITED(status)) result = WEXITSTATUS(status);
-
-   return result;
+   }
+   return WSTOPSIG(fgstatus);
 }
 
-void waitbg(void) {
+void sigchldhandler(int sig) {
    int status;
    pid_t id;
 
-   for (jobs.c = jobs.b; jobs.c != jobs.t; INC(jobs, c)) {
-       if (CURRENT->type != BACKGROUND) continue;
-       id = CURRENT->id;
-
-       // TODO: weird EINTR thing here too??
-       while ((id = waitpid(-id, &status, WNOHANG | WUNTRACED)) > 0)
+   (void)sig;
+   while ((id = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
+       for (jobs.c = jobs.b; jobs.c != jobs.t; INC(jobs, c)) if (CURRENT->id == id) {
            if (WIFSTOPPED(status)) CURRENT->type = SUSPENDED;
-
-       if (id == -1 && errno != ECHILD)
-           note("Unable to wait on some child processes");
+           else deletejob();
+           break;
+       }
+       if (jobs.c == jobs.t) {
+           fgstatus = status;
+           if (!WIFSTOPPED(fgstatus)) while (waitpid(-id, NULL, 0) != -1);
+       }
    }
 }
index 12fa031d5efe2dce5e1b87ffdc0263adfe196467..7b33124870a8e77062ec110066d5077a04181131 100644 (file)
--- a/src/job.h
+++ b/src/job.h
@@ -13,10 +13,11 @@ struct job {
 
 extern struct stack jobs;
 extern struct termios raw, canonical;
+extern struct sigaction sigchld, sigdfl, sigign;
 
 void *findjob(pid_t jobid);
 void *deletejob(void);
 int setconfig(struct termios *mode);
 int setfg(struct job job);
 int waitfg(struct job job);
-void waitbg(void);
+void sigchldhandler(int sig);
index 30a92febc9dea942c66a952f02b17927397f7690..cabe510f1f135e27ddb705c91fa95d15c2c7afa3 100644 (file)
@@ -7,8 +7,11 @@
 #include "run.h"
 #include "utils.h"
 
-int main(int argc, char **argv) {
-   options(&argc, &argv);
+int main(int localargc, char **localargv) {
+   argc = localargc;
+   argv = localargv;
+
+   options();
 
    initialize();
 
index 453613924a210521088ab998db2bf4f1309ebe8b..0476c18cfba909eeaa99d6f407268668ea1ebc6e 100644 (file)
@@ -1,4 +1,5 @@
 #include <err.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
@@ -6,40 +7,55 @@
 #include "input.h"
 #include "options.h"
 
-int login, interactive;
+int login, interactive, argc;
 Input input;
+char **argv;
 
-void options(int *argcp, char ***argvp) {
+static void usage(char *program, int code) {
+   printf("Usage: %s [file] [-c string] [-hl]\n"
+          "    <file> ...      Run script\n"
+          "    -c <string> ... Run commands\n"
+          "    -h              Show this help message\n"
+          "    -l              Run as a login shell\n", program);
+   exit(code);
+}
+
+void options(void) {
    int opt, l;
-   char *usage = "TODO: WRITE USAGE";
 
-   login = ***argvp == '-';
+   login = **argv == '-';
    interactive = 1;
    input = userinput;
 
-   while ((opt = getopt(*argcp, *argvp, ":c:hl")) != -1) switch (opt) {
-   case 'c':
-       interactive = 0;
-       input = stringinput;
-       string = optarg;
-       break;
-   case 'h':
-       errx(EXIT_SUCCESS, "%s", usage);
-   case 'l':
-       login = 1;
-       break;
-   case ':':
-       errx(EXIT_FAILURE, "Expected argument following `-%c'\n%s", optopt, usage);
-   case '?':
-   default:
-       errx(EXIT_FAILURE, "Unknown command line option `-%c'\n%s", optopt, usage);
+   while ((opt = getopt(argc, argv, ":c:hl")) != -1) {
+       switch (opt) {
+       case 'c':
+           interactive = 0;
+           input = stringinput;
+           string = optarg;
+           break;
+       case 'h':
+           usage(*argv, EXIT_SUCCESS);
+       case 'l':
+           login = 1;
+           break;
+       case ':':
+           warnx("Expected argument following `-%c'\n", optopt);
+           usage(*argv, EXIT_FAILURE);
+       case '?':
+       default:
+           warnx("Unknown command line option `-%c'\n", optopt);
+           usage(*argv, EXIT_FAILURE);
+       }
+       if (opt == 'c') break;
    }
-   *argcp -= optind;
-   *argvp += optind;
-
-   if (!string && **argvp) {
+   if (!string && argv[optind]) {
        interactive = 0;
        input = scriptinput;
-       script = **argvp;
+       script = argv[optind];
+   }
+   if (!interactive) {
+       argc -= optind;
+       argv += optind;
    }
 }
index 724c3aa7a7f6bebbcc46b996183a8c01903095a2..991820771a4769c9ad2abddfc4e5540be2221081 100644 (file)
@@ -1,6 +1,7 @@
 typedef INPUT((*Input));
 
-extern int login, interactive;
+extern int login, interactive, argc;
 extern Input input;
+extern char **argv;
 
-void options(int *argcp, char ***argvp);
+void options(void);
index 7a93f748a4dc639fcc2d09a3fdfca23e5ecc0735..4dea8ca97645c012cfa53a706bb0549a03391adb 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "config.h"
 #include "input.h"
+#include "options.h"
 #include "parse.h"
 #include "utils.h"
 
@@ -12,7 +13,7 @@ static struct cmd cmds[MAXCMDS + 1];
 struct cmd empty = {0};
 
 struct cmd *parse(char *b) {
-   char **t, *name, *value, *end, *p, *env;
+   char **t, *name, *value, *stlend, *p, *end, *env;
    struct cmd *c;
    long l;
    int e, offset;
@@ -33,8 +34,8 @@ struct cmd *parse(char *b) {
        c->r->newfd = *b == '>';
        if (*(b - 1)) {
            if (c->args == --t) c->args = NULL;
-           if ((l = strtol(*t, &end, 10)) < 0 || l > INT_MAX || end != b) {
-               note("Incorrect syntax for file redirection\r");
+           if ((l = strtol(*t, &stlend, 10)) < 0 || l > INT_MAX || stlend != b) {
+               note("Incorrect syntax for file redirection");
                return &empty;
            }
            c->r->newfd = l;
@@ -56,7 +57,7 @@ struct cmd *parse(char *b) {
            if (*end == '\\') ++end;
        }
        if (!b) {
-           note("Quote left open-ended\r");
+           note("Quote left open-ended");
            return &empty;
        }
        memmove(p, p + 1, end-- - p);
@@ -95,14 +96,16 @@ struct cmd *parse(char *b) {
        p = b++;
        while (*b && *b != '$') ++b;
        if (!*b) {
-           note("Environment variable lacks a terminating `$'\r");
+           note("Environment variable lacks a terminating `$'");
            return &empty;
        }
        *b++ = '\0';
        for (end = b; *end; ++end);
 
-       if ((env = getenv(p + 1)) == NULL) {
-           note("Environment variable does not exist\r");
+       l = strtol(p + 1, &stlend, 10);
+       if (stlend == b - 1) env = l >= 0 && l < argc ? argv[l] : b - 1;
+       else if ((env = getenv(p + 1)) == NULL) {
+           note("Environment variable does not exist");
            return &empty;
        }
        e = strlen(env);
@@ -126,8 +129,9 @@ struct cmd *parse(char *b) {
            *b = '\0';
            c->r->mode = END;
            for (c->r = c->rds; c->r->mode; ++c->r) if (*c->r->oldname == '&') {
-               if ((l = strtol(++c->r->oldname, &end, 10)) < 0 || l > INT_MAX || *end) {
-                   note("Incorrect syntax for file redirection\r");
+               if ((l = strtol(++c->r->oldname, &stlend, 10)) < 0
+                   || l > INT_MAX || *stlend) {
+                   note("Incorrect syntax for file redirection");
                    return &empty;
                }
                c->r->oldfd = l;
@@ -142,7 +146,7 @@ struct cmd *parse(char *b) {
        *b = '\0';
        if (value) {
            if (setenv(name, value, 1) == -1) {
-               note("Unable to set environment variable\r");
+               note("Unable to set environment variable");
                return &empty;
            }
            value = name = NULL;
@@ -153,7 +157,7 @@ struct cmd *parse(char *b) {
    case AND:
    case PIPE:
    case OR:
-       note("Expected another command\r");
+       note("Expected another command");
        return &empty;
    default:
        break;
index a93f65ff52819509af254bc817e09ff7d3bbd824..9adbe86a3faa9f52cb0cc1dce35c9aa9d4109979 100644 (file)
--- a/src/run.c
+++ b/src/run.c
@@ -132,7 +132,6 @@ int run(struct cmd *cmd) {
            if (cmd->term == OR && status == 0) break;
        }
    }
-   waitbg();
 
    return 1;
 }
index f943c205d452fdae3a9f586e2840a157d5e48bf1..b8cf6b91b71716fc186c7e6aec9301f97cda7f3e 100644 (file)
@@ -1,4 +1,5 @@
 #include <err.h>
+#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include "options.h"
 #include "utils.h"
 
+char *home;
+
 void note(char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    (errno ? vwarn : vwarnx)(fmt, args);
    va_end(args);
    putchar('\r');
+   errno = 0;
 }
 
 void fatal(char *fmt, ...) {
@@ -31,31 +35,54 @@ void fatal(char *fmt, ...) {
    exit(EXIT_FAILURE);
 }
 
-char *prependhome(char *name) {
-   static char *p, path[MAXPATH + 1];
+char *catpath(char *dir, char *filename) {
+   static char path[MAXPATH + 1];
    
-   if (!p) {
-       if (!(p = getenv("HOME")))
-           fatal("Unable to access $HOME$ environment variable");
-       strcpy(path, p);
-       strcat(path, "/");
-       p = path + strlen(path);
-   }
-   *p = '\0';
-   strcat(path, name);
+   strcpy(path, dir);
+   strcat(path, "/");
+   strcat(path, filename);
 
    return path;
 }
 
-void initialize(void) { // <-- TODO: Set $SHLVL$ in this function
-   cfmakeraw(&raw);
+void initialize(void) {
+   char *shlvlstr;
+   long shlvl;
+
+   // Raw mode
    if (tcgetattr(STDIN_FILENO, &canonical) == -1)
-       fatal("Unable to get default termios config");
+       err(EXIT_FAILURE, "Unable to get default termios config");
+   cfmakeraw(&raw);
    if (!setconfig(&raw)) exit(EXIT_FAILURE);
+
+   // Set signal actions
+   sigchld.sa_handler = sigchldhandler;
+   sigdfl.sa_handler = SIG_DFL;
+   sigign.sa_handler = SIG_IGN;
+   if (sigaction(SIGCHLD, &sigchld, NULL) == -1)
+       fatal("Unable to install SIGCHLD handler");
+
+   // Initialize `home'
+   if (!(home = getenv("HOME")))
+       fatal("Unable to locate user's home directory, $HOME$ not set");
+
+   // Update $SHLVL$
+   if (!(shlvlstr = getenv("SHLVL"))) shlvlstr = "0";
+   if ((shlvl = strtol(shlvlstr, NULL, 10)) < 0) shlvl = 0;
+   asprintf(&shlvlstr, "%ld", shlvl + 1);
+   if (setenv("SHLVL", shlvlstr, 1) == -1)
+       note("Unable to update $SHLVL$ environment variable");
+   free(shlvlstr);
+
+   // History read
    if (interactive) readhistory();
 }
 
 void deinitialize(void) {
+
+   // History write
    if (interactive) writehistory();
+
+   // Canonical mode
    setconfig(&canonical);
 }
index a78c43ba8f0a26e2bd4a89a6920ed6d8c64c29f9..24bd6e8eb7a8e0956c4d4a70bec772063fde6883 100644 (file)
@@ -1,5 +1,7 @@
+extern char *home;
+
 void note(char *fmt, ...);
 void fatal(char *fmt, ...);
-char *prependhome(char *name);
+char *catpath(char *dir, char *filename);
 void initialize(void);
 void deinitialize(void);