summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules3
-rw-r--r--Makefile14
-rw-r--r--alias.c123
-rw-r--r--builtins.c73
-rw-r--r--cd.c10
-rw-r--r--interactive.c86
-rw-r--r--parse.c69
m---------pwd0
-rw-r--r--pwd.c48
-rw-r--r--set.c5
-rw-r--r--sh.h2
-rw-r--r--sh.l9
-rw-r--r--sh.y8
-rw-r--r--shed.c79
-rw-r--r--shed.h99
-rw-r--r--shed_commands.c164
-rw-r--r--shed_complete.c52
-rw-r--r--shed_edit.c97
-rw-r--r--shed_history.c41
-rw-r--r--shed_insert.c62
-rw-r--r--shed_io.c91
-rw-r--r--shed_move.c45
-rw-r--r--shed_non_vi.c105
-rw-r--r--type.c61
-rw-r--r--umask.c27
-rw-r--r--unspecified.c8
27 files changed, 1232 insertions, 150 deletions
diff --git a/.gitignore b/.gitignore
index 3480f40..fedb0b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,6 @@ fg
getopts
jobs
newgrp
-pwd
read
sh
ulimit
diff --git a/.gitmodules b/.gitmodules
index 9fad81f..00d808e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -8,3 +8,6 @@
[submodule "true"]
path = true
url = ../true/
+[submodule "pwd"]
+ path = pwd
+ url = ../pwd/
diff --git a/Makefile b/Makefile
index d0c7662..0f4e094 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
default: all
-CFLAGS=-g -Wall -Wextra -Wpedantic
+CFLAGS=-g -Wall -Wextra -Wpedantic -D_XOPEN_SOURCE=700
UTILITY=sh
SOURCES=alias.c bg.c builtins.c cd.c command.c fc.c fg.c getopts.c \
@@ -12,22 +12,24 @@ SOURCES=alias.c bg.c builtins.c cd.c command.c fc.c fg.c getopts.c \
ulimit.c umask.c wait.c \
dot.c eval.c exec.c exit.c export.c readonly.c shift.c times.c trap.c \
unset.c \
- interactive.c parse.c init.c getopt.c
-HEADERS=sh.h
+ interactive.c parse.c init.c getopt.c unspecified.c \
+ shed.c shed_commands.c shed_edit.c shed_history.o shed_insert.c shed_io.c shed_move.c shed_non_vi.cshed_complete.c
+HEADERS=sh.h shed.h
OBJECTS=alias.o bg.o builtins.o cd.o command.o fc.o fg.o getopts.o \
hash.o jobs.o kill.o newgrp.o pwd.o read.o set.o main.o type.o \
ulimit.o umask.o wait.o \
dot.o eval.o exec.o exit.o export.o readonly.o shift.o times.o trap.o \
unset.o \
- interactive.o parse.o init.o getopt.o \
+ interactive.o parse.o init.o getopt.o unspecified.o \
+ shed.o shed_commands.o shed_edit.o shed_history.o shed_insert.o shed_io.o shed_move.o shed_non_vi.o shed_complete.o \
sh.tab.o sh.yy.o
-BUILTINS=alias bg cd command fc fg getopts jobs newgrp pwd read \
+BUILTINS=alias bg cd command fc fg getopts jobs newgrp read \
ulimit umask unalias wait
all: $(UTILITY) $(L10N) $(BUILTINS)
sh: $(OBJECTS) $(HEADERS)
- $(CC) -o $@ $(CFLAGS) $(OBJECTS) -ly -ll
+ $(CC) -o $@ $(CFLAGS) $(OBJECTS)
$(BUILTINS): $(UTILITY)
#ln -s $(UTILITY) $@
diff --git a/alias.c b/alias.c
index 23c557a..ce906ba 100644
--- a/alias.c
+++ b/alias.c
@@ -24,26 +24,52 @@
#include <unistd.h>
struct alias {
+ struct alias *next;
+ struct alias *prev;
char *alias;
char *command;
};
static struct alias *aliases = NULL;
-static void set_alias(const char *alias, const char *command)
+static int set_alias(const char *alias, const char *command)
{
- (void)alias; (void)command;
-}
+ struct alias *p = aliases;
+ for (; p != NULL; p = p->next) {
+ if (strcmp(p->alias, alias) == 0) {
+ free(p->command);
+ p->command = strdup(command);
+ return 0;
+ }
+ }
+
+ struct alias *a = calloc(1, sizeof(*a));
+ if (a == NULL) {
+ return 1;
+ }
+
+ a->alias = strdup(alias);
+ a->command = strdup(command);
-char * sh_get_alias(const char *alias)
-{
if (aliases == NULL) {
- return NULL;
+ aliases = a;
+ return 0;
+ }
+
+ for (p = aliases; p->next != NULL; p = p->next) {
+ /* find tail */
}
+ p->next = a;
+ a->prev = p;
+
+ return 0;
+}
- for (int i = 0; aliases[i].alias; i++) {
- if (!strcmp(aliases[i].alias, alias)) {
- return aliases[i].command;
+char * sh_get_alias(const char *alias)
+{
+ for (struct alias *p = aliases; p != NULL; p = p->next) {
+ if (!strcmp(p->alias, alias)) {
+ return p->command;
}
}
@@ -67,28 +93,95 @@ int alias_main(int argc, char *argv[])
}
if (argv[optind] == NULL) {
- for (int i = 0; aliases && aliases[i].alias; i++) {
- printf("%s=%s\n", aliases[i].alias, aliases[i].command);
+ for (struct alias *p = aliases; p != NULL; p = p->next) {
+ printf("%s=%s\n", p->alias, p->command);
}
return 0;
}
+ int ret = 0;
do {
char *a = argv[optind++];
char *eq = strchr(a, '=');
if (eq) {
*eq = '\0';
- set_alias(a, eq + 1);
+ ret |= set_alias(a, eq + 1);
} else {
show_alias(a);
}
} while (optind < argc);
- return 0;
+ return ret;
+}
+
+static struct alias *free_alias(struct alias *alias)
+{
+ struct alias *next = alias->next;
+ if (alias == aliases) {
+ aliases = next;
+ }
+
+ if (alias->next) {
+ alias->next->prev = alias->prev;
+ }
+
+ if (alias->prev) {
+ alias->prev->next = alias->next;
+ }
+
+ free(alias->alias);
+ free(alias->command);
+ free(alias);
+ return next;
}
int unalias_main(int argc, char **argv)
{
- printf("Sorry, %s isn't implemented yet.\n", argv[0]);
- return argc;
+ int all = 0;
+
+ int c = 0;
+ while ((c = getopt(argc, argv, "a")) != -1) {
+ switch (c) {
+ case 'a':
+ all = 1;
+ break;
+
+ default:
+ return 1;
+ }
+ }
+
+ if (all) {
+ if (optind < argc) {
+ fprintf(stderr, "unalias: -a takes no arguments\n");
+ return 1;
+ }
+ for (struct alias *p = aliases; p != NULL; p = free_alias(p)) {
+ /* handled by update step */
+ }
+ return 0;
+ }
+
+ if (optind >= argc) {
+ fprintf(stderr, "unalias: missing operands\n");
+ return 1;
+ }
+
+ int ret = 0;
+ for (int i = optind; i < argc; i++) {
+ int removed = 0;
+ for (struct alias *p = aliases; p != NULL; p = p->next) {
+ if (strcmp(p->alias, argv[i]) == 0) {
+ removed = 1;
+ free_alias(p);
+ break;
+ }
+ }
+ if (!removed) {
+ fprintf(stderr, "unalias: %s not defined\n", argv[i]);
+ ret = 1;
+ }
+ }
+
+ return ret;
}
diff --git a/builtins.c b/builtins.c
index a1952fa..1736411 100644
--- a/builtins.c
+++ b/builtins.c
@@ -79,11 +79,14 @@ int newgrp_main(int argc, char *argv[]);
int pwd_main(int argc, char *argv[]);
int read_main(int argc, char *argv[]);
int true_main(int argc, char *argv[]);
+int type_main(int argc, char *argv[]);
int ulimit_main(int argc, char *argv[]);
int umask_main(int argc, char *argv[]);
int unalias_main(int argc, char *argv[]);
int wait_main(int argc, char *argv[]);
+int unspecified_main(int argc, char *argv[]);
+
#ifndef EXTRA_BUILTINS
#define EXTRA_BUILTINS /* empty */
#endif
@@ -103,6 +106,7 @@ static struct builtin regular_builtins[] = {
{ "pwd", pwd_main },
{ "read", read_main },
{ "true", true_main },
+ { "type", type_main },
{ "ulimit", ulimit_main },
{ "umask", umask_main },
{ "unalias", unalias_main },
@@ -111,6 +115,60 @@ static struct builtin regular_builtins[] = {
{ 0, 0 },
};
+static struct builtin unspecified_builtins[] = {
+ { "alloc", unspecified_main },
+ { "autoload", unspecified_main },
+ { "bind", unspecified_main },
+ { "bindkey", unspecified_main },
+ { "builtin", unspecified_main },
+ { "bye", unspecified_main },
+ { "caller", unspecified_main },
+ { "cap", unspecified_main },
+ { "chdir", unspecified_main },
+ { "clone", unspecified_main },
+ { "copmarguments", unspecified_main },
+ { "compcall", unspecified_main },
+ { "compctl", unspecified_main },
+ { "compdescribe", unspecified_main },
+ { "compfiles", unspecified_main },
+ { "compgen", unspecified_main },
+ { "compgroups", unspecified_main },
+ { "complete", unspecified_main },
+ { "compquote", unspecified_main },
+ { "comptags", unspecified_main },
+ { "comptry", unspecified_main },
+ { "compvalues", unspecified_main },
+ { "declare", unspecified_main },
+ { "dirs", unspecified_main },
+ { "disable", unspecified_main },
+ { "disown", unspecified_main },
+ { "dosh", unspecified_main },
+ { "echotc", unspecified_main },
+ { "echoti", unspecified_main },
+ { "help", unspecified_main },
+ { "history", unspecified_main },
+ { "hist", unspecified_main },
+ { "let", unspecified_main },
+ { "local", unspecified_main },
+ { "login", unspecified_main },
+ { "logout", unspecified_main },
+ { "map", unspecified_main },
+ { "mapfile", unspecified_main },
+ { "popd", unspecified_main },
+ { "print", unspecified_main },
+ { "pushed", unspecified_main },
+ { "readarray", unspecified_main },
+ { "repeat", unspecified_main },
+ { "savehistory", unspecified_main },
+ { "source", unspecified_main },
+ { "shopt", unspecified_main },
+ { "stop", unspecified_main },
+ { "suspend", unspecified_main },
+ { "typeset", unspecified_main },
+ { "whence", unspecified_main },
+ { 0, 0 },
+};
+
static int (*sh_getbuiltin(const char *util, struct builtin *bi))(int argc, char *argv[])
{
for (int i = 0; bi[i].name; i++) {
@@ -138,11 +196,15 @@ int sh_builtin(int argc, char *argv[])
int (*m)(int, char *[]) = sh_getbuiltin(util, special_builtins);
if (m == NULL) {
m = sh_getbuiltin(util, regular_builtins);
- if (m == NULL) {
- return 1;
- }
}
+ if (m == NULL) {
+ m = sh_getbuiltin(util, unspecified_builtins);
+ }
+
+ if (m == NULL) {
+ return 1;
+ }
optind = 0;
return m(argc, argv);
@@ -169,6 +231,11 @@ int sh_is_regular_builtin(const char *util)
return is_builtin(util, regular_builtins);
}
+int sh_is_unspecified(const char *util)
+{
+ return is_builtin(util, unspecified_builtins);
+}
+
#define main true_main
#include "true/true.c"
#undef main
diff --git a/cd.c b/cd.c
index 29aba49..7233f8d 100644
--- a/cd.c
+++ b/cd.c
@@ -17,7 +17,6 @@
*
*/
-#include "sh.h"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
@@ -25,6 +24,11 @@
#include <stddef.h>
#include <string.h>
#include <unistd.h>
+#include "sh.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX _POSIX_PATH_MAX
+#endif
static void cd_in_cdpath(size_t n, char p[])
{
@@ -49,7 +53,8 @@ static void cd_in_cdpath(size_t n, char p[])
static void cd_make_canonical(char *path)
{
- char tmp[strlen(path)];
+ char tmp[strlen(path) + 1];
+ strcpy(tmp, path);
/* TODO */
@@ -131,6 +136,7 @@ int cd_main(int argc, char *argv[])
return 1;
}
+ getcwd(curpath, sizeof(curpath));
setenv("OLDPWD", oldpath, 1);
setenv("PWD", curpath, 1);
return 0;
diff --git a/interactive.c b/interactive.c
index 6db84b0..cad6867 100644
--- a/interactive.c
+++ b/interactive.c
@@ -19,46 +19,90 @@
#define _XOPEN_SOURCE 700
-#include "sh.h"
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#ifdef SIGWINCH
+#include <sys/ioctl.h>
+#endif
+
+#include "sh.h"
+#include "shed.h"
-static char *sh_getline(char **cmdline)
+sig_atomic_t got_sigwinch = 0;
+static char sh_columns[8] = "80";
+static char sh_lines[8] = "25";
+
+#ifdef SIGWINCH
+static void sh_sigwinch(int sig)
{
- char *prompt = getenv(*cmdline ? "PS2" : "PS1");
- /* wordexpand prompt */
- fprintf(stderr, "%s", prompt);
+ got_sigwinch = 1;
+ signal(sig, sh_sigwinch);
+}
+#endif
- size_t n = 0;
- getline(cmdline, &n, stdin);
- return *cmdline;
+void sh_getwinsize(void)
+{
+ #ifdef TIOCGWINSZ
+ struct winsize ws = { 0 };
+ ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
+ snprintf(sh_columns, sizeof(sh_columns), "%hhu", ws.ws_col);
+ snprintf(sh_lines, sizeof(sh_lines), "%hhu", ws.ws_row);
+ #else
+ FILE *cols = popen("tput cols", "r");
+ if (cols) {
+ fread(cols, 1, sizeof(sh_columns), sh_columns);
+ fclose(cols);
+ }
+
+ FILE *lines = popen("tput lines", "r");
+ if (lines) {
+ fread(lines, 1, sizeof(sh_lines), sh_lines);
+ fclose(lines);
+ }
+ #endif
+
+ setenv("COLUMNS", sh_columns, 1);
+ setenv("LINES", sh_lines, 1);
}
int sh_interactive(void)
{
- while (!feof(stdin)) {
- char *cmdline = NULL;
- sh_getline(&cmdline);
+ struct shed ed = {
+ .prompt = getenv("PS1"),
+ .handle = shed_handle_non_vi,
+ };
+
+ sh_getwinsize();
+ #ifdef SIGWINCH
+ signal(SIGWINCH, sh_sigwinch);
+ #endif
- if (cmdline == NULL) {
- break;
+ while (shed(&ed) != NULL) {
+ if (ed.cur->nread == 1 && ed.cur->buf[0] == CTRL_D) {
+ printf("\n");
+ return 0;
}
- struct command *command = sh_parse(cmdline);
- #if 0
- while (command == NULL) {
- /* append more text */
- /* attempt parsing again */
+ if (strlen(ed.cur->buf) == 0) {
+ continue;
+ }
+
+ struct command *command = sh_parse(ed.cur->buf);
+ if (errno == SIGINT) {
+ if (got_sigwinch) {
+ sh_getwinsize();
+ }
}
- #endif
if (command) {
sh_execute(command);
sh_freecmd(command);
}
-
- free(cmdline);
}
return 0;
diff --git a/parse.c b/parse.c
index 61e41ee..3fae890 100644
--- a/parse.c
+++ b/parse.c
@@ -17,8 +17,6 @@
*
*/
-#define _XOPEN_SOURCE 700
-
#include "sh.h"
#include <ctype.h>
#include <errno.h>
@@ -27,10 +25,10 @@
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <wordexp.h>
struct simple_command {
- int argc;
- char **argv;
+ wordexp_t we;
char **redirection;
};
@@ -59,48 +57,34 @@ struct command {
struct command *sh_parse(const char *cmdline)
{
- char *l = strdup(cmdline);
- char *start = l;
- while (isspace(*start)) {
- start++;
- }
-
- char *end = l + strlen(l) - 1;
- while (isspace(*end) && end > start) {
- *end = '\0';
- end--;
+ struct command *cmd = calloc(1, sizeof(*cmd));
+ if (cmd == NULL) {
+ return cmd;
}
+ cmd->type = SIMPLE;
- if (end <= start) {
- free(l);
+ if (wordexp(cmdline, &cmd->cmd.simple.we, WRDE_SHOWERR) != 0) {
+ free(cmd);
return NULL;
}
- struct command *cmd = calloc(1, sizeof(*cmd));
- if (cmd == NULL) {
- /* TODO: should probably crash here */
- return cmd;
- }
+ char *alias = sh_get_alias(cmd->cmd.simple.we.we_wordv[0]);
+ if (alias) {
+ if (cmd->cmd.simple.we.we_wordc == 1) {
+ free(cmd);
+ return sh_parse(alias);
+ }
- cmd->type = SIMPLE;
- cmd->cmd.simple.argv = calloc(100, sizeof(char *));
+ /* TODO: handle parameters */
+ }
- char *next = strtok(start, " ");
- do {
- cmd->cmd.simple.argv[cmd->cmd.simple.argc++] = strdup(next);
- } while ((next = strtok(NULL, " ")) != NULL);
-
- free(l);
return cmd;
}
void sh_freecmd(struct command *command)
{
if (command->type == SIMPLE) {
- struct simple_command *c = &(command->cmd.simple);
- for (int i = 0; c->argv[i]; i++) {
- free(c->argv[i]);
- }
+ wordfree(&command->cmd.simple.we);
}
free(command);
}
@@ -129,33 +113,40 @@ char *sh_find_in_path(const char *file, const char *pathvar)
int sh_simple_command(struct simple_command *c)
{
- char *path = c->argv[0];
+ char *path = c->we.we_wordv[0];
if (!strchr(path, '/')) {
if (sh_is_special_builtin(path)) {
- return sh_builtin(c->argc, c->argv);
+ return sh_builtin(c->we.we_wordc, c->we.we_wordv);
+ }
+
+ if (sh_is_unspecified(path)) {
+ return sh_builtin(c->we.we_wordc, c->we.we_wordv);
+ return 1;
}
/*
if (sh_is_function(path)) {
- return sh_function(c->argc, c->argv);
+ return sh_function(c->we.we_wordc, c->we.we_wordv);
}
*/
if (sh_is_regular_builtin(path)) {
- return sh_builtin(c->argc, c->argv);
+ return sh_builtin(c->we.we_wordc, c->we.we_wordv);
}
path = sh_find_in_path(path, "PATH");
}
if (path == NULL) {
- fprintf(stderr, "sh: %s: %s\n", c->argv[0], strerror(ENOENT));
+ fprintf(stderr, "sh: %s: %s\n", c->we.we_wordv[0], strerror(ENOENT));
+ return 1;
}
pid_t pid = fork();
if (pid == 0) {
- execv(path, c->argv/*, exported_environ*/);
+ extern char **environ;
+ execve(path, c->we.we_wordv, environ);
fprintf(stderr, "sh: %s: %s\n", path, strerror(errno));
exit(1);
}
diff --git a/pwd b/pwd
new file mode 160000
+Subproject bbfcb397ceaefd5abc3504b85127386cd6a45bd
diff --git a/pwd.c b/pwd.c
index 00e26e2..beb0b19 100644
--- a/pwd.c
+++ b/pwd.c
@@ -1,7 +1,7 @@
/*
* UNG's Not GNU
*
- * Copyright (c) 2011, Jakob Kaivo <jakob@kaivo.net>
+ * Copyright (c) 2011-2020, Jakob Kaivo <jakob@kaivo.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,47 +18,11 @@
*/
#include "sh.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
+#define main real_pwd_main
+#include "pwd/pwd.c"
-const char *pwd_desc = "return working directory name";
-const char *pwd_inv = "pwd [-L|-P]";
-
-int
-pwd_main (int argc, char **argv)
+int pwd_main(int argc, char *argv[])
{
- //char mode = 'L';
- char *cwd;
- int c;
-
- while ((c = getopt (argc, argv, ":LP")) != -1) {
- switch (c) {
- case 'L':
- case 'P':
- ////mode = c;
- break;
- default:
- return 1;
- }
- }
-
- if (optind < argc)
- return 1;
-
- // FIXME:
- // if (mode == 'L')
- // if (cwd !~ /./ && cwd !~ /../)
- // print (cwd);
- // else if (strlen (cwd) > PATH_MAX && NO_DOT_OR_DOT_DOT)
- // unspecified
- // else
- // mode = 'P';
- // if (mode == 'P')
- // decompose_to_shortest_path
- // print (decomposed)
- cwd = (char*)getenv ("PWD");
- printf ("%s\n", cwd);
-
- return 0;
+ opterr = 0;
+ return real_pwd_main(argc, argv);
}
diff --git a/set.c b/set.c
index de7f288..0bd718c 100644
--- a/set.c
+++ b/set.c
@@ -110,11 +110,10 @@ int set_main(int argc, char *argv[])
break;
case ':':
- if (optopt == 'o') {
- show = 1;
- } else {
+ if (optopt != 'o') {
return 1;
}
+ show = 1;
break;
default:
diff --git a/sh.h b/sh.h
index 3479baa..a69e867 100644
--- a/sh.h
+++ b/sh.h
@@ -42,6 +42,7 @@ struct command;
struct command *sh_parse(const char *cmdline);
void sh_freecmd(struct command *cmd);
int sh_execute(struct command *cmd);
+char *sh_get_alias(const char *cmd);
void sh_set(char option, int value);
void sh_seto(char *option, int value);
@@ -51,6 +52,7 @@ int sh_geto(char *option);
int sh_interactive(void);
int sh_is_special_builtin(const char *util);
+int sh_is_unspecified(const char *util);
int sh_is_regular_builtin(const char *util);
int (*sh_get_builtin(const char *util))(int argc, char *argv[]);
int sh_builtin(int argc, char *argv[]);
diff --git a/sh.l b/sh.l
index c38ea7c..289c97a 100644
--- a/sh.l
+++ b/sh.l
@@ -1,9 +1,7 @@
%{
+#include <stdio.h>
#include "sh.h"
#include "sh.tab.h"
-/* thanks, flex, for implicitly declaring identifiers */
-/* bonus points for something that isn't part of ISO C */
-extern int fileno(FILE *);
%}
NAME [_a-zA-Z0-9]
@@ -65,6 +63,11 @@ OPERATOR [()|;&<>]
{WHITESPACE} ;
%%
+int yywrap(void)
+{
+ return 1;
+}
+
void sh_silence_warning(void)
{
input();
diff --git a/sh.y b/sh.y
index 14ad022..7a981d7 100644
--- a/sh.y
+++ b/sh.y
@@ -310,3 +310,11 @@ sequential_sep : ';' linebreak {
puts("sequential_sep:newline_list");
}
;
+
+%%
+
+int yyerror(const char *s)
+{
+ return fprintf(stderr, "%s\n", s);
+}
+
diff --git a/shed.c b/shed.c
new file mode 100644
index 0000000..ea3b3ee
--- /dev/null
+++ b/shed.c
@@ -0,0 +1,79 @@
+#define _POSIX_C_SOURCE 200809L
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include "shed.h"
+
+/*
+ * BIG FAT TODO: DEAL WITH MULTIBYTE CHARACTERS CORRECTLY
+ */
+
+struct shed *shed(struct shed *e)
+{
+ if (e == NULL) {
+ e = calloc(1, sizeof(*e));
+ if (e == NULL) {
+ return NULL;
+ }
+ }
+
+ /* TODO: head/tail/history */
+ if (e->cur == NULL) {
+ e->cur = calloc(1, sizeof(*e->cur));
+ if (e->cur == NULL) {
+ /* FIXME */
+ return e;
+ }
+ e->handle = shed_handle_non_vi;
+ e->head = e->cur;
+ e->tail = e->cur;
+ } else {
+ /* TODO: skip if the last input was blank */
+ e->tail->next = calloc(1, sizeof(*e->cur));
+ if (e->tail->next == NULL) {
+ /* FIXME */
+ return e;
+ }
+ e->tail->next->prev = e->tail;
+ e->tail = e->tail->next;
+ e->cur = e->tail;
+ }
+
+ struct termios original_tio;
+ if (tcgetattr(STDIN_FILENO, &original_tio) != 0) {
+ e->cur = NULL;
+ return e;
+ }
+
+ struct termios tio = original_tio;
+ tio.c_iflag &= ~BRKINT;
+ tio.c_lflag &= ~ECHO;
+ tio.c_lflag &= ~IEXTEN;
+ tio.c_lflag &= ~ICANON;
+ tio.c_lflag &= ~ISIG;
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &tio);
+
+ if (e->prompt == NULL) {
+ e->prompt = getenv("PS1");
+ if (e->prompt == NULL) {
+ e->prompt = "$ ";
+ }
+ }
+ write(STDOUT_FILENO, e->prompt, strlen(e->prompt));
+
+ char c;
+ do {
+ if (read(STDIN_FILENO, &c, 1) == -1) {
+ //b.nread = -1;
+ /* TODO: signal failure */
+ break;
+ }
+ } while (e->handle(e, &tio, c));
+
+ /* TODO: make sure this doesn't overflow */
+ e->cur->buf[e->cur->nread] = '\0';
+
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &original_tio);
+ return e;
+}
diff --git a/shed.h b/shed.h
new file mode 100644
index 0000000..a11f982
--- /dev/null
+++ b/shed.h
@@ -0,0 +1,99 @@
+#ifndef SHED_H
+#define SHED_H
+
+#include <sys/types.h>
+
+enum {
+ CTRL_A = 0x01,
+ CTRL_B = 0x02,
+ CTRL_C = 0x03,
+ CTRL_D = 0x04,
+ CTRL_E = 0x05,
+ CTRL_F = 0x06,
+ CTRL_G = 0x07,
+ CTRL_H = 0x08,
+ CTRL_I = 0x09,
+ CTRL_J = 0x0a,
+ CTRL_K = 0x0b,
+ CTRL_L = 0x0c,
+ CTRL_M = 0x0d,
+ CTRL_N = 0x0e,
+ CTRL_O = 0x0f,
+ CTRL_P = 0x10,
+ CTRL_Q = 0x11,
+ CTRL_R = 0x12,
+ CTRL_S = 0x13,
+ CTRL_T = 0x14,
+ CTRL_U = 0x15,
+ CTRL_V = 0x16,
+ CTRL_W = 0x17,
+ CTRL_X = 0x18,
+ CTRL_Y = 0x19,
+ CTRL_Z = 0x1a,
+ ESCAPE = 0x1b,
+};
+
+struct termios;
+
+struct buffer {
+ struct buffer *next;
+ struct buffer *prev;
+ size_t len;
+ size_t pos;
+ ssize_t nread;
+ char buf[512]; /* TODO: dynamic */
+};
+
+struct shed {
+ char *prompt;
+ int (*handle)(struct shed *e, struct termios *, char);
+ struct buffer *head;
+ struct buffer *tail;
+ struct buffer *cur;
+ int count;
+};
+
+void shed_replace_char(struct buffer *b, char c);
+void shed_insert_char(struct buffer *b, char c);
+void shed_remove_char(struct buffer *b, int forward);
+void shed_move_cursor(struct buffer *b, int move);
+
+int shed_append(struct shed *e);
+int shed_append_end(struct shed *e);
+int shed_backspace(struct shed *e);
+int shed_cancel(struct shed *e);
+int shed_change(struct shed *e);
+int shed_comment(struct shed *e);
+int shed_convert_case(struct shed *e);
+int shed_eof(struct shed *e);
+int shed_delete(struct shed *e);
+int shed_delete_toend(struct shed *e);
+int shed_erase(struct shed *e);
+int shed_execute(struct shed *e);
+int shed_insert(struct shed *e);
+int shed_insert_beginning(struct shed *e);
+int shed_redraw(struct shed *e);
+int shed_replace(struct shed *e);
+int shed_start_over(struct shed *e);
+int shed_worderase(struct shed *e);
+
+int shed_complete_wordexp(struct shed *e);
+
+int shed_history_backward(struct shed *e);
+int shed_history_forward(struct shed *e);
+
+int shed_move_forward(struct shed *e);
+int shed_move_backward(struct shed *e);
+int shed_move_beginning(struct shed *e);
+int shed_move_end(struct shed *e);
+int shed_move_0(struct shed *e);
+int shed_move_column(struct shed *e);
+
+int shed_handle_insert(struct shed *e, struct termios *t, char c);
+int shed_handle_replace(struct shed *e, struct termios *t, char c);
+int shed_handle_edit(struct shed *e, struct termios *t, char c);
+int shed_handle_non_vi(struct shed *e, struct termios *t, char c);
+
+struct shed *shed(struct shed *e);
+
+#endif
diff --git a/shed_commands.c b/shed_commands.c
new file mode 100644
index 0000000..51b986b
--- /dev/null
+++ b/shed_commands.c
@@ -0,0 +1,164 @@
+#define _POSIX_C_SOURCE 200809L
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "shed.h"
+
+int shed_append(struct shed *e)
+{
+ e->count = 1;
+ shed_move_forward(e);
+ return shed_insert(e);
+}
+
+int shed_append_end(struct shed *e)
+{
+ shed_move_end(e);
+ return shed_insert(e);
+}
+
+int shed_backspace(struct shed *e)
+{
+ int count = e->count ? e->count : 1;
+ for (int i = 0; i < count; i++) {
+ shed_remove_char(e->cur, 0);
+ }
+ return 1;
+}
+
+int shed_cancel(struct shed *e)
+{
+ struct buffer *b = e->cur;
+ write(STDOUT_FILENO, "^C\n", 3);
+ b->nread = 0;
+ return 0;
+}
+
+int shed_change(struct shed *e)
+{
+ shed_delete_toend(e);
+ return shed_append_end(e);
+}
+
+int shed_comment(struct shed *e)
+{
+ shed_move_cursor(e->cur, -e->cur->pos);
+ shed_insert_char(e->cur, '#');
+ return 0;
+}
+
+int shed_convert_case(struct shed *e)
+{
+ char *b = e->cur->buf;
+ size_t pos = e->cur->pos;
+ int count = e->count ? e->count : 1;
+
+ /* TODO: don't overflow */
+ for (int i = 0; i < count && pos + i < (size_t)e->cur->nread; i++) {
+ if (isupper(b[pos + i])) {
+ shed_replace_char(e->cur, tolower(b[pos + i]));
+ } else if (islower(b[pos + i])) {
+ shed_replace_char(e->cur, toupper(b[pos + i]));
+ } else {
+ shed_replace_char(e->cur, b[pos + i]);
+ }
+ }
+ return 1;
+}
+
+int shed_delete(struct shed *e)
+{
+ int count = e->count ? e->count : 1;
+ for (int i = 0; i < count; i++) {
+ shed_remove_char(e->cur, 1);
+ }
+ return 1;
+}
+
+int shed_delete_toend(struct shed *e)
+{
+ while (e->cur->pos < (size_t)e->cur->nread) {
+ shed_remove_char(e->cur, 1);
+ }
+ return 1;
+}
+
+int shed_eof(struct shed *e)
+{
+ struct buffer *b = e->cur;
+ if (b->nread == 0) {
+ b->buf[0] = CTRL_D;
+ b->nread = 1;
+ return 0;
+ }
+ return 1;
+}
+
+int shed_erase(struct shed *e)
+{
+ struct buffer *b = e->cur;
+ while (b->pos != 0) {
+ shed_remove_char(b, 0);
+ }
+ return 1;
+}
+
+int shed_execute(struct shed *e)
+{
+ struct buffer *b = e->cur;
+ b->pos = b->nread;
+ //shed_insert_char(b, '\n');
+ write(STDOUT_FILENO, "\n", 1);
+ if (e->handle == shed_handle_edit) {
+ e->handle = shed_handle_insert;
+ }
+ return 0;
+}
+
+int shed_insert(struct shed *e)
+{
+ e->handle = shed_handle_insert;
+ return 1;
+}
+
+int shed_insert_beginning(struct shed *e)
+{
+ shed_move_0(e);
+ return shed_insert(e);
+}
+
+int shed_redraw(struct shed *e)
+{
+ system("tput clear"); // TODO: pfork() and cache the output
+ write(STDOUT_FILENO, e->prompt, strlen(e->prompt));
+ write(STDOUT_FILENO, e->cur->buf, e->cur->nread);
+ int move = e->cur->nread - e->cur->pos;
+ e->cur->pos = e->cur->nread;
+ shed_move_cursor(e->cur, -move);
+ return 1;
+}
+
+int shed_replace(struct shed *e)
+{
+ e->handle = shed_handle_replace;
+ return 1;
+}
+
+int shed_start_over(struct shed *e)
+{
+ shed_move_0(e);
+ return shed_change(e);
+}
+
+int shed_worderase(struct shed *e)
+{
+ struct buffer *b = e->cur;
+ while (b->pos != 0 && (isblank(b->buf[b->pos - 1]) || ispunct(b->buf[b->pos - 1]))) {
+ shed_remove_char(b, 0);
+ }
+ while (b->pos != 0 && !(isspace(b->buf[b->pos - 1]) || ispunct(b->buf[b->pos - 1]))) {
+ shed_remove_char(b, 0);
+ }
+ return 1;
+}
diff --git a/shed_complete.c b/shed_complete.c
new file mode 100644
index 0000000..145e0a7
--- /dev/null
+++ b/shed_complete.c
@@ -0,0 +1,52 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wordexp.h>
+#include "shed.h"
+
+static void shed_reprompt(struct shed *e)
+{
+ write(STDOUT_FILENO, e->prompt, strlen(e->prompt));
+ size_t pos = e->cur->pos;
+ e->cur->pos = 0;
+ e->count = (int)pos + 1;
+ shed_move_column(e);
+ e->count = 0;
+}
+
+int shed_complete_wordexp(struct shed *e)
+{
+ char *start = e->cur->buf + e->cur->pos;
+ while (!isblank(*start) && start != e->cur->buf) {
+ start--;
+ }
+ if (isblank(*start)) {
+ start++;
+ }
+
+ char *word = calloc(1, strlen(start) + 2);
+ strcpy(word, start);
+ char *end = word;
+ while (!isblank(*end) && *end != '\0') {
+ end++;
+ }
+ *end = '\0';
+
+ if (strpbrk(word, "*?[") == NULL) {
+ strcat(word, "*");
+ }
+
+ wordexp_t we = { 0 };
+ wordexp(word, &we, WRDE_NOCMD);
+ for (size_t i = 0; i < we.we_wordc; i++) {
+ write(STDOUT_FILENO, i == 0 ? "\n" : "\t", 1);
+ write(STDOUT_FILENO, we.we_wordv[i], strlen(we.we_wordv[i]));
+ }
+ write(STDOUT_FILENO, "\n", 2);
+ wordfree(&we);
+
+ free(word);
+ shed_reprompt(e);
+ return 1;
+}
diff --git a/shed_edit.c b/shed_edit.c
new file mode 100644
index 0000000..b742414
--- /dev/null
+++ b/shed_edit.c
@@ -0,0 +1,97 @@
+#define _POSIX_C_SOURCE 200809L
+#include <ctype.h>
+#include <stdlib.h>
+#include "shed.h"
+
+int shed_handle_edit(struct shed *e, struct termios *t, char c)
+{
+ (void)t;
+ int r = -1;
+
+ switch (c) {
+ case '\n': r = shed_execute(e); break;
+ case CTRL_L: r = shed_redraw(e); break;
+ case '#': r = shed_comment(e); break;
+
+ case '=': // r = shed_complete_wordexp(e); break;
+ case '\\': // r = shed_complete_pathname(e); break;
+ case '*': // r = shed_complete_wildcard(e); break;
+
+ case '@': // r = shed_insert_alias(e); // read one letter
+
+ case '~': r = shed_convert_case(e); break;
+ case '.': // r = shed_repeat(e); break;
+ case 'v': // r = shed_invoke_vi(e); break;
+
+ case 'l': /* FALLTHROUGH */
+ case ' ': r = shed_move_forward(e); break;
+ case 'h': r = shed_move_backward(e); break;
+ case 'w': // r = shed_move_forword(e); break;
+ case 'W': // r = shed_move_forbigword(e); break;
+ case 'e': // r = shed_move_endword(e); break;
+ case 'E': // r = shed_move_endbigword(e); break;
+ case 'b': // r = shed_move_backword(e); break;
+ case 'B': // r = shed_move_backbigword(e); break;
+ case '^': r = shed_move_beginning(e); break;
+ case '$': r = shed_move_end(e); break;
+ case '0': r = shed_move_0(e); break;
+ case '|': r = shed_move_column(e); break;
+
+ case 'f': // r = shed_move_first(e); break;
+ case 'F': // r = shed_move_prev(e); break;
+ case 't': // r = shed_move_before(e); break;
+ case 'T': // r = shed_move_after(e); break;
+ case ';': // r = shed_repeat_fftt(e); break;
+ case ',': // r = shed_reverse_fftt(e); break;
+
+ case 'a': r = shed_append(e); break;
+ case 'A': r = shed_append_end(e); break;
+ case 'i': r = shed_insert(e); break;
+ case 'I': r = shed_insert_beginning(e); break;
+ case 'R': r = shed_replace(e); break;
+
+ case 'c': // r = shed_delete_to(e); break;
+ case 'C': r = shed_change(e); break;
+ case 'S': r = shed_start_over(e); break;
+
+ case 'r': // r = shed_replace(e); break;
+ case '_': // ???
+ case 'x': r = shed_delete(e); break;
+ case 'X': r = shed_backspace(e); break;
+ case 'd': // r = shed_delete_motion(e); break;
+ case 'D': r = shed_delete_toend(e); break;
+ case 'y': // r = shed_yank(e); break;
+ case 'Y': // r = shed_bigyank(e); break;
+ case 'p': // r = shed_paste(e); break;
+ case 'P': // r = shed_bigpaste(e); break;
+
+ case 'u': // r = shed_undo(e); break;
+ case 'U': // r = shed_bigundo(e); break;
+
+ case 'k': /* FALLTHROUGH */
+ case '-': r = shed_history_backward(e); break;
+
+ case 'j': /* FALLTHOUGH */
+ case '+': r = shed_history_forward(e); break;
+
+ case 'G': // r = shed_history_first(e); break;
+ case '/': // r = shed_history_backsearch(e); break;
+ case '?': // r = shed_history_forwardsearch(e); break;
+ case 'n': // r = shed_history_repeat(e); break;
+ case 'N': // r = shed_history_reverse(e); break;
+
+ default:
+ break;
+ }
+
+ if (isdigit(c)) {
+ char digit[2] = { c, '\0' };
+ e->count = (e->count * 10) + atoi(digit);
+ }
+
+ if (r != -1) {
+ e->count = 0;
+ }
+
+ return r;
+}
diff --git a/shed_history.c b/shed_history.c
new file mode 100644
index 0000000..00ce0e1
--- /dev/null
+++ b/shed_history.c
@@ -0,0 +1,41 @@
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include "shed.h"
+
+static int shed_refresh(struct shed *e, struct buffer *next)
+{
+ shed_move_end(e);
+ size_t s = e->cur->pos;
+ char clrbuf[s];
+
+ memset(clrbuf, '\b', s);
+ write(STDOUT_FILENO, clrbuf, s);
+
+ memset(clrbuf, ' ', s);
+ write(STDOUT_FILENO, clrbuf, s);
+
+ memset(clrbuf, '\b', s);
+ write(STDOUT_FILENO, clrbuf, s);
+
+ e->cur = next;
+ e->cur->pos = 0;
+ shed_move_end(e);
+ return 1;
+}
+
+int shed_history_forward(struct shed *e)
+{
+ if (e->cur->next != NULL) {
+ return shed_refresh(e, e->cur->next);
+ }
+ return 1;
+}
+
+int shed_history_backward(struct shed *e)
+{
+ if (e->cur->prev != NULL) {
+ return shed_refresh(e, e->cur->prev);
+ }
+ return 1;
+}
diff --git a/shed_insert.c b/shed_insert.c
new file mode 100644
index 0000000..384cbea
--- /dev/null
+++ b/shed_insert.c
@@ -0,0 +1,62 @@
+#define _POSIX_C_SOURCE 200809L
+#include <termios.h>
+#include "shed.h"
+
+static int shed_insert_special(struct shed *e, struct termios *t, char c)
+{
+ if (c == '\n') {
+ return shed_execute(e);
+ }
+
+ if (c == t->c_cc[VERASE]) {
+ return shed_backspace(e);
+ }
+
+ if (c == t->c_cc[VINTR]) {
+ return shed_cancel(e);
+ }
+
+ if (c == t->c_cc[VKILL]) {
+ return shed_erase(e);
+ }
+
+ if (c == CTRL_V) {
+ /* quote the next character */
+ return 1;
+ }
+
+ if (c == CTRL_W) {
+ return shed_worderase(e);
+ }
+
+ if (c == t->c_cc[VEOF]) {
+ return shed_eof(e);
+ }
+
+ if (c == ESCAPE) {
+ e->handle = shed_handle_edit;
+ return 1;
+ }
+
+ return -1;
+}
+
+int shed_handle_insert(struct shed *e, struct termios *t, char c)
+{
+ int r = shed_insert_special(e, t, c);
+ if (r == -1) {
+ shed_insert_char(e->cur, c);
+ r = 1;
+ }
+ return r;
+}
+
+int shed_handle_replace(struct shed *e, struct termios *t, char c)
+{
+ int r = shed_insert_special(e, t, c);
+ if (r == -1) {
+ shed_replace_char(e->cur, c);
+ r = 1;
+ }
+ return r;
+}
diff --git a/shed_io.c b/shed_io.c
new file mode 100644
index 0000000..17aee8e
--- /dev/null
+++ b/shed_io.c
@@ -0,0 +1,91 @@
+#define _POSIX_C_SOURCE 200809L
+#include <string.h>
+#include <unistd.h>
+#include "shed.h"
+
+void shed_replace_char(struct buffer *b, char c)
+{
+ if (b->pos >= (size_t)b->nread) {
+ shed_insert_char(b, c);
+ return;
+ }
+ b->buf[b->pos] = c;
+ write(STDOUT_FILENO, &c, 1);
+ b->pos++;
+}
+
+void shed_insert_char(struct buffer *b, char c)
+{
+ if (b->pos >= (size_t)b->nread) {
+ b->buf[b->pos] = c;
+ write(STDOUT_FILENO, &c, 1);
+ } else {
+ size_t shift = b->nread - b->pos;
+ char *start = b->buf + b->pos;
+ memmove(start + 1, start, shift);
+ *start = c;
+
+ char back[shift];
+ memset(back, '\b', shift);
+ write(STDOUT_FILENO, start, shift + 1);
+ write(STDOUT_FILENO, back, shift);
+ }
+
+ b->pos++;
+ b->nread++;
+}
+
+void shed_remove_char(struct buffer *b, int forward)
+{
+ /* TODO: don't forward delete past the end */
+ if (forward) {
+ write(STDOUT_FILENO, b->buf + b->pos, 1);
+ b->pos++;
+ }
+
+ if (b->pos == 0) {
+ return;
+ }
+
+ b->pos--;
+
+ char *start = b->buf + b->pos;
+ size_t shift = (size_t)b->nread - b->pos;
+
+ b->nread--;
+
+ for (size_t i = 0; i < shift; i++) {
+ start[i] = start[i + 1];
+ }
+ start[shift] = '\0';
+
+ char back[shift];
+ memset(back, '\b', shift);
+ write(STDOUT_FILENO, back, 1);
+ write(STDOUT_FILENO, start, shift);
+ write(STDOUT_FILENO, " \b", 2);
+ write(STDOUT_FILENO, back, shift - 1);
+}
+
+void shed_move_cursor(struct buffer *b, int move)
+{
+ if (move > 0) {
+ int maxfwd = (int)(b->nread - b->pos);
+ if (move > maxfwd) {
+ move = maxfwd;
+ }
+ write(STDOUT_FILENO, b->buf + b->pos, move);
+ b->pos += move;
+ }
+
+ if (move < 0) {
+ size_t len = (size_t)(-move);
+ if (len > b->pos) {
+ len = b->pos;
+ }
+ char back[len];
+ memset(back, '\b', len);
+ write(STDOUT_FILENO, back, len);
+ b->pos -= len;
+ }
+}
diff --git a/shed_move.c b/shed_move.c
new file mode 100644
index 0000000..0970085
--- /dev/null
+++ b/shed_move.c
@@ -0,0 +1,45 @@
+#define _POSIX_C_SOURCE 200809L
+#include <ctype.h>
+#include "shed.h"
+
+int shed_move_forward(struct shed *e)
+{
+ shed_move_cursor(e->cur, e->count ? e->count : 1);
+ return 1;
+}
+
+int shed_move_backward(struct shed *e)
+{
+ shed_move_cursor(e->cur, e->count ? -e->count : -1);
+ return 1;
+}
+
+int shed_move_beginning(struct shed *e)
+{
+ shed_move_cursor(e->cur, -e->cur->pos);
+ while (isspace(e->cur->buf[e->cur->pos])) {
+ shed_move_cursor(e->cur, 1);
+ }
+ return 1;
+}
+
+int shed_move_end(struct shed *e)
+{
+ shed_move_cursor(e->cur, e->cur->nread);
+ return 1;
+}
+
+int shed_move_0(struct shed *e)
+{
+ shed_move_cursor(e->cur, -e->cur->pos);
+ return 1;
+}
+
+int shed_move_column(struct shed *e)
+{
+ if (e->count == 0) {
+ e->count = 1;
+ }
+ shed_move_cursor(e->cur, e->count - e->cur->pos - 1);
+ return 1;
+}
diff --git a/shed_non_vi.c b/shed_non_vi.c
new file mode 100644
index 0000000..b915bf3
--- /dev/null
+++ b/shed_non_vi.c
@@ -0,0 +1,105 @@
+#define _POSIX_C_SOURCE 200809L
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+#include "shed.h"
+
+int shed_handle_non_vi(struct shed *e, struct termios *t, char c)
+{
+ if (c == t->c_cc[VEOF]) {
+ if (shed_eof(e) == 0) {
+ return 0;
+ }
+ }
+
+ if (c == t->c_cc[VINTR]) {
+ return shed_cancel(e);
+ }
+
+ if (c == t->c_cc[VEOL]) {
+ return shed_move_end(e);
+ }
+
+ if (c == t->c_cc[VERASE]) {
+ return shed_backspace(e);
+ }
+
+ if (c == t->c_cc[VQUIT]) {
+ /* ^\ */
+ return 1;
+ }
+
+ if (c == t->c_cc[VSTART]) {
+ /* ^Q */
+ return 1;
+ }
+
+ if (c == t->c_cc[VSTOP]) {
+ /* ^S */
+ return 1;
+ }
+
+ if (c == t->c_cc[VSUSP]) {
+ /* ^Z */
+ return 1;
+ }
+
+ if (c == '\033') {
+ char esc[2];
+ read(STDIN_FILENO, esc, 2);
+ if (esc[1] == 'A') {
+ return shed_history_backward(e);
+ }
+ if (esc[1] == 'B') {
+ return shed_history_forward(e);
+ }
+ if (esc[1] == 'C') {
+ return shed_move_forward(e);
+ }
+ if (esc[1] == 'D' && e->cur->pos > 0) {
+ return shed_move_backward(e);
+ }
+ return 1;
+ }
+
+ if (c == '\n') {
+ return shed_execute(e);
+ }
+
+ if (CTRL_A <= c && c <= CTRL_Z) {
+ switch (c) {
+ case CTRL_A: return shed_move_0(e);
+ case CTRL_B: return shed_move_backward(e);
+ case CTRL_C: return shed_cancel(e);
+ case CTRL_D: return shed_delete(e);
+ case CTRL_E: return shed_move_end(e);
+ case CTRL_F: return shed_move_forward(e);
+ case CTRL_G: break; // return abort(e)?
+ case CTRL_H: return shed_backspace(e);
+ case CTRL_I: return shed_complete_wordexp(e);
+ case CTRL_J: return shed_execute(e);
+ case CTRL_K: return shed_delete_toend(e);
+ case CTRL_L: return shed_redraw(e);
+ case CTRL_M: return shed_execute(e);
+ case CTRL_N: return shed_history_forward(e);
+ case CTRL_O: // return ??? /* ??? */
+ case CTRL_P: return shed_history_backward(e);
+ case CTRL_Q: // /* TTY START */
+ case CTRL_R: // return history_backsearch(e);
+ case CTRL_S: // /* TTY STOP */
+ case CTRL_T: break; // /* ??? */
+ case CTRL_U: return shed_erase(e);
+ case CTRL_V: break; // return quote(e);
+ case CTRL_W: return shed_worderase(e);
+ case CTRL_X: // /* ??? */
+ case CTRL_Y: // /* ??? */
+ case CTRL_Z: // /* ??? */
+ break;
+ }
+ return 1;
+ }
+
+ /* regular character */
+ shed_insert_char(e->cur, c);
+ return 1;
+}
diff --git a/type.c b/type.c
index b3f0bf5..2bfc1f8 100644
--- a/type.c
+++ b/type.c
@@ -1,7 +1,64 @@
+#include "sh.h"
#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
int type_main(int argc, char **argv)
{
- printf("Sorry, %s isn't implemented yet.\n", argv[0]);
- return argc;
+ int c;
+ while ((c = getopt(argc, argv, "")) != -1) {
+ switch (c) {
+ default:
+ return 1;
+ }
+ }
+
+ if (optind >= argc) {
+ fprintf(stderr, "type: missing operands\n");
+ return 1;
+ }
+
+ int ret = 0;
+ for (int i = optind; i < argc; i++) {
+ if (sh_is_special_builtin(argv[i])) {
+ printf("%s: special built-in\n", argv[i]);
+ continue;
+ }
+
+ if (sh_is_unspecified(argv[i])) {
+ printf("%s: allowable POSIX extension (not implemented)\n", argv[i]);
+ }
+
+ /*
+ if (sh_is_function(argv[i])) {
+ printf("%s: function\n", argv[i]);
+ continue;
+ }
+ */
+
+ if (sh_is_regular_builtin(argv[i])) {
+ printf("%s: built-in\n", argv[i]);
+ continue;
+ }
+
+ char *alias = sh_get_alias(argv[i]);
+ if (alias != NULL) {
+ printf("%s: alias (%s)\n", argv[i], alias);
+ continue;
+ }
+
+ // keyword
+
+ char *path = sh_find_in_path(argv[i], "PATH");
+ if (path) {
+ printf("%s: %s\n", argv[i], path);
+ free(path);
+ continue;
+ }
+
+ fprintf(stderr, "type: %s not found\n", argv[i]);
+ ret = 1;
+ }
+
+ return ret;
}
diff --git a/umask.c b/umask.c
index f6fca22..124c226 100644
--- a/umask.c
+++ b/umask.c
@@ -47,19 +47,20 @@ int umask_main(int argc, char *argv[])
umask(mask);
if (symbolic) {
- fputs("u=", stdout);
- fputs(mask & S_IRUSR ? "" : "r", stdout);
- fputs(mask & S_IWUSR ? "" : "w", stdout);
- fputs(mask & S_IXUSR ? "" : "x", stdout);
- fputs(",g=", stdout);
- fputs(mask & S_IRGRP ? "" : "r", stdout);
- fputs(mask & S_IWGRP ? "" : "w", stdout);
- fputs(mask & S_IXGRP ? "" : "x", stdout);
- fputs(",o=", stdout);
- fputs(mask & S_IRGRP ? "" : "r", stdout);
- fputs(mask & S_IWGRP ? "" : "w", stdout);
- fputs(mask & S_IXGRP ? "" : "x", stdout);
- putchar('\n');
+ printf("u=");
+ printf("%s", mask & S_IRUSR ? "" : "r");
+ printf("%s", mask & S_IWUSR ? "" : "w");
+ printf("%s", mask & S_IXUSR ? "" : "x");
+
+ printf(",g=");
+ printf("%s", mask & S_IRGRP ? "" : "r");
+ printf("%s", mask & S_IWGRP ? "" : "w");
+ printf("%s", mask & S_IXGRP ? "" : "x");
+
+ printf("%s", ",o=");
+ printf("%s", mask & S_IRGRP ? "" : "r");
+ printf("%s", mask & S_IWGRP ? "" : "w");
+ printf("%s\n", mask & S_IXGRP ? "" : "x");
} else {
printf("%04o\n", mask);
}
diff --git a/unspecified.c b/unspecified.c
new file mode 100644
index 0000000..28c1647
--- /dev/null
+++ b/unspecified.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int unspecified_main(int argc, char *argv[])
+{
+ (void)argc;
+ fprintf(stderr, "sh: %s is an extension allowed by POSIX but not implemented in this shell\n", argv[0]);
+ return 1;
+}