Pull code from yggdrasil userspace
This commit is contained in:
@@ -2,12 +2,17 @@ CC?=$(CROSS_COMPILE)gcc
|
||||
CFLAGS?=-ggdb \
|
||||
-O0 \
|
||||
-Wall \
|
||||
-Werror
|
||||
-Werror \
|
||||
-Iinclude
|
||||
LDFLAGS?=-lgcc
|
||||
|
||||
O=build
|
||||
|
||||
sh_OBJS=$(O)/sh.o
|
||||
sh_OBJS=$(O)/sh.o \
|
||||
$(O)/readline.o \
|
||||
$(O)/cmd.o \
|
||||
$(O)/parse.o \
|
||||
$(O)/builtin.o
|
||||
|
||||
all: mkdirs $(O)/sh
|
||||
|
||||
@@ -23,5 +28,5 @@ install: $(O)/sh
|
||||
$(O)/sh: $(sh_OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(sh_OBJS)
|
||||
|
||||
$(O)/%.o: %.c
|
||||
$(O)/%.o: src/%.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
struct cmd_unit;
|
||||
|
||||
typedef int (*builtin_func_t) (const struct cmd_unit *);
|
||||
|
||||
builtin_func_t builtin_find(const char *name);
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
//struct cmd_exec {
|
||||
// int in_fd, out_fd, err_fd;
|
||||
// size_t argc;
|
||||
// char *args[12];
|
||||
//};
|
||||
//
|
||||
//struct cmd_link {
|
||||
// struct cmd_exec cmd;
|
||||
// struct cmd_link *prev, *next;
|
||||
//};
|
||||
|
||||
int eval(char *str);
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#define COLOR_RED "\033[31m"
|
||||
#define COLOR_GREEN "\033[32m"
|
||||
#define COLOR_YELLOW "\033[33m"
|
||||
#define COLOR_MAGENTA "\033[35m"
|
||||
#define COLOR_CYAN "\033[36m"
|
||||
#define COLOR_RESET "\033[0m"
|
||||
|
||||
// TODO: take this from env (kernel does not support env yet)
|
||||
#define PATH "/bin"
|
||||
#define PS1 COLOR_MAGENTA "%u" COLOR_RESET \
|
||||
"@" \
|
||||
COLOR_CYAN "%h" COLOR_RESET " " \
|
||||
COLOR_YELLOW "%d" COLOR_RESET \
|
||||
" %# "
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
struct cmd_unit {
|
||||
int fds[3];
|
||||
int pid, res;
|
||||
int argc;
|
||||
char *args[64];
|
||||
struct cmd_unit *prev, *next;
|
||||
};
|
||||
|
||||
struct cmd {
|
||||
struct cmd_unit *first, *last;
|
||||
};
|
||||
|
||||
int cmd_parse(const char *str, struct cmd *cmd);
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int readline(char *buf, size_t lim);
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
printf("This is a stub\n");
|
||||
return 0;
|
||||
}
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include "builtin.h"
|
||||
#include "config.h"
|
||||
#include "parse.h"
|
||||
#include "cmd.h"
|
||||
|
||||
#define DEF_BUILTIN(b_name) \
|
||||
static int __bcmd_##b_name(const struct cmd_unit *cmd)
|
||||
#define DECL_BUILTIN(b_name) \
|
||||
{ .name = #b_name, .exec = __bcmd_##b_name }
|
||||
|
||||
extern char **environ;
|
||||
|
||||
struct sh_builtin {
|
||||
const char *name;
|
||||
int (*exec) (const struct cmd_unit *cmd);
|
||||
};
|
||||
|
||||
static struct sh_builtin __builtins[];
|
||||
|
||||
////
|
||||
|
||||
DEF_BUILTIN(env) {
|
||||
for (size_t i = 0; environ[i]; ++i) {
|
||||
printf("%s\n", environ[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// XXX: Broken
|
||||
//DEF_BUILTIN(exit) {
|
||||
// if (cmd->argc > 1) {
|
||||
// exit(atoi(cmd->args[1]));
|
||||
// }
|
||||
// exit(0);
|
||||
//}
|
||||
|
||||
DEF_BUILTIN(cd) {
|
||||
if (cmd->argc != 2) {
|
||||
printf("usage: cd <path>\n");
|
||||
return -1;
|
||||
}
|
||||
return chdir(cmd->args[1]);
|
||||
}
|
||||
|
||||
DEF_BUILTIN(cat) {
|
||||
if (cmd->argc < 2) {
|
||||
printf("usage: cat <filename> ...\n");
|
||||
return -1;
|
||||
}
|
||||
char buf[4096];
|
||||
ssize_t bread;
|
||||
|
||||
for (int i = 1; i < cmd->argc; ++i) {
|
||||
int fd = open(cmd->args[i], O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
perror(cmd->args[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((bread = read(fd, buf, sizeof(buf))) > 0) {
|
||||
write(STDOUT_FILENO, buf, bread);
|
||||
}
|
||||
|
||||
if (bread < 0) {
|
||||
perror(cmd->args[i]);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(chmod) {
|
||||
mode_t mode = 0;
|
||||
const char *p;
|
||||
if (cmd->argc != 3) {
|
||||
printf("usage: chmod <mode> <filename>\n");
|
||||
return -1;
|
||||
}
|
||||
p = cmd->args[1];
|
||||
while (*p) {
|
||||
if (*p > '7' || *p < '0') {
|
||||
printf("Invalid mode: %s\n", cmd->args[1]);
|
||||
return -1;
|
||||
}
|
||||
mode <<= 3;
|
||||
mode |= *p - '0';
|
||||
++p;
|
||||
}
|
||||
|
||||
if (chmod(cmd->args[2], mode)) {
|
||||
perror(cmd->args[2]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(stat) {
|
||||
struct stat st;
|
||||
if (cmd->argc != 2) {
|
||||
printf("usage: stat <filename>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stat(cmd->args[1], &st) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("device %u\n", (unsigned int) st.st_dev); // TODO: __dev_t
|
||||
printf("inode %u\n", (unsigned int) st.st_ino); // TODO: __ino_t
|
||||
printf("mode %u\n", st.st_mode);
|
||||
printf("nlink %u\n", (unsigned int) st.st_nlink); // TODO: __nlink_t
|
||||
printf("uid %u\n", st.st_uid);
|
||||
printf("gid %u\n", st.st_gid);
|
||||
printf("rdev %u\n", (unsigned int) st.st_rdev); // TODO: __dev_t
|
||||
printf("size %u\n", (unsigned int) st.st_size); // TODO: off_t here
|
||||
printf("atime %u\n", (unsigned int) st.st_atime);
|
||||
printf("mtime %u\n", (unsigned int) st.st_mtime);
|
||||
printf("ctime %u\n", (unsigned int) st.st_ctime);
|
||||
printf("blksize %u\n", (unsigned int) st.st_blksize); // TODO: __blksize_t
|
||||
printf("blocks %u\n", (unsigned int) st.st_blocks); // TODO: __blkcnt_t
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(sync) {
|
||||
sync();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(touch) {
|
||||
int fd;
|
||||
if (cmd->argc != 2) {
|
||||
printf("usage: touch <filename>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((fd = open(cmd->args[1], O_WRONLY | O_CREAT, 0755)) < 0) {
|
||||
perror(cmd->args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(chown) {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
const char *p;
|
||||
|
||||
if (cmd->argc != 3) {
|
||||
printf("usage: chown <uid>:<gid> <filename>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(p = strchr(cmd->args[1], ':'))) {
|
||||
printf("Invalid UID:GID pair: %s\n", cmd->args[1]);
|
||||
return -1;
|
||||
}
|
||||
uid = atoi(cmd->args[1]);
|
||||
gid = atoi(p + 1);
|
||||
|
||||
if (chown(cmd->args[2], uid, gid)) {
|
||||
perror(cmd->args[2]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(exec) {
|
||||
//struct cmd_exec new_cmd;
|
||||
if (cmd->argc < 2) {
|
||||
printf("usage: exec <command> ...\n");
|
||||
}
|
||||
|
||||
char path_path[256];
|
||||
int res;
|
||||
|
||||
if (cmd->args[1][0] == '.' || cmd->args[1][0] == '/') {
|
||||
if ((res = access(cmd->args[1], X_OK)) != 0) {
|
||||
perror(cmd->args[1]);
|
||||
return res;
|
||||
}
|
||||
|
||||
strcpy(path_path, cmd->args[1]);
|
||||
exit(execve(path_path, (char *const *) &cmd->args[1], NULL));
|
||||
}
|
||||
|
||||
snprintf(path_path, sizeof(path_path), "%s/%s", PATH, cmd->args[1]);
|
||||
if ((res = access(path_path, X_OK)) == 0) {
|
||||
exit(execve(path_path, (char *const *) &cmd->args[1], NULL));
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(clear) {
|
||||
printf("\033[2J\033[1;1f");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(echo) {
|
||||
for (int i = 1; i < cmd->argc; ++i) {
|
||||
printf("%s ", cmd->args[i]);
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(set) {
|
||||
if (cmd->argc != 3) {
|
||||
fprintf(stderr, "Usage: set <key> <value>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
setenv(cmd->args[1], cmd->args[2], 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: support usernames (getpwnam_r)
|
||||
DEF_BUILTIN(setid) {
|
||||
if (cmd->argc == 2) {
|
||||
// Assume gid == uid
|
||||
if (setuid(atoi(cmd->args[1])) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (setgid(atoi(cmd->args[1])) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (cmd->argc != 3) {
|
||||
printf("usage: setid <uid> <gid>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setuid(atoi(cmd->args[1])) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (setgid(atoi(cmd->args[2])) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(builtins) {
|
||||
for (size_t i = 0; __builtins[i].name; ++i) {
|
||||
printf("%s ", __builtins[i].name);
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
static struct sh_builtin __builtins[] = {
|
||||
DECL_BUILTIN(builtins),
|
||||
DECL_BUILTIN(cat),
|
||||
DECL_BUILTIN(cd),
|
||||
DECL_BUILTIN(chmod),
|
||||
DECL_BUILTIN(chown),
|
||||
DECL_BUILTIN(clear),
|
||||
DECL_BUILTIN(echo),
|
||||
DECL_BUILTIN(env),
|
||||
DECL_BUILTIN(exec),
|
||||
// DECL_BUILTIN(exit),
|
||||
DECL_BUILTIN(set),
|
||||
DECL_BUILTIN(setid),
|
||||
DECL_BUILTIN(stat),
|
||||
DECL_BUILTIN(sync),
|
||||
DECL_BUILTIN(touch),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
builtin_func_t builtin_find(const char *name) {
|
||||
for (size_t i = 0; i < sizeof(__builtins) / sizeof(__builtins[0]); ++i) {
|
||||
if (!__builtins[i].name) {
|
||||
return NULL;
|
||||
}
|
||||
if (!strcmp(__builtins[i].name, name)) {
|
||||
return __builtins[i].exec;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
#include <sys/termios.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "builtin.h"
|
||||
#include "config.h"
|
||||
#include "parse.h"
|
||||
#include "cmd.h"
|
||||
|
||||
typedef int (*spawn_func_t) (void *arg, struct cmd_unit *cmd);
|
||||
extern char **environ;
|
||||
|
||||
static int spawn_binary(void *arg, struct cmd_unit *cmd) {
|
||||
return execve(arg, (char *const *) cmd->args, environ);
|
||||
}
|
||||
|
||||
static int spawn_builtin(void *arg, struct cmd_unit *cmd) {
|
||||
builtin_func_t func = arg;
|
||||
return func(cmd);
|
||||
}
|
||||
|
||||
static int cmd_spawn(spawn_func_t fn, void *arg, struct cmd_unit *cmd, int *pgid) {
|
||||
int pid;
|
||||
int pipe_fds[2];
|
||||
|
||||
if (cmd->next) {
|
||||
// Create pipe pair for this -> next communication
|
||||
int res = pipe(pipe_fds);
|
||||
if (res != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// this stdout = write end
|
||||
cmd->fds[1] = pipe_fds[1];
|
||||
// next stdin = read end
|
||||
cmd->next->fds[0] = pipe_fds[0];
|
||||
}
|
||||
|
||||
if ((pid = fork()) < 0) {
|
||||
perror("fork()");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
pid = getpid();
|
||||
if (*pgid == -1) {
|
||||
*pgid = pid;
|
||||
}
|
||||
setpgid(pid, *pgid);
|
||||
|
||||
if (cmd->prev) {
|
||||
dup2(cmd->fds[0], STDIN_FILENO);
|
||||
close(cmd->fds[0]);
|
||||
}
|
||||
if (cmd->next) {
|
||||
dup2(cmd->fds[1], STDOUT_FILENO);
|
||||
close(cmd->fds[1]);
|
||||
}
|
||||
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
signal(SIGTTOU, SIG_IGN);
|
||||
tcsetpgrp(STDIN_FILENO, *pgid);
|
||||
}
|
||||
|
||||
exit(fn(arg, cmd));
|
||||
} else {
|
||||
cmd->pid = pid;
|
||||
if (cmd->next) {
|
||||
// Close write end, it's now handled by child
|
||||
close(pipe_fds[1]);
|
||||
}
|
||||
if (cmd->prev) {
|
||||
// Close read end, it's now handled by child
|
||||
close(cmd->fds[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_exec_binary(struct cmd_unit *cmd, int *pgid) {
|
||||
char path_path[256];
|
||||
int res;
|
||||
|
||||
if (cmd->args[0][0] == '.' || cmd->args[0][0] == '/') {
|
||||
if ((res = access(cmd->args[0], X_OK)) != 0) {
|
||||
perror(cmd->args[0]);
|
||||
return res;
|
||||
}
|
||||
|
||||
strcpy(path_path, cmd->args[0]);
|
||||
return cmd_spawn(spawn_binary, path_path, cmd, pgid);
|
||||
}
|
||||
|
||||
const char *pathvar = getenv("PATH");
|
||||
if (!pathvar || !*pathvar) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *p = pathvar;
|
||||
const char *e;
|
||||
while (1) {
|
||||
e = strchr(p, ':');
|
||||
size_t len;
|
||||
if (!e) {
|
||||
len = strlen(p);
|
||||
} else {
|
||||
len = e - p;
|
||||
}
|
||||
|
||||
strncpy(path_path, p, len);
|
||||
path_path[len] = '/';
|
||||
strcpy(path_path + len + 1, cmd->args[0]);
|
||||
|
||||
if ((res = access(path_path, X_OK)) == 0) {
|
||||
return cmd_spawn(spawn_binary, path_path, cmd, pgid);
|
||||
}
|
||||
|
||||
if (!e) {
|
||||
break;
|
||||
}
|
||||
p = e + 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cmd_unit_exec(struct cmd_unit *cmd, int *pgid) {
|
||||
int res;
|
||||
builtin_func_t builtin;
|
||||
|
||||
// TODO: don't spawn ALL the commands as separate processes
|
||||
// e.g. exit is broken and $$ evaluation will be incorrect
|
||||
// this is just a dirty workaround for "exit"
|
||||
if (!strcmp(cmd->args[0], "exit")) {
|
||||
if (cmd->argc > 1) {
|
||||
exit(atoi(cmd->args[1]));
|
||||
}
|
||||
exit(0);
|
||||
} else {
|
||||
if ((builtin = builtin_find(cmd->args[0])) != NULL) {
|
||||
return cmd_spawn(spawn_builtin, builtin, cmd, pgid);
|
||||
}
|
||||
}
|
||||
|
||||
if ((res = cmd_exec_binary(cmd, pgid)) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("sh: command not found: %s\n", cmd->args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int eval(char *str) {
|
||||
char *p;
|
||||
struct cmd cmd;
|
||||
int cmd_res, pgid;
|
||||
|
||||
while (isspace(*str)) {
|
||||
++str;
|
||||
}
|
||||
|
||||
if ((p = strchr(str, '#'))) {
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
if (!*str) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd_parse(str, &cmd) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Spawn all subprocesses
|
||||
pgid = -1;
|
||||
for (struct cmd_unit *u = cmd.first; u; u = u->next) {
|
||||
u->pid = -1;
|
||||
cmd_unit_exec(u, &pgid);
|
||||
}
|
||||
|
||||
// Wait for spawned subprocesses to finish
|
||||
for (struct cmd_unit *u = cmd.first; u; u = u->next) {
|
||||
if (u->pid != -1) {
|
||||
if (waitpid(u->pid, &cmd_res, 0) < 0) {
|
||||
perror("waitpid()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regain control of foreground group
|
||||
pgid = getpgid(0);
|
||||
signal(SIGTTOU, SIG_IGN);
|
||||
tcsetpgrp(STDIN_FILENO, pgid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
#include "parse.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static int cmd_unit_parse(const char *str, struct cmd_unit *unit) {
|
||||
// Parse until end of string or |
|
||||
size_t len;
|
||||
char *word = NULL;
|
||||
char c;
|
||||
unit->argc = 0;
|
||||
|
||||
// TODO: escapes
|
||||
// TODO: variable substitution
|
||||
while ((c = *str++) && c != '|') {
|
||||
if (isspace(c)) {
|
||||
if (word) {
|
||||
word[len] = 0;
|
||||
unit->args[unit->argc++] = word;
|
||||
word = NULL;
|
||||
}
|
||||
} else {
|
||||
if (!word) {
|
||||
word = malloc(256);
|
||||
len = 0;
|
||||
}
|
||||
|
||||
if (len == 255) {
|
||||
return -1;
|
||||
}
|
||||
word[len++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (word) {
|
||||
word[len] = 0;
|
||||
unit->args[unit->argc++] = word;
|
||||
}
|
||||
|
||||
unit->args[unit->argc] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_parse(const char *str, struct cmd *cmd) {
|
||||
const char *p = str;
|
||||
const char *e;
|
||||
|
||||
cmd->last = NULL;
|
||||
|
||||
while (1) {
|
||||
while (isspace(*p)) ++p;
|
||||
if (!*p) {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: escaped/quoted |
|
||||
// and ||
|
||||
e = strchr(p, '|');
|
||||
|
||||
if (p != e) {
|
||||
struct cmd_unit *unit = malloc(sizeof(struct cmd_unit));
|
||||
if (cmd_unit_parse(p, unit) != 0) {
|
||||
// TODO: cleanup
|
||||
return -1;
|
||||
}
|
||||
|
||||
unit->next = NULL;
|
||||
if (cmd->last) {
|
||||
cmd->last->next = unit;
|
||||
} else {
|
||||
cmd->first = unit;
|
||||
}
|
||||
unit->prev = cmd->last;
|
||||
cmd->last = unit;
|
||||
}
|
||||
|
||||
if (!e) {
|
||||
break;
|
||||
}
|
||||
p = e + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
//#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//#define KEY_UP (256)
|
||||
//#define KEY_DOWN (257)
|
||||
//#define KEY_RIGHT (258)
|
||||
//#define KEY_LEFT (259)
|
||||
//
|
||||
//// With support for escape sequences
|
||||
//static int getch_del(void) {
|
||||
// struct timeval tv = {
|
||||
// .tv_sec = 0,
|
||||
// .tv_usec = 500000
|
||||
// };
|
||||
// fd_set fds;
|
||||
// FD_ZERO(&fds);
|
||||
// FD_SET(STDIN_FILENO, &fds);
|
||||
// int res;
|
||||
//
|
||||
// if ((res = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv)) < 0) {
|
||||
// return res;
|
||||
// }
|
||||
//
|
||||
// if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||
// res = 0;
|
||||
// if (read(STDIN_FILENO, &res, 1) != 1) {
|
||||
// return -1;
|
||||
// }
|
||||
// return res;
|
||||
// } else {
|
||||
// return -1;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//static int escape_read(void) {
|
||||
// // Maybe [
|
||||
// int c = getch_del();
|
||||
//
|
||||
// if (c < 0) {
|
||||
// return '\033';
|
||||
// }
|
||||
//
|
||||
// if (c != '[') {
|
||||
// return -1;
|
||||
// }
|
||||
//
|
||||
// c = getch_del();
|
||||
//
|
||||
// switch (c) {
|
||||
// case 'A':
|
||||
// return KEY_UP;
|
||||
// case 'B':
|
||||
// return KEY_DOWN;
|
||||
// case 'C':
|
||||
// return KEY_RIGHT;
|
||||
// case 'D':
|
||||
// return KEY_LEFT;
|
||||
// default:
|
||||
// return -1;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//static int getch(void) {
|
||||
// char c;
|
||||
//
|
||||
// if (read(STDIN_FILENO, &c, 1) != 1) {
|
||||
// return -1;
|
||||
// }
|
||||
//
|
||||
// if (c == '\033') {
|
||||
// int r = escape_read();
|
||||
//
|
||||
// if (r > 0) {
|
||||
// return r;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return c;
|
||||
//}
|
||||
|
||||
int readline(char *buf, size_t lim) {
|
||||
// TODO: rewrite this for new kernel line discipline to disable ECHO/ICANON
|
||||
ssize_t len = read(STDIN_FILENO, buf, lim);
|
||||
|
||||
// Just strip newline
|
||||
if (len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf[len - 1] == '\n') {
|
||||
buf[len - 1] = 0;
|
||||
}
|
||||
|
||||
//printf("Got len = %ld\n", len);
|
||||
//while (1);
|
||||
|
||||
//int len = 0;
|
||||
//int cur = 0;
|
||||
//int chr;
|
||||
|
||||
//while (1) {
|
||||
// if ((chr = getch()) <= 0) {
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
// if (len == lim) {
|
||||
// printf("Input line is too long\n");
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
// switch (chr) {
|
||||
// case KEY_LEFT:
|
||||
// if (cur) {
|
||||
// puts2("\033[D");
|
||||
// --cur;
|
||||
// }
|
||||
// break;
|
||||
// case KEY_RIGHT:
|
||||
// if (cur < len) {
|
||||
// puts2("\033[C");
|
||||
// ++cur;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if (chr == '\n') {
|
||||
// putchar(chr);
|
||||
// buf[len] = 0;
|
||||
// break;
|
||||
// } else if (chr == '\b') {
|
||||
// if (cur) {
|
||||
// --cur;
|
||||
// for (int i = cur; i < len - 1; ++i) {
|
||||
// buf[i] = buf[i + 1];
|
||||
// }
|
||||
// puts2("\033[D\033[s");
|
||||
// --len;
|
||||
// for (int i = cur; i < len; ++i) {
|
||||
// putchar(buf[i]);
|
||||
// }
|
||||
// puts2(" \033[u");
|
||||
// }
|
||||
// } else if (chr >= ' ' && chr < 255) {
|
||||
// putchar(chr);
|
||||
// puts2("\033[s");
|
||||
// for (int i = cur; i < len; ++i) {
|
||||
// putchar(buf[i]);
|
||||
// }
|
||||
// puts2("\033[u");
|
||||
// for (int i = len; i > cur; --i) {
|
||||
// buf[i] = buf[i - 1];
|
||||
// }
|
||||
// buf[cur++] = chr;
|
||||
// ++len;
|
||||
// }
|
||||
//}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "readline.h"
|
||||
#include "config.h"
|
||||
#include "cmd.h"
|
||||
|
||||
// Data needed for prompt
|
||||
static char p_hostname[64];
|
||||
static char p_username[64];
|
||||
static char p_cwd[256];
|
||||
static uid_t p_uid;
|
||||
//static gid_t p_gid;
|
||||
|
||||
static void update_prompt(void) {
|
||||
struct utsname _u;
|
||||
struct passwd _p;
|
||||
struct passwd *p;
|
||||
char pwbuf[256];
|
||||
|
||||
p_uid = getuid();
|
||||
|
||||
if (getcwd(p_cwd, sizeof(p_cwd)) == NULL) {
|
||||
perror("getcwd()");
|
||||
p_cwd[0] = 0;
|
||||
}
|
||||
|
||||
if (uname(&_u) != 0) {
|
||||
perror("uname()");
|
||||
} else {
|
||||
strcpy(p_hostname, _u.nodename);
|
||||
}
|
||||
if (getpwuid_r(p_uid, &_p, pwbuf, sizeof(pwbuf), &p) != 0) {
|
||||
perror("getpwuid_r()");
|
||||
p_username[0] = 0;
|
||||
} else {
|
||||
strcpy(p_username, p->pw_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void display_prompt(void) {
|
||||
const char *p = PS1;
|
||||
|
||||
while (*p) {
|
||||
if (*p == '%') {
|
||||
switch (*++p) {
|
||||
case '#':
|
||||
putchar(p_uid == 0 ? '#' : '$');
|
||||
break;
|
||||
case 'h':
|
||||
fputs(p_hostname, stdout);
|
||||
break;
|
||||
case 'u':
|
||||
fputs(p_username, stdout);
|
||||
break;
|
||||
case 'd':
|
||||
fputs(p_cwd, stdout);
|
||||
break;
|
||||
default:
|
||||
putchar('%');
|
||||
putchar(*p);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
putchar(*p);
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void signal_handle(int signum) {
|
||||
switch (signum) {
|
||||
case SIGINT:
|
||||
printf("\n");
|
||||
update_prompt();
|
||||
display_prompt();
|
||||
break;
|
||||
default:
|
||||
printf("\nUnhandled signal: %d\n", signum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int fd = STDIN_FILENO;
|
||||
char linebuf[256];
|
||||
int res;
|
||||
|
||||
signal(SIGINT, signal_handle);
|
||||
|
||||
if (argc == 2) {
|
||||
fd = open(argv[1], O_RDONLY, 0);
|
||||
|
||||
if (fd < 0) {
|
||||
perror(argv[1]);
|
||||
return -1;
|
||||
}
|
||||
} else if (argc > 2) {
|
||||
printf("usage: sh [filename]\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO: source /etc/profile?
|
||||
setenv("PATH", "/sbin:/bin", 0);
|
||||
|
||||
if (!isatty(fd)) {
|
||||
while (1) {
|
||||
// TODO: use fgets here
|
||||
//if (gets_safe(fd, linebuf, sizeof(linebuf)) < 0) {
|
||||
// break;
|
||||
//}
|
||||
|
||||
eval(linebuf);
|
||||
}
|
||||
|
||||
if (fd != STDIN_FILENO) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
update_prompt();
|
||||
display_prompt();
|
||||
|
||||
if (readline(linebuf, sizeof(linebuf)) < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((res = eval(linebuf)) != 0) {
|
||||
printf(COLOR_RED "Status: %d" COLOR_RESET "\n", res);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user