Rewrite sh for PoC pipe support, add simple grep
"PoC" is rather for "Piece of Crap" here
This commit is contained in:
parent
0feedadfd3
commit
4c2741e015
21
Makefile
21
Makefile
@ -29,8 +29,13 @@ STAGE_BIN=$(STAGE)/init \
|
||||
$(STAGE)/bin/wr \
|
||||
$(STAGE)/sbin/insmod \
|
||||
$(STAGE)/sbin/reboot \
|
||||
$(STAGE)/sbin/lspci \
|
||||
$(STAGE)/bin/vsh \
|
||||
$(STAGE)/bin/grep \
|
||||
$(STAGE)/test.ko
|
||||
|
||||
# $(STAGE)/bin/t0 \
|
||||
|
||||
# TODO
|
||||
# newlib: gettimeofday is broken?
|
||||
|
||||
@ -44,9 +49,12 @@ STAGE_BIN=$(STAGE)/init \
|
||||
|
||||
sh_OBJS=$(O)/sh/sh.o \
|
||||
$(O)/sh/readline.o \
|
||||
$(O)/sh/builtin.o \
|
||||
$(O)/sh/cmd.o
|
||||
$(O)/sh/cmd.o \
|
||||
$(O)/sh/parse.o \
|
||||
$(O)/sh/builtin.o
|
||||
|
||||
ase_OBJS=$(O)/ase/ase.o
|
||||
|
||||
vsh_OBJS=$(O)/vsh/vsh.o \
|
||||
$(O)/vsh/input.o \
|
||||
$(O)/vsh/video.o \
|
||||
@ -90,6 +98,15 @@ $(STAGE)/init: init.c
|
||||
@printf " CC\t%s\n" $(@:$(STAGE)/%=/%)
|
||||
$(CC) -o $@ $(usr_CFLAGS) $(usr_LDFLAGS) init.c
|
||||
|
||||
$(STAGE)/bin/t0: t0.c
|
||||
$(CC) -I../ports/SDL2-2.0.12/include \
|
||||
-o $@ \
|
||||
$(usr_CFLAGS) \
|
||||
$(usr_LDFLAGS) \
|
||||
$< \
|
||||
../ports/SDL-build/build/.libs/libSDL2.a \
|
||||
-lm
|
||||
|
||||
$(STAGE)/bin/%: core/bin/%.c
|
||||
$(CC) -o $@ $(usr_CFLAGS) $(usr_LDFLAGS) $<
|
||||
|
||||
|
158
core/bin/grep.c
Normal file
158
core/bin/grep.c
Normal file
@ -0,0 +1,158 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define PAT_ANY (-1)
|
||||
#define PAT_ALPHA (-2)
|
||||
#define PAT_DIGIT (-3)
|
||||
|
||||
static int char_cmp(char c, int p) {
|
||||
switch (p) {
|
||||
case PAT_ANY:
|
||||
return 1;
|
||||
case PAT_ALPHA:
|
||||
return isalpha(c);
|
||||
case PAT_DIGIT:
|
||||
return isdigit(c);
|
||||
default:
|
||||
return c == (char) p;
|
||||
}
|
||||
}
|
||||
|
||||
static int grep_match(const char *line, const char *pattern) {
|
||||
// TODO: proper implementation
|
||||
const char *p, *t;
|
||||
char pc;
|
||||
#define MATCH_NONE 0
|
||||
#define MATCH_ONE 1
|
||||
#define MATCH_ONE_MORE 2
|
||||
#define MATCH_ZERO_MORE 3
|
||||
#define MATCH_ONE_OPT 4
|
||||
int match_type = MATCH_NONE;
|
||||
int match_char;
|
||||
|
||||
// Line: abcdef abcdef
|
||||
// p: abcdef abcdef
|
||||
// bcdef abcdef
|
||||
// ...
|
||||
for (p = line; *p; ++p) {
|
||||
const char *c = p;
|
||||
t = pattern;
|
||||
int fail = 0;
|
||||
|
||||
while (!fail && *c && *t) {
|
||||
pc = *t;
|
||||
|
||||
// Character match
|
||||
// TODO: groups
|
||||
// TODO: ranges
|
||||
if (pc != '(') {
|
||||
++t;
|
||||
|
||||
if (isalnum(pc) || pc == '_') {
|
||||
match_char = pc;
|
||||
} else {
|
||||
if (pc == '.') {
|
||||
match_char = PAT_ANY;
|
||||
} else if (pc == '\\') {
|
||||
pc = *t++;
|
||||
|
||||
switch (pc) {
|
||||
case 'd':
|
||||
match_char = PAT_DIGIT;
|
||||
break;
|
||||
case 'a':
|
||||
match_char = PAT_ALPHA;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid pattern char: \\%c\n", pc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*t == '+') {
|
||||
// Match 1 or more
|
||||
match_type = MATCH_ONE_MORE;
|
||||
++t;
|
||||
} else if (*t == '*') {
|
||||
// Match 0 or more
|
||||
match_type = MATCH_ZERO_MORE;
|
||||
++t;
|
||||
} else if (*t == '?') {
|
||||
// Match 0 or 1
|
||||
match_type = MATCH_ONE_OPT;
|
||||
++t;
|
||||
} else {
|
||||
// Match single char
|
||||
match_type = MATCH_ONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (match_type == MATCH_NONE) {
|
||||
fprintf(stderr, "Couldn't parse pattern string\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (match_type) {
|
||||
case MATCH_ONE:
|
||||
if (!char_cmp(*c, match_char)) {
|
||||
fail = 1;
|
||||
}
|
||||
++c;
|
||||
break;
|
||||
case MATCH_ONE_MORE:
|
||||
if (!char_cmp(*c, match_char)) {
|
||||
fail = 1;
|
||||
break;
|
||||
}
|
||||
while (char_cmp(*c, match_char)) {
|
||||
++c;
|
||||
}
|
||||
break;
|
||||
case MATCH_ZERO_MORE:
|
||||
while (char_cmp(*c, match_char)) {
|
||||
++c;
|
||||
}
|
||||
break;
|
||||
case MATCH_ONE_OPT:
|
||||
if (char_cmp(*c, match_char)) {
|
||||
++c;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unhandled pattern\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (*t && !*c) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fail) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char buf[1024];
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: %s PATTERN [FILE]", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *pattern = argv[1];
|
||||
|
||||
while (fgets(buf, sizeof(buf), stdin) != NULL) {
|
||||
if (grep_match(buf, pattern) != 0) {
|
||||
fwrite(buf, 1, strlen(buf), stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
32
sh/builtin.c
32
sh/builtin.c
@ -6,16 +6,17 @@
|
||||
#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_exec *cmd)
|
||||
static int __bcmd_##b_name(const struct cmd_unit *cmd)
|
||||
#define DECL_BUILTIN(b_name) \
|
||||
{ .name = #b_name, .exec = __bcmd_##b_name }
|
||||
|
||||
struct sh_builtin {
|
||||
const char *name;
|
||||
int (*exec) (const struct cmd_exec *cmd);
|
||||
int (*exec) (const struct cmd_unit *cmd);
|
||||
};
|
||||
|
||||
static struct sh_builtin __builtins[];
|
||||
@ -30,12 +31,13 @@ DEF_BUILTIN(env) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEF_BUILTIN(exit) {
|
||||
if (cmd->argc > 1) {
|
||||
exit(atoi(cmd->args[1]));
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
// XXX: Broken
|
||||
//DEF_BUILTIN(exit) {
|
||||
// if (cmd->argc > 1) {
|
||||
// exit(atoi(cmd->args[1]));
|
||||
// }
|
||||
// exit(0);
|
||||
//}
|
||||
|
||||
DEF_BUILTIN(cd) {
|
||||
if (cmd->argc != 2) {
|
||||
@ -271,7 +273,7 @@ static struct sh_builtin __builtins[] = {
|
||||
DECL_BUILTIN(echo),
|
||||
DECL_BUILTIN(env),
|
||||
DECL_BUILTIN(exec),
|
||||
DECL_BUILTIN(exit),
|
||||
// DECL_BUILTIN(exit),
|
||||
DECL_BUILTIN(set),
|
||||
DECL_BUILTIN(setid),
|
||||
DECL_BUILTIN(stat),
|
||||
@ -280,16 +282,14 @@ static struct sh_builtin __builtins[] = {
|
||||
{NULL}
|
||||
};
|
||||
|
||||
int builtin_exec(const struct cmd_exec *cmd, int *cmd_res) {
|
||||
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 -1;
|
||||
return NULL;
|
||||
}
|
||||
if (!strcmp(__builtins[i].name, cmd->args[0])) {
|
||||
*cmd_res = __builtins[i].exec(cmd);
|
||||
return 0;
|
||||
if (!strcmp(__builtins[i].name, name)) {
|
||||
return __builtins[i].exec;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
struct cmd_exec;
|
||||
struct cmd_unit;
|
||||
|
||||
int builtin_exec(const struct cmd_exec *cmd, int *res);
|
||||
typedef int (*builtin_func_t) (const struct cmd_unit *);
|
||||
|
||||
builtin_func_t builtin_find(const char *name);
|
||||
|
156
sh/cmd.c
156
sh/cmd.c
@ -1,6 +1,5 @@
|
||||
#include <sys/termios.h>
|
||||
// TODO
|
||||
//#include <sys/ioctl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@ -11,53 +10,36 @@
|
||||
|
||||
#include "builtin.h"
|
||||
#include "config.h"
|
||||
#include "parse.h"
|
||||
#include "cmd.h"
|
||||
|
||||
int ioctl(int fd, unsigned long req, ...);
|
||||
typedef int (*spawn_func_t) (void *arg, struct cmd_unit *cmd);
|
||||
|
||||
static int make_cmd(char *input, struct cmd_exec *ex) {
|
||||
char *p = strchr(input, ' ');
|
||||
char *e;
|
||||
|
||||
if (!p) {
|
||||
ex->args[0] = input;
|
||||
ex->args[1] = NULL;
|
||||
ex->argc = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*p++ = 0;
|
||||
ex->args[0] = input;
|
||||
ex->argc = 1;
|
||||
|
||||
while (1) {
|
||||
while (isspace(*p)) {
|
||||
++p;
|
||||
}
|
||||
if (!*p) {
|
||||
break;
|
||||
}
|
||||
|
||||
e = strchr(p, ' ');
|
||||
|
||||
if (!e) {
|
||||
ex->args[ex->argc++] = p;
|
||||
break;
|
||||
} else {
|
||||
*e++ = 0;
|
||||
ex->args[ex->argc++] = p;
|
||||
p = e;
|
||||
}
|
||||
}
|
||||
ex->args[ex->argc] = NULL;
|
||||
|
||||
return 0;
|
||||
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(const char *path, const struct cmd_exec *cmd, int *cmd_res) {
|
||||
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()");
|
||||
@ -66,23 +48,38 @@ static int cmd_spawn(const char *path, const struct cmd_exec *cmd, int *cmd_res)
|
||||
|
||||
if (pid == 0) {
|
||||
pid = getpid();
|
||||
ioctl(STDIN_FILENO, TIOCSPGRP, &pid);
|
||||
setpgid(0, 0);
|
||||
exit(execve(path, (char *const *) cmd->args, environ));
|
||||
} else {
|
||||
if (waitpid(pid, cmd_res, 0) != 0) {
|
||||
perror("waitpid()");
|
||||
if (*pgid == -1) {
|
||||
*pgid = pid;
|
||||
}
|
||||
setpgid(0, *pgid);
|
||||
|
||||
if (cmd->prev) {
|
||||
dup2(STDIN_FILENO, cmd->fds[0]);
|
||||
}
|
||||
if (cmd->next) {
|
||||
dup2(STDOUT_FILENO, cmd->fds[1]);
|
||||
}
|
||||
|
||||
// Regain control of foreground group
|
||||
pid = getpgid(0);
|
||||
ioctl(STDIN_FILENO, TIOCSPGRP, &pid);
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
ioctl(STDIN_FILENO, TIOCSPGRP, &pid);
|
||||
}
|
||||
|
||||
exit(fn(arg, cmd));
|
||||
} else {
|
||||
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]);
|
||||
}
|
||||
cmd->pid = pid;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_exec_binary(const struct cmd_exec *cmd, int *cmd_res) {
|
||||
static int cmd_exec_binary(struct cmd_unit *cmd, int *pgid) {
|
||||
char path_path[256];
|
||||
int res;
|
||||
|
||||
@ -93,7 +90,7 @@ static int cmd_exec_binary(const struct cmd_exec *cmd, int *cmd_res) {
|
||||
}
|
||||
|
||||
strcpy(path_path, cmd->args[0]);
|
||||
return cmd_spawn(path_path, cmd, cmd_res);
|
||||
return cmd_spawn(spawn_binary, path_path, cmd, pgid);
|
||||
}
|
||||
|
||||
const char *pathvar = getenv("PATH");
|
||||
@ -117,7 +114,7 @@ static int cmd_exec_binary(const struct cmd_exec *cmd, int *cmd_res) {
|
||||
strcpy(path_path + len + 1, cmd->args[0]);
|
||||
|
||||
if ((res = access(path_path, X_OK)) == 0) {
|
||||
return cmd_spawn(path_path, cmd, cmd_res);
|
||||
return cmd_spawn(spawn_binary, path_path, cmd, pgid);
|
||||
}
|
||||
|
||||
if (!e) {
|
||||
@ -129,15 +126,26 @@ static int cmd_exec_binary(const struct cmd_exec *cmd, int *cmd_res) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cmd_exec(const struct cmd_exec *cmd) {
|
||||
int res, cmd_res;
|
||||
static int cmd_unit_exec(struct cmd_unit *cmd, int *pgid) {
|
||||
int res;
|
||||
builtin_func_t builtin;
|
||||
|
||||
if ((res = builtin_exec(cmd, &cmd_res)) == 0) {
|
||||
return cmd_res;
|
||||
// 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, &cmd_res)) == 0) {
|
||||
return cmd_res;
|
||||
if ((res = cmd_exec_binary(cmd, pgid)) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("sh: command not found: %s\n", cmd->args[0]);
|
||||
@ -145,8 +153,9 @@ static int cmd_exec(const struct cmd_exec *cmd) {
|
||||
}
|
||||
|
||||
int eval(char *str) {
|
||||
struct cmd_exec cmd;
|
||||
char *p;
|
||||
struct cmd cmd;
|
||||
int cmd_res, pgid;
|
||||
|
||||
while (isspace(*str)) {
|
||||
++str;
|
||||
@ -160,9 +169,28 @@ int eval(char *str) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (make_cmd(str, &cmd) != 0) {
|
||||
abort();
|
||||
if (cmd_parse(str, &cmd) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return cmd_exec(&cmd);
|
||||
// Spawn all subprocesses
|
||||
pgid = -1;
|
||||
for (struct cmd_unit *u = cmd.first; u; u = u->next) {
|
||||
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);
|
||||
ioctl(STDIN_FILENO, TIOCSPGRP, &pgid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
14
sh/cmd.h
14
sh/cmd.h
@ -1,8 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
struct cmd_exec {
|
||||
size_t argc;
|
||||
char *args[12];
|
||||
};
|
||||
//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);
|
||||
|
83
sh/parse.c
Normal file
83
sh/parse.c
Normal file
@ -0,0 +1,83 @@
|
||||
#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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
15
sh/parse.h
Normal file
15
sh/parse.h
Normal file
@ -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);
|
Loading…
x
Reference in New Issue
Block a user