Rewrite sh for PoC pipe support, add simple grep

"PoC" is rather for "Piece of Crap" here
This commit is contained in:
Mark 2020-07-02 12:49:04 +03:00
parent 0feedadfd3
commit 4c2741e015
8 changed files with 397 additions and 88 deletions

View File

@ -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
View 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;
}

View File

@ -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;
}

View File

@ -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
View File

@ -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;
}

View File

@ -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
View 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
View 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);