summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkaashoek <kaashoek>2006-08-23 01:09:24 +0000
committerkaashoek <kaashoek>2006-08-23 01:09:24 +0000
commit8b58e81077abf4e843873f16c03077e2fafce52d (patch)
tree9613a801fc9b3421ee79725782e3ef9bb4650574
parentf18ab5c04e5380e0fb27f63e8335e5d621315c1d (diff)
downloadxv6-labs-8b58e81077abf4e843873f16c03077e2fafce52d.tar.gz
xv6-labs-8b58e81077abf4e843873f16c03077e2fafce52d.tar.bz2
xv6-labs-8b58e81077abf4e843873f16c03077e2fafce52d.zip
i/o redirection in sh
better parsing of sh commands (copied from jos sh) cat: read from 1 if no args sbrk system call, but untested getpid system call moved locks in keyboard intr, but why do we get intr w. null characters from keyboard?
-rw-r--r--Notes8
-rw-r--r--cat.c35
-rw-r--r--console.c17
-rw-r--r--defs.h1
-rw-r--r--echo.c2
-rw-r--r--lapic.c2
-rw-r--r--ls.c57
-rw-r--r--proc.c18
-rw-r--r--sh.c300
-rw-r--r--syscall.c29
-rw-r--r--syscall.h2
-rw-r--r--ulib.c17
-rw-r--r--user.h3
-rw-r--r--userfs.c4
-rw-r--r--usys.S2
15 files changed, 428 insertions, 69 deletions
diff --git a/Notes b/Notes
index dd81f1b..4c9855d 100644
--- a/Notes
+++ b/Notes
@@ -131,3 +131,11 @@ maybe get rid of per-proc gdt and ts
one per cpu
refresh it when needed
setupsegs(proc *)
+
+why do we get 0 characters from keyboard?
+are the locks in the right place in keyboardintr?
+
+sh: support pipes? leave it for the class?
+sh: dynamic memory allocation?
+sh: should sh support ; () & --- need malloc
+sh: stop stdin on ctrl-d (for cat > y) \ No newline at end of file
diff --git a/cat.c b/cat.c
index 8154ae2..631bd09 100644
--- a/cat.c
+++ b/cat.c
@@ -1,17 +1,32 @@
+#include "types.h"
+#include "stat.h"
#include "user.h"
char buf[513];
-int
-main(int argc, char *argv[])
+void
+rfile(int fd)
{
- int fd, i, cc;
+ int cc;
- if(argc < 2){
- puts("Usage: cat files...\n");
+ while((cc = read(fd, buf, sizeof(buf) - 1)) > 0){
+ buf[cc] = '\0';
+ puts(buf);
+ }
+ if(cc < 0){
+ puts("cat: read error\n");
exit();
}
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fd, i;
+ if (argc <= 1) {
+ rfile(0);
+ } else {
for(i = 1; i < argc; i++){
fd = open(argv[i], 0);
if(fd < 0){
@@ -20,16 +35,10 @@ main(int argc, char *argv[])
puts("\n");
exit();
}
- while((cc = read(fd, buf, sizeof(buf) - 1)) > 0){
- buf[cc] = '\0';
- puts(buf);
- }
- if(cc < 0){
- puts("cat: read error\n");
- exit();
- }
+ rfile(fd);
close(fd);
}
+ }
exit();
}
diff --git a/console.c b/console.c
index 3d19a8e..d1741dd 100644
--- a/console.c
+++ b/console.c
@@ -304,26 +304,31 @@ char kbd_buf[KBD_BUF];
int kbd_r;
int kbd_w;
struct spinlock kbd_lock;
+static uint shift;
void
kbd_intr()
{
uint st, data, c;
- static uint shift;
+
+ acquire(&kbd_lock);
st = inb(KBSTATP);
if ((st & KBS_DIB) == 0){
+ release(&kbd_lock);
return;
}
data = inb(KBDATAP);
if (data == 0xE0) {
shift |= E0ESC;
+ release(&kbd_lock);
return;
} else if (data & 0x80) {
// Key released
data = (shift & E0ESC ? data : data & 0x7F);
shift &= ~(shiftcode[data] | E0ESC);
+ release(&kbd_lock);
return;
} else if (shift & E0ESC) {
// Last character was an E0 escape; or with 0x80
@@ -341,8 +346,12 @@ kbd_intr()
else if ('A' <= c && c <= 'Z')
c += 'a' - 'A';
}
-
- acquire(&kbd_lock);
+
+ // xxx hack
+ if (c == 0x0) {
+ release(&kbd_lock);
+ return;
+ }
if(((kbd_w + 1) % KBD_BUF) != kbd_r){
kbd_buf[kbd_w++] = c;
@@ -367,7 +376,7 @@ console_read(int minor, char *dst, int n)
sleep(&kbd_r, &kbd_lock);
while(n > 0 && kbd_w != kbd_r){
- *dst = kbd_buf[kbd_r];
+ *dst = (kbd_buf[kbd_r]) & 0xff;
cons_putc(*dst & 0xff);
dst++;
--n;
diff --git a/defs.h b/defs.h
index 2928428..b8b7b84 100644
--- a/defs.h
+++ b/defs.h
@@ -16,6 +16,7 @@ struct jmpbuf;
void setupsegs(struct proc *);
struct proc * copyproc(struct proc*);
struct spinlock;
+int growproc(int);
void sleep(void *, struct spinlock *);
void wakeup(void *);
void scheduler(void);
diff --git a/echo.c b/echo.c
index 5d0c5a4..7bf3a45 100644
--- a/echo.c
+++ b/echo.c
@@ -1,3 +1,5 @@
+#include "types.h"
+#include "stat.h"
#include "user.h"
int
diff --git a/lapic.c b/lapic.c
index 161d0a5..0d389f1 100644
--- a/lapic.c
+++ b/lapic.c
@@ -120,7 +120,7 @@ lapic_timerinit(void)
void
lapic_timerintr(void)
{
- cprintf("cpu%d: timer interrupt!\n", cpu());
+ // cprintf("cpu%d: timer interrupt!\n", cpu());
lapic_write (LAPIC_EOI, 0);
}
diff --git a/ls.c b/ls.c
index 3441eba..34fac2a 100644
--- a/ls.c
+++ b/ls.c
@@ -7,13 +7,24 @@ char buf[512];
struct stat st;
struct dirent dirent;
+void
+pname(char *n)
+{
+ int i;
+
+ for (i = 0; (i < DIRSIZ) && (n[i] != '\0') ; i++) {
+ printf(1, "%c", n[i]);
+ }
+ for (; i < DIRSIZ; i++)
+ printf(1, " ");
+}
+
int
main(int argc, char *argv[])
{
int fd;
uint off;
uint sz;
- int i;
if(argc > 2){
puts("Usage: ls [dir]\n");
@@ -23,7 +34,7 @@ main(int argc, char *argv[])
if (argc == 2) {
fd = open(argv[1], 0);
if(fd < 0){
- printf(2, "ls: cannot open dir %s\n", argv[1]);
+ printf(2, "ls: cannot open %s\n", argv[1]);
exit();
}
} else {
@@ -38,31 +49,31 @@ main(int argc, char *argv[])
printf(2, "ls: cannot stat dir\n");
exit();
}
- if (st.st_type != T_DIR) {
- printf(2, "ls: dir is not a directory\n");
- }
- sz = st.st_size;
- for(off = 0; off < sz; off += sizeof(struct dirent)) {
- if (read(fd, &dirent, sizeof(struct dirent)) != sizeof(struct dirent)) {
- printf(1, "ls: read error\n");
- break;
- }
- if (dirent.inum != 0) {
- // xxx prepend to name the pathname supplied to ls (e.g. .. in ls ..)
- if (stat (dirent.name, &st) < 0) {
- printf(1, "stat: failed %s\n", dirent.name);
- continue;
+
+ switch (st.st_type) {
+ case T_FILE:
+ pname(argv[1]);
+ printf(1, "%d %d %d\n", st.st_type, st.st_ino, st.st_size);
+ break;
+ case T_DIR:
+ sz = st.st_size;
+ for(off = 0; off < sz; off += sizeof(struct dirent)) {
+ if (read(fd, &dirent, sizeof(struct dirent)) != sizeof(struct dirent)) {
+ printf(1, "ls: read error\n");
+ break;
}
- for (i = 0; i < DIRSIZ; i++) {
- if (dirent.name[i] != '\0')
- printf(1, "%c", dirent.name[i]);
- else
- printf(1, " ");
+ if (dirent.inum != 0) {
+ // xxx prepend to name the pathname supplied to ls (e.g. .. in ls ..)
+ if (stat (dirent.name, &st) < 0) {
+ printf(1, "stat: failed %s\n", dirent.name);
+ continue;
+ }
+ pname(dirent.name);
+ printf(1, "%d %d %d\n", st.st_type, dirent.inum, st.st_size);
}
- printf(1, "%d %d %d\n", st.st_type, dirent.inum, st.st_size);
}
+ break;
}
close(fd);
-
exit();
}
diff --git a/proc.c b/proc.c
index 9f7064f..7382add 100644
--- a/proc.c
+++ b/proc.c
@@ -138,6 +138,24 @@ copyproc(struct proc* p)
return np;
}
+int
+growproc(int n)
+{
+ struct proc *cp = curproc[cpu()];
+ char *newmem, *oldmem;
+
+ newmem = kalloc(cp->sz + n);
+ if(newmem == 0) return -1;
+ memmove(newmem, cp->mem, cp->sz);
+ memset(newmem + cp->sz, 0, n);
+ oldmem = cp->mem;
+ cp->mem = newmem;
+ kfree(oldmem, cp->sz);
+ cp->sz += n;
+ cprintf("growproc: added %d bytes\n", n);
+ return 0;
+}
+
// Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up.
// Scheduler never returns. It loops, doing:
diff --git a/sh.c b/sh.c
index 9e0cb3f..2f2d297 100644
--- a/sh.c
+++ b/sh.c
@@ -4,52 +4,296 @@
#include "fs.h"
#include "fcntl.h"
-char *args[100];
-void parse(char buf[]);
+#define BUFSIZ 512
+#define MAXARGS 10
+#define MAXNODE 2
+
+// only allocate nodes for i/o redir; at some point we may have to build a
+// a real parse tree.
+struct node {
+ int token;
+ char *s;
+};
+struct node list[MAXNODE];
+int nextnode;
+
+char buf[BUFSIZ];
+char *argv[MAXARGS];
+char argv0buf[BUFSIZ];
+int argc;
+
+int debug = 1;
+
+int parse(char *s);
+void runcmd(void);
+int ioredirection(void);
+int gettoken(char *s, char **token);
+int _gettoken(char *s, char **p1, char **p2);
+void addnode(int token, char *s);
int
main(void)
{
- char buf[128];
- int pid;
-
while(1){
puts("$ ");
memset (buf, '\0', sizeof(buf));
gets(buf, sizeof(buf));
- if(buf[0] == '\0')
+ if (parse(buf) < 0)
continue;
- pid = fork();
- if(pid == 0){
- parse(buf);
- if (buf[0] == 'c' && buf[1] == 'd' && buf[2] == '\0') { // cd
- chdir(&buf[3]);
- } else {
- exec(buf, args);
- printf(1, "%s: not found\n", buf);
- exit();
+ runcmd();
+ }
+}
+
+int
+parse(char *s)
+{
+ char *t;
+ int c;
+
+ gettoken(s, 0);
+
+ argc = 0;
+ nextnode = 0;
+ while (1) {
+ switch ((c = gettoken(0, &t))) {
+
+ case 'w': // Add an argument
+ if (argc == MAXARGS) {
+ printf(2, "too many arguments\n");
+ return -1;
}
+ argv[argc++] = t;
+ break;
+
+ case '<': // Input redirection
+ // Grab the filename from the argument list
+ if (gettoken(0, &t) != 'w') {
+ printf(2, "syntax error: < not followed by word\n");
+ return -1;
+ }
+ addnode('<', t);
+ break;
+
+ case '>': // Output redirection
+ // Grab the filename from the argument list
+ if (gettoken(0, &t) != 'w') {
+ printf(2, "syntax error: > not followed by word\n");
+ return -1;
+ }
+ addnode('>', t);
+ break;
+
+ case 0: // String is complete
+ return 0;
+
+ default:
+ printf(2, "syntax error: bad return %d from gettoken", c);
+ return -1;
+
}
- if(pid > 0)
- wait();
}
}
+
void
-parse(char buf[])
+runcmd(void)
+{
+ int i, r, pid;
+
+ // Return immediately if command line was empty.
+ if(argc == 0) {
+ if (debug)
+ printf(2, "EMPTY COMMAND\n");
+ return;
+ }
+
+ // Clean up command line.
+ // Read all commands from the filesystem: add an initial '/' to
+ // the command name.
+ // This essentially acts like 'PATH=/'.
+ if (argv[0][0] != '/') {
+ argv0buf[0] = '/';
+ strcpy(argv0buf + 1, argv[0]);
+ argv[0] = argv0buf;
+ }
+ argv[argc] = 0;
+
+ // Print the command.
+ if (debug) {
+ printf(2, "[%d] SPAWN:", getpid());
+ for (i = 0; argv[i]; i++)
+ printf(2, " %s", argv[i]);
+ for (i = 0; i < nextnode; i++) {
+ printf(2, "%c %s", list[i].token, list[i].s);
+ }
+ printf(2, "\n");
+ }
+
+ if (strcmp(argv[0], "/cd") == 0) {
+ if (debug) printf (2, "/cd %s is build in\n", argv[1]);
+ chdir(argv[1]);
+ return;
+ }
+
+ pid = fork();
+ if (pid == 0) {
+ if (ioredirection() < 0)
+ exit();
+ if ((r = exec(argv0buf, (char**) argv)) < 0) {
+ printf(2, "exec %s: %d\n", argv[0], r);
+ exit();
+ }
+ }
+
+ if (pid > 0) {
+ if (debug)
+ printf(2, "[%d] WAIT %s\n", getpid(), argv[0]);
+ wait();
+ if (debug)
+ printf(2, "[%d] wait finished\n", getpid());
+ }
+}
+
+int
+ioredirection(void)
{
- int j = 1;
- int i;
- args[0] = buf;
- for (i = 0; buf[i] != '\0'; i++) {
- if (buf[i] == ' ') {
- buf[i] = '\0';
- args[j++] = buf + i + 1;
- if (j >= 100) {
- printf(2, "too many args\n");
+ int i, fd, dfd;
+
+ for (i = 0; i < nextnode; i++) {
+ switch (list[i].token) {
+ case '<':
+ if ((fd = open(list[i].s, O_RDONLY)) < 0) {
+ printf(2, "failed to open %s for read: %d", list[i].s, fd);
+ return -1;
+ }
+
+ if (debug)
+ printf(2, "redirect 0 from %s\n", list[i].s);
+
+ close(0);
+ if ((dfd = dup(fd)) < 0)
+ printf(2, "dup failed\n");
+ if (debug)
+ printf(2, "dup returns %d\n", dfd);
+ close(fd);
+ break;
+ case '>':
+ if ((fd = open(list[i].s, O_WRONLY|O_CREATE)) < 0) {
+ printf(2, "failed to open %s for write: %d", list[i].s, fd);
exit();
}
+
+ if (debug)
+ printf(2, "redirect 1 to %s\n", list[i].s);
+
+ if (close(1) < 0)
+ printf(2, "close 1 failed\n");
+ if ((dfd = dup(fd)) < 0)
+ printf(2, "dup failed\n");
+ if (debug)
+ printf(2, "dup returns %d\n", dfd);
+ close(fd);
+ break;
}
}
- args[j] = '\0';
+ return 0;
+}
+
+void
+addnode(int token, char *s)
+{
+ if (nextnode >= MAXNODE) {
+ printf(2, "addnode: ran out of nodes\n");
+ return;
+ }
+
+ list[nextnode].token = token;
+ list[nextnode].s = s;
+ nextnode++;
+}
+
+
+// gettoken(s, 0) prepares gettoken for subsequent calls and returns 0.
+// gettoken(0, token) parses a shell token from the previously set string,
+// null-terminates that token, stores the token pointer in '*token',
+// and returns a token ID (0, '<', '>', '|', or 'w').
+// Subsequent calls to 'gettoken(0, token)' will return subsequent
+// tokens from the string.
+
+int
+gettoken(char *s, char **p1)
+{
+ static int c, nc;
+ static char* np1, *np2;
+
+ if (s) {
+ nc = _gettoken(s, &np1, &np2);
+ return 0;
+ }
+ c = nc;
+ *p1 = np1;
+ nc = _gettoken(np2, &np1, &np2);
+ return c;
}
+
+
+// Get the next token from string s.
+// Set *p1 to the beginning of the token and *p2 just past the token.
+// Returns
+// 0 for end-of-string;
+// < for <;
+// > for >;
+// | for |;
+// w for a word.
+//
+// Eventually (once we parse the space where the \0 will go),
+// words get nul-terminated.
+#define WHITESPACE " \t\r\n"
+#define SYMBOLS "<|>&;()"
+
+int
+_gettoken(char *s, char **p1, char **p2)
+{
+ int t;
+
+ if (s == 0) {
+ if (debug > 1)
+ printf(2, "GETTOKEN NULL\n");
+ return 0;
+ }
+
+ if (debug > 1)
+ printf(2, "GETTOKEN: %s\n", s);
+
+ *p1 = 0;
+ *p2 = 0;
+
+ while (strchr(WHITESPACE, *s))
+ *s++ = 0;
+ if (*s == 0) {
+ if (debug > 1)
+ printf(2, "EOL\n");
+ return 0;
+ }
+ if (strchr(SYMBOLS, *s)) {
+ t = *s;
+ *p1 = s;
+ *s++ = 0;
+ *p2 = s;
+ if (debug > 1)
+ printf(2, "TOK %c\n", t);
+ return t;
+ }
+ *p1 = s;
+ while (*s && !strchr(WHITESPACE SYMBOLS, *s))
+ s++;
+ *p2 = s;
+ if (debug > 1) {
+ t = **p2;
+ **p2 = 0;
+ printf(2, "WORD: %s\n", *p1);
+ **p2 = t;
+ }
+ return 'w';
+}
+
diff --git a/syscall.c b/syscall.c
index 80d5d8e..39c6821 100644
--- a/syscall.c
+++ b/syscall.c
@@ -140,6 +140,7 @@ sys_write(void)
return -1;
if(addr + n > p->sz)
return -1;
+
ret = fd_write(p->fds[fd], p->mem + addr, n);
return ret;
}
@@ -421,6 +422,7 @@ sys_dup(void)
fd_close(fd1);
return -1;
}
+ cp->fds[ufd1] = fd1;
fd1->type = cp->fds[fd]->type;
fd1->readable = cp->fds[fd]->readable;
fd1->writeable = cp->fds[fd]->writeable;
@@ -450,6 +452,27 @@ sys_link(void)
}
int
+sys_getpid(void)
+{
+ struct proc *cp = curproc[cpu()];
+ return cp->pid;
+}
+
+
+int
+sys_sbrk(void)
+{
+ int r, n;
+ struct proc *cp = curproc[cpu()];
+
+ if(fetcharg(0, &n) < 0)
+ return -1;
+ r = growproc(n);
+ setupsegs(cp);
+ return r;
+}
+
+int
sys_exec(void)
{
struct proc *cp = curproc[cpu()];
@@ -638,6 +661,12 @@ syscall(void)
case SYS_dup:
ret = sys_dup();
break;
+ case SYS_getpid:
+ ret = sys_getpid();
+ break;
+ case SYS_sbrk:
+ ret = sys_sbrk();
+ break;
default:
cprintf("unknown sys call %d\n", num);
// XXX fault
diff --git a/syscall.h b/syscall.h
index c89d5a3..4efd255 100644
--- a/syscall.h
+++ b/syscall.h
@@ -15,4 +15,6 @@
#define SYS_mkdir 16
#define SYS_chdir 17
#define SYS_dup 18
+#define SYS_getpid 19
+#define SYS_sbrk 20
diff --git a/ulib.c b/ulib.c
index 004b934..c6c7f19 100644
--- a/ulib.c
+++ b/ulib.c
@@ -20,6 +20,14 @@ strcpy(char *s, char *t)
return os;
}
+int
+strcmp(const char *p, const char *q)
+{
+ while (*p && *p == *q)
+ p++, q++;
+ return (int) ((unsigned char) *p - (unsigned char) *q);
+}
+
unsigned int
strlen(char *s)
{
@@ -41,6 +49,15 @@ memset(void *dst, int c, unsigned int n)
}
char *
+strchr(const char *s, char c)
+{
+ for (; *s; s++)
+ if (*s == c)
+ return (char *) s;
+ return 0;
+}
+
+char *
gets(char *buf, int max)
{
int i = 0, cc;
diff --git a/user.h b/user.h
index 26d984c..cc82ccf 100644
--- a/user.h
+++ b/user.h
@@ -18,10 +18,13 @@ int link(char *, char *);
int mkdir(char *);
int chdir(char *);
int dup(int);
+int getpid();
int stat(char *, struct stat *stat);
int puts(char*);
char* strcpy(char*, char*);
+char *strchr(const char *s, char c);
+int strcmp(const char *p, const char *q);
void printf(int fd, char *fmt, ...);
char *gets(char *, int max);
unsigned int strlen(char *);
diff --git a/userfs.c b/userfs.c
index 25dfaf5..d1cd49e 100644
--- a/userfs.c
+++ b/userfs.c
@@ -20,6 +20,10 @@ main(void)
printf(stdout, "userfs is running\n");
+ if (sbrk(4096) < 0) {
+ printf(stdout, "sbrk failed\n");
+ }
+
fd = open("echo", 0);
if(fd >= 0){
printf(stdout, "open echo ok\n");
diff --git a/usys.S b/usys.S
index c7a162c..617fdc5 100644
--- a/usys.S
+++ b/usys.S
@@ -25,3 +25,5 @@ STUB(link)
STUB(mkdir)
STUB(chdir)
STUB(dup)
+STUB(getpid)
+STUB(sbrk)