aboutsummaryrefslogtreecommitdiff
path: root/user/bin
diff options
context:
space:
mode:
authornthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
committernthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
commitc63f340d90800895f007de64b7d2d14624263331 (patch)
tree2c0849fa597dd6da831c8707b6f2603403778d7b /user/bin
Created student weenix repository
Diffstat (limited to 'user/bin')
-rw-r--r--user/bin/.gitignore4
-rw-r--r--user/bin/ed.c1757
-rw-r--r--user/bin/hd.c90
-rw-r--r--user/bin/ls.c108
-rw-r--r--user/bin/sh.c1910
-rw-r--r--user/bin/sl.c502
-rw-r--r--user/bin/sleep.c44
-rw-r--r--user/bin/stat.c49
-rw-r--r--user/bin/uname.c93
9 files changed, 4557 insertions, 0 deletions
diff --git a/user/bin/.gitignore b/user/bin/.gitignore
new file mode 100644
index 0000000..19a3fd8
--- /dev/null
+++ b/user/bin/.gitignore
@@ -0,0 +1,4 @@
+sh
+ls
+ed
+uname
diff --git a/user/bin/ed.c b/user/bin/ed.c
new file mode 100644
index 0000000..5510876
--- /dev/null
+++ b/user/bin/ed.c
@@ -0,0 +1,1757 @@
+/*
+ * ed is the standard text editor.
+ *
+ * I think that Keith was the one who ported ed to weenix. Thanks Keith.
+ * Note: dap also helped in 2007, but it still doesn't work.
+ * Working version added in 2010.
+ */
+
+/* Just for fun:
+ * From: patl@athena.mit.edu (Patrick J. LoPresti)
+ * Subject: The True Path (long)
+ * Date: 11 Jul 91 03:17:31 GMT
+ * Newsgroups: alt.religion.emacs,alt.slack
+ *
+ * When I log into my Xenix system with my 110 baud teletype, both vi
+ * *and* Emacs are just too damn slow. They print useless messages like,
+ * 'C-h for help' and '"foo" File is read only'. So I use the editor
+ * that doesn't waste my VALUABLE time.
+ *
+ * Ed, man! !man ed
+ *
+ * ED(1) UNIX Programmer's Manual ED(1)
+ *
+ * NAME
+ * ed - text editor
+ *
+ * SYNOPSIS
+ * ed [ - ] [ -x ] [ name ]
+ * DESCRIPTION
+ * Ed is the standard text editor.
+ * ---
+ *
+ * Computer Scientists love ed, not just because it comes first
+ * alphabetically, but because it's the standard. Everyone else loves ed
+ * because it's ED!
+ *
+ * "Ed is the standard text editor."
+ *
+ * And ed doesn't waste space on my Timex Sinclair. Just look:
+ *
+ * -rwxr-xr-x 1 root 24 Oct 29 1929 /bin/ed
+ * -rwxr-xr-t 4 root 1310720 Jan 1 1970 /usr/ucb/vi
+ * -rwxr-xr-x 1 root 5.89824e37 Oct 22 1990 /usr/bin/emacs
+ *
+ * Of course, on the system *I* administrate, vi is symlinked to ed.
+ * Emacs has been replaced by a shell script which 1) Generates a syslog
+ * message at level LOG_EMERG; 2) reduces the user's disk quota by 100K;
+ * and 3) RUNS ED!!!!!!
+ *
+ * "Ed is the standard text editor."
+ *
+ * Let's look at a typical novice's session with the mighty ed:
+ *
+ * golem> ed
+ *
+ * ?
+ * help
+ * ?
+ * ?
+ * ?
+ * quit
+ * ?
+ * exit
+ * ?
+ * bye
+ * ?
+ * hello?
+ * ?
+ * eat flaming death
+ * ?
+ * ^C
+ * ?
+ * ^C
+ * ?
+ * ^D
+ * ?
+ *
+ * ---
+ * Note the consistent user interface and error reportage. Ed is
+ * generous enough to flag errors, yet prudent enough not to overwhelm
+ * the novice with verbosity.
+ *
+ * "Ed is the standard text editor."
+ *
+ * Ed, the greatest WYGIWYG editor of all.
+ *
+ * ED IS THE TRUE PATH TO NIRVANA! ED HAS BEEN THE CHOICE OF EDUCATED
+ * AND IGNORANT ALIKE FOR CENTURIES! ED WILL NOT CORRUPT YOUR PRECIOUS
+ * BODILY FLUIDS!! ED IS THE STANDARD TEXT EDITOR! ED MAKES THE SUN
+ * SHINE AND THE BIRDS SING AND THE GRASS GREEN!!
+ *
+ * When I use an editor, I don't want eight extra KILOBYTES of worthless
+ * help screens and cursor positioning code! I just want an EDitor!!
+ * Not a "viitor". Not a "emacsitor". Those aren't even WORDS!!!! ED!
+ * ED! ED IS THE STANDARD!!!
+ *
+ * TEXT EDITOR.
+ *
+ * When IBM, in its ever-present omnipotence, needed to base their
+ * "edlin" on a UNIX standard, did they mimic vi? No. Emacs? Surely
+ * you jest. They chose the most karmic editor of all. The standard.
+ *
+ * Ed is for those who can *remember* what they are working on. If you
+ * are an idiot, you should use Emacs. If you are an Emacs, you should
+ * not be vi. If you use ED, you are on THE PATH TO REDEMPTION. THE
+ * SO-CALLED "VISUAL" EDITORS HAVE BEEN PLACED HERE BY ED TO TEMPT THE
+ * FAITHLESS. DO NOT GIVE IN!!! THE MIGHTY ED HAS SPOKEN!!!
+ *
+ * ?
+ *
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * Editor
+ */
+
+#define SIGHUP 1
+#define SIGINTR 2
+#define SIGQUIT 3
+#define FNSIZE 64
+#define LBSIZE 512
+#define ESIZE 128
+#define GBSIZE 256
+#define NBRA 5
+
+#define CBRA 1
+#define CCHR 2
+#define CDOT 4
+#define CCL 6
+#define NCCL 8
+#define CDOL 10
+#define CEOF 11
+#define CKET 12
+
+#define STAR 01
+
+#define error errfunc()
+#define READ 0
+#define WRITE 1
+
+char peekc;
+char lastc;
+char savedfile[FNSIZE];
+char file[FNSIZE];
+char linebuf[LBSIZE];
+char rhsbuf[LBSIZE / 2];
+char expbuf[ESIZE + 4];
+int circfl;
+int *zero;
+int *dot;
+int *dol;
+int *endcore;
+int *fendcore;
+int *addr1;
+int *addr2;
+char genbuf[LBSIZE];
+int count[2];
+char *nextip;
+char *linebp;
+int ninbuf;
+int io;
+int pflag;
+int onhup;
+int onquit;
+int vflag = 1;
+int listf;
+int col;
+char *globp;
+int tfile = -1;
+int tline;
+char tfname[] = "/tmp/exxxxx";
+char *loc1;
+char *loc2;
+char *locs;
+char ibuff[512];
+int iblock = -1;
+char obuff[512];
+int oblock = -1;
+int ichanged;
+int nleft;
+
+void errfunc();
+
+/* int *errlab=(int*)errfunc; */
+char TMPERR[] = "TMP";
+int names[26];
+char *braslist[NBRA];
+char *braelist[NBRA];
+
+void init();
+
+void commands();
+
+int *address();
+
+void setdot();
+
+void newline();
+
+int append(int (*f)(), int *a);
+
+void delete ();
+
+void setnoaddr();
+
+void filename();
+
+int gettty();
+
+void global(int k);
+
+void nonzero();
+
+void move(int cflag);
+
+char *getline(int tl);
+
+void setall();
+
+void exfile();
+
+void substitute(long inglob);
+
+void putfile();
+
+void putd();
+
+void dounix();
+
+void compile(int c);
+
+int execute(int gf, int *addr);
+
+int putline();
+
+char *getblock(int atl, int iof);
+
+int getchar();
+
+void blkio(int b, char *buf, void *);
+
+int compsub();
+
+void dosub();
+
+char *place(char *asp, char *al1, char *al2);
+
+void reverse(int *aa1, int *aa2);
+
+int advance(char *alp, char *aep);
+
+int cclass(char *aset, int ac, int af);
+
+void puts(char *as);
+
+void putchar(char ac);
+
+void reset();
+
+void setexit();
+
+extern int strlen(const char *s);
+
+int creat(const char *fname, mode_t mode) { return open(fname, O_CREAT, mode); }
+
+int signal(int a1, ...) { return 0; }
+
+int main(int argc, char **argv)
+{
+ register char *p1, *p2;
+
+ onquit = signal(SIGQUIT, 1);
+ onhup = signal(SIGHUP, 1);
+ argv++;
+ if (argc > 1 && **argv == '-')
+ {
+ vflag = 0;
+ /* allow debugging quits? */
+ if ((*argv)[1] == 'q')
+ {
+ signal(SIGQUIT, 0);
+ vflag++;
+ }
+ argv++;
+ argc--;
+ }
+ if (argc > 1)
+ {
+ p1 = *argv;
+ p2 = savedfile;
+ while ((*p2++ = *p1++))
+ ;
+ globp = "r";
+ }
+ fendcore = sbrk(0);
+ init();
+ /* setexit(); */
+ commands();
+ unlink(tfname);
+ return 0;
+}
+
+int getfile();
+
+void commands()
+{
+ register int *a1, c;
+
+ for (;;)
+ {
+ if (pflag)
+ {
+ pflag = 0;
+ addr1 = addr2 = dot;
+ goto print;
+ }
+ addr1 = 0;
+ addr2 = 0;
+ do
+ {
+ addr1 = addr2;
+ if ((a1 = address()) == 0)
+ {
+ c = getchar();
+ break;
+ }
+ addr2 = a1;
+ if ((c = getchar()) == ';')
+ {
+ c = ',';
+ dot = a1;
+ }
+ } while (c == ',');
+ if (addr1 == 0)
+ {
+ addr1 = addr2;
+ }
+ switch (c)
+ {
+ case 'a':
+ setdot();
+ newline();
+ append(gettty, addr2);
+ continue;
+
+ case 'c':
+ delete ();
+ append(gettty, addr1 - 1);
+ continue;
+
+ case 'd':
+ delete ();
+ continue;
+
+ case 'e':
+ setnoaddr();
+ if ((peekc = getchar()) != ' ')
+ error;
+ savedfile[0] = 0;
+ init();
+ addr2 = zero;
+ goto caseread;
+
+ case 'f':
+ setnoaddr();
+ if ((c = getchar()) != '\n')
+ {
+ peekc = c;
+ savedfile[0] = 0;
+ filename();
+ }
+ puts(savedfile);
+ continue;
+
+ case 'g':
+ global(1);
+ continue;
+
+ case 'i':
+ setdot();
+ nonzero();
+ newline();
+ append(gettty, addr2 - 1);
+ continue;
+
+ case 'k':
+ if ((c = getchar()) < 'a' || c > 'z')
+ error;
+ newline();
+ setdot();
+ nonzero();
+ names[c - 'a'] = *addr2 | 01;
+ continue;
+
+ case 'm':
+ move(0);
+ continue;
+
+ case '\n':
+ if (addr2 == 0)
+ {
+ addr2 = dot + 1;
+ }
+ addr1 = addr2;
+ goto print;
+
+ case 'l':
+ listf++;
+ newline();
+ goto print;
+ case 'p':
+ newline();
+ print:
+ setdot();
+ nonzero();
+ a1 = addr1;
+ do
+ puts(getline(*a1++));
+ while (a1 <= addr2);
+ dot = addr2;
+ listf = 0;
+ continue;
+
+ case 'q':
+ setnoaddr();
+ newline();
+ unlink(tfname);
+ exit(0);
+
+ case 'r':
+ caseread:
+ filename();
+ if ((io = open(file, O_RDONLY, 0)) < 0)
+ {
+ lastc = '\n';
+ error;
+ }
+ setall();
+ ninbuf = 0;
+ append(getfile, addr2);
+ exfile();
+ continue;
+
+ case 's':
+ setdot();
+ nonzero();
+ substitute((long)globp);
+ continue;
+
+ case 't':
+ move(1);
+ continue;
+
+ case 'v':
+ global(0);
+ continue;
+
+ case 'w':
+ setall();
+ nonzero();
+ filename();
+ if ((io = open(file, O_CREAT | O_RDWR | O_TRUNC, 0666)) < 0)
+ error;
+ putfile();
+ exfile();
+ continue;
+
+ case '=':
+ setall();
+ newline();
+ count[1] = (addr2 - zero) & 077777;
+ putd();
+ putchar('\n');
+ continue;
+
+ case '!':
+ dounix();
+ continue;
+
+ case EOF:
+ return;
+ }
+ error;
+ }
+}
+
+int *address()
+{
+ register int *a1, minus, c;
+ int n, relerr;
+
+ minus = 0;
+ a1 = 0;
+ for (;;)
+ {
+ c = getchar();
+ if ('0' <= c && c <= '9')
+ {
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += c - '0';
+ } while ((c = getchar()) >= '0' && c <= '9');
+ peekc = c;
+ if (a1 == 0)
+ {
+ a1 = zero;
+ }
+ if (minus < 0)
+ {
+ n = -n;
+ }
+ a1 += n;
+ minus = 0;
+ continue;
+ }
+ relerr = 0;
+ if (a1 || minus)
+ {
+ relerr++;
+ }
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ continue;
+
+ case '+':
+ minus++;
+ if (a1 == 0)
+ {
+ a1 = dot;
+ }
+ continue;
+
+ case '-':
+ case '^':
+ minus--;
+ if (a1 == 0)
+ {
+ a1 = dot;
+ }
+ continue;
+
+ case '?':
+ case '/':
+ compile(c);
+ a1 = dot;
+ for (;;)
+ {
+ if (c == '/')
+ {
+ a1++;
+ if (a1 > dol)
+ {
+ a1 = zero;
+ }
+ }
+ else
+ {
+ a1--;
+ if (a1 < zero)
+ {
+ a1 = dol;
+ }
+ }
+ if (execute(0, a1))
+ {
+ break;
+ }
+ if (a1 == dot)
+ error;
+ }
+ break;
+
+ case '$':
+ a1 = dol;
+ break;
+
+ case '.':
+ a1 = dot;
+ break;
+
+ case '\'':
+ if ((c = getchar()) < 'a' || c > 'z')
+ error;
+ for (a1 = zero; a1 <= dol; a1++)
+ {
+ if (names[c - 'a'] == (*a1 | 01))
+ {
+ break;
+ }
+ }
+ break;
+
+ default:
+ peekc = c;
+ if (a1 == 0)
+ {
+ return (0);
+ }
+ a1 += minus;
+ if (a1 < zero || a1 > dol)
+ error;
+ return (a1);
+ }
+ if (relerr)
+ error;
+ }
+}
+
+void setdot()
+{
+ if (addr2 == 0)
+ {
+ addr1 = addr2 = dot;
+ }
+ if (addr1 > addr2)
+ error;
+}
+
+void setall()
+{
+ if (addr2 == 0)
+ {
+ addr1 = zero + 1;
+ addr2 = dol;
+ if (dol == zero)
+ {
+ addr1 = zero;
+ }
+ }
+ setdot();
+}
+
+void setnoaddr()
+{
+ if (addr2)
+ error;
+}
+
+void nonzero()
+{
+ if (addr1 <= zero || addr2 > dol)
+ error;
+}
+
+void newline()
+{
+ register int c;
+
+ if ((c = getchar()) == '\n')
+ {
+ return;
+ }
+ if (c == 'p' || c == 'l')
+ {
+ pflag++;
+ if (c == 'l')
+ {
+ listf++;
+ }
+ if (getchar() == '\n')
+ {
+ return;
+ }
+ }
+ error;
+}
+
+void filename()
+{
+ register char *p1, *p2;
+ register int c;
+
+ count[1] = 0;
+ c = getchar();
+ if (c == '\n' || c == EOF)
+ {
+ p1 = savedfile;
+ if (*p1 == 0)
+ error;
+ p2 = file;
+ while ((*p2++ = *p1++))
+ ;
+ return;
+ }
+ if (c != ' ')
+ error;
+ while ((c = getchar()) == ' ')
+ ;
+ if (c == '\n')
+ error;
+ p1 = file;
+ do
+ {
+ *p1++ = c;
+ } while ((c = getchar()) != '\n');
+ *p1++ = 0;
+ if (savedfile[0] == 0)
+ {
+ p1 = savedfile;
+ p2 = file;
+ while ((*p1++ = *p2++))
+ ;
+ }
+}
+
+void exfile()
+{
+ close(io);
+ io = -1;
+ if (vflag)
+ {
+ putd();
+ putchar('\n');
+ }
+}
+
+void errfunc(void)
+{
+ register int c;
+
+ listf = 0;
+ puts("?");
+ count[0] = 0;
+ lseek(0, 0, 2);
+ pflag = 0;
+ if (globp)
+ {
+ lastc = '\n';
+ }
+ globp = 0;
+ peekc = lastc;
+ while ((c = getchar()) != '\n' && c != EOF)
+ ;
+ if (io > 0)
+ {
+ close(io);
+ io = -1;
+ }
+ /* reset(); */
+}
+
+int getchar()
+{
+ if ((lastc = peekc))
+ {
+ peekc = 0;
+ return (lastc);
+ }
+ if (globp)
+ {
+ if ((lastc = *globp++) != 0)
+ {
+ return (lastc);
+ }
+ globp = 0;
+ return (EOF);
+ }
+ if (read(0, &lastc, 1) <= 0)
+ {
+ return (lastc = EOF);
+ }
+ lastc &= 0177;
+ return (lastc);
+}
+
+int gettty()
+{
+ register long c, gf;
+ register char *p;
+
+ p = linebuf;
+ gf = (long)globp;
+ while ((c = getchar()) != '\n')
+ {
+ if (c == EOF)
+ {
+ if (gf)
+ {
+ peekc = c;
+ }
+ return (int)(c);
+ }
+ if ((c &= 0177) == 0)
+ {
+ continue;
+ }
+ *p++ = c;
+ if (p >= &linebuf[LBSIZE - 2])
+ error;
+ }
+ *p++ = 0;
+ if (linebuf[0] == '.' && linebuf[1] == 0)
+ {
+ return (EOF);
+ }
+ return (0);
+}
+
+int getfile()
+{
+ register int c;
+ register char *lp, *fp;
+
+ lp = linebuf;
+ fp = nextip;
+ do
+ {
+ if (--ninbuf < 0)
+ {
+ if ((ninbuf = read(io, genbuf, LBSIZE) - 1) < 0)
+ {
+ return (EOF);
+ }
+ fp = genbuf;
+ }
+ if (lp >= &linebuf[LBSIZE])
+ error;
+ if ((*lp++ = c = *fp++ & 0177) == 0)
+ {
+ lp--;
+ continue;
+ }
+ if (++count[1] == 0)
+ {
+ ++count[0];
+ }
+ } while (c != '\n');
+ *--lp = 0;
+ nextip = fp;
+ return (0);
+}
+
+void putfile()
+{
+ int *a1;
+ register char *fp, *lp;
+ register int nib;
+
+ nib = 512;
+ fp = genbuf;
+ a1 = addr1;
+ do
+ {
+ lp = getline(*a1++);
+ for (;;)
+ {
+ if (--nib < 0)
+ {
+ write(io, genbuf, fp - genbuf);
+ nib = 511;
+ fp = genbuf;
+ }
+ if (++count[1] == 0)
+ {
+ ++count[0];
+ }
+ if ((*fp++ = *lp++) == 0)
+ {
+ fp[-1] = '\n';
+ break;
+ }
+ }
+ } while (a1 <= addr2);
+ write(io, genbuf, fp - genbuf);
+}
+
+int append(f, a) int (*f)();
+int *a;
+{
+ register int *a1, *a2, *rdot;
+ int nline, tl;
+ int *corep = (int *)endcore;
+ struct core
+ {
+ int integer;
+ };
+
+ nline = 0;
+ dot = a;
+ while ((*f)() == 0)
+ {
+ if (dol >= endcore)
+ {
+ if (sbrk(1024) == (char *)-1)
+ error;
+ *corep += 1024;
+ }
+ tl = putline();
+ nline++;
+ a1 = ++dol;
+ a2 = a1 + 1;
+ rdot = ++dot;
+ while (a1 > rdot)
+ *--a2 = *--a1;
+ *rdot = tl;
+ }
+ return (nline);
+}
+
+void dounix()
+{
+ register int savint, pid, rpid;
+ int retcode;
+
+ setnoaddr();
+ if ((pid = fork()) == 0)
+ {
+ char *argv[] = {"/bin/sh", 0};
+ char *envp[] = {"PATH=/bin", 0};
+
+ signal(SIGHUP, onhup);
+ signal(SIGQUIT, onquit);
+ execve("/bin/sh", argv, envp);
+ exit(-1);
+ }
+ savint = signal(SIGINTR, 1);
+ while ((rpid = wait(&retcode)) != pid && rpid != -1)
+ ;
+ signal(SIGINTR, savint);
+ puts("!");
+}
+
+void delete ()
+{
+ register int *a1, *a2, *a3;
+
+ setdot();
+ newline();
+ nonzero();
+ a1 = addr1;
+ a2 = addr2 + 1;
+ a3 = dol;
+ dol -= a2 - a1;
+ do
+ *a1++ = *a2++;
+ while (a2 <= a3);
+ a1 = addr1;
+ if (a1 > dol)
+ {
+ a1 = dol;
+ }
+ dot = a1;
+}
+
+char *getline(int tl)
+{
+ register char *bp, *lp;
+ register int nl;
+
+ lp = linebuf;
+ bp = getblock(tl, READ);
+ nl = nleft;
+ tl &= ~0377;
+ while ((*lp++ = *bp++))
+ if (--nl == 0)
+ {
+ bp = getblock(tl += 0400, READ);
+ nl = nleft;
+ }
+ return (linebuf);
+}
+
+int putline()
+{
+ register char *bp, *lp;
+ register int nl;
+ int tl;
+
+ lp = linebuf;
+ tl = tline;
+ bp = getblock(tl, WRITE);
+ nl = nleft;
+ tl &= ~0377;
+ while ((*bp = *lp++))
+ {
+ if (*bp++ == '\n')
+ {
+ *--bp = 0;
+ linebp = lp;
+ break;
+ }
+ if (--nl == 0)
+ {
+ bp = getblock(tl += 0400, WRITE);
+ nl = nleft;
+ }
+ }
+ nl = tline;
+ tline += (((lp - linebuf) + 03) >> 1) & 077776;
+ return (nl);
+}
+
+char *getblock(int atl, int iof)
+{
+ register int bno, off;
+
+ bno = (atl >> 8) & 0377;
+ off = (atl << 1) & 0774;
+ if (bno >= 255)
+ {
+ puts(TMPERR);
+ error;
+ }
+ nleft = 512 - off;
+ if (bno == iblock)
+ {
+ ichanged |= iof;
+ return (ibuff + off);
+ }
+ if (bno == oblock)
+ {
+ return (obuff + off);
+ }
+ if (iof == READ)
+ {
+ if (ichanged)
+ {
+ blkio(iblock, ibuff, (void *)write);
+ }
+ ichanged = 0;
+ iblock = bno;
+ blkio(bno, ibuff, (void *)read);
+ return (ibuff + off);
+ }
+ if (oblock >= 0)
+ {
+ blkio(oblock, obuff, (void *)write);
+ }
+ oblock = bno;
+ return (obuff + off);
+}
+
+void blkio(int b, char *buf, void *iofcn)
+{
+ int (*iof)(int f, char *b, int len) =
+ (int (*)(int f, char *b, int len))iofcn;
+ lseek(tfile, b, SEEK_SET);
+ if ((*iof)(tfile, buf, 512) != 512)
+ {
+ puts(TMPERR);
+ error;
+ }
+}
+
+void init()
+{
+ register char *p;
+ register int pid;
+
+ close(tfile);
+ tline = 0;
+ iblock = -1;
+ oblock = -1;
+ ichanged = 0;
+ pid = getpid();
+ for (p = &tfname[11]; p > &tfname[6];)
+ {
+ *--p = (pid & 07) + '0';
+ pid >>= 3;
+ }
+ close(creat(tfname, 0600));
+ tfile = open(tfname, O_RDWR, 0);
+ brk(fendcore);
+ dot = zero = dol = fendcore;
+ endcore = fendcore - 2;
+}
+
+void global(int k)
+{
+ register char *gp;
+ register int c;
+ register int *a1;
+ char globuf[GBSIZE];
+
+ if (globp)
+ error;
+ setall();
+ nonzero();
+ if ((c = getchar()) == '\n')
+ error;
+ compile(c);
+ gp = globuf;
+ while ((c = getchar()) != '\n')
+ {
+ if (c == EOF)
+ error;
+ if (c == '\\')
+ {
+ c = getchar();
+ if (c != '\n')
+ {
+ *gp++ = '\\';
+ }
+ }
+ *gp++ = c;
+ if (gp >= &globuf[GBSIZE - 2])
+ error;
+ }
+ *gp++ = '\n';
+ *gp++ = 0;
+ for (a1 = zero; a1 <= dol; a1++)
+ {
+ *a1 &= ~01;
+ if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
+ {
+ *a1 |= 01;
+ }
+ }
+ for (a1 = zero; a1 <= dol; a1++)
+ {
+ if (*a1 & 01)
+ {
+ *a1 &= ~01;
+ dot = a1;
+ globp = globuf;
+ commands();
+ a1 = zero;
+ }
+ }
+}
+
+int getsub();
+
+void substitute(long inglob)
+{
+ register int gsubf, *a1, nl;
+
+ gsubf = compsub();
+ for (a1 = addr1; a1 <= addr2; a1++)
+ {
+ if (execute(0, a1) == 0)
+ {
+ continue;
+ }
+ inglob |= 01;
+ dosub();
+ if (gsubf)
+ {
+ while (*loc2)
+ {
+ if (execute(1, 0) == 0)
+ {
+ break;
+ }
+ dosub();
+ }
+ }
+ *a1 = putline();
+ nl = append(getsub, a1);
+ a1 += nl;
+ addr2 += nl;
+ }
+ if (inglob == 0)
+ error;
+}
+
+int compsub()
+{
+ register int seof, c;
+ register char *p;
+
+ if ((seof = getchar()) == '\n')
+ error;
+ compile(seof);
+ p = rhsbuf;
+ for (;;)
+ {
+ c = getchar();
+ if (c == '\\')
+ {
+ c = getchar() | 0200;
+ }
+ if (c == '\n')
+ error;
+ if (c == seof)
+ {
+ break;
+ }
+ *p++ = c;
+ if (p >= &rhsbuf[LBSIZE / 2])
+ error;
+ }
+ *p++ = 0;
+ if ((peekc = getchar()) == 'g')
+ {
+ peekc = 0;
+ newline();
+ return (1);
+ }
+ newline();
+ return (0);
+}
+
+int getsub()
+{
+ register char *p1, *p2;
+
+ p1 = linebuf;
+ if ((p2 = linebp) == 0)
+ {
+ return (EOF);
+ }
+ while ((*p1++ = *p2++))
+ ;
+ linebp = 0;
+ return (0);
+}
+
+void dosub()
+{
+ register char *lp, *sp, *rp;
+ int c;
+
+ lp = linebuf;
+ sp = genbuf;
+ rp = rhsbuf;
+ while (lp < loc1)
+ *sp++ = *lp++;
+ while ((c = *rp++))
+ {
+ if (c == '&')
+ {
+ sp = place(sp, loc1, loc2);
+ continue;
+ }
+ else if (c < 0 && (c &= 0177) >= '1' && c < NBRA + '1')
+ {
+ sp = place(sp, braslist[c - '1'], braelist[c - '1']);
+ continue;
+ }
+ *sp++ = c & 0177;
+ if (sp >= &genbuf[LBSIZE])
+ error;
+ }
+ lp = loc2;
+ loc2 = sp + (long)linebuf - (long)genbuf;
+ while ((*sp++ = *lp++))
+ if (sp >= &genbuf[LBSIZE])
+ error;
+ lp = linebuf;
+ sp = genbuf;
+ while ((*lp++ = *sp++))
+ ;
+}
+
+char *place(char *asp, char *al1, char *al2)
+{
+ register char *sp, *l1, *l2;
+
+ sp = asp;
+ l1 = al1;
+ l2 = al2;
+ while (l1 < l2)
+ {
+ *sp++ = *l1++;
+ if (sp >= &genbuf[LBSIZE])
+ error;
+ }
+ return (sp);
+}
+
+int getcopy();
+
+void move(int cflag)
+{
+ register int *adt, *ad1, *ad2;
+
+ setdot();
+ nonzero();
+ if ((adt = address()) == 0)
+ error;
+ newline();
+ ad1 = addr1;
+ ad2 = addr2;
+ if (cflag)
+ {
+ ad1 = dol;
+ append(getcopy, ad1++);
+ ad2 = dol;
+ }
+ ad2++;
+ if (adt < ad1)
+ {
+ dot = adt + (ad2 - ad1);
+ if ((++adt) == ad1)
+ {
+ return;
+ }
+ reverse(adt, ad1);
+ reverse(ad1, ad2);
+ reverse(adt, ad2);
+ }
+ else if (adt >= ad2)
+ {
+ dot = adt++;
+ reverse(ad1, ad2);
+ reverse(ad2, adt);
+ reverse(ad1, adt);
+ }
+ else
+ error;
+}
+
+void reverse(int *aa1, int *aa2)
+{
+ register int *a1, *a2, t;
+
+ a1 = aa1;
+ a2 = aa2;
+ for (;;)
+ {
+ t = *--a2;
+ if (a2 <= a1)
+ {
+ return;
+ }
+ *a2 = *a1;
+ *a1++ = t;
+ }
+}
+
+int getcopy()
+{
+ if (addr1 > addr2)
+ {
+ return (EOF);
+ }
+ getline(*addr1++);
+ return (0);
+}
+
+void compile(int aeof)
+{
+ register int eof, c;
+ register char *ep;
+ char *lastep;
+ char bracket[NBRA], *bracketp;
+ int nbra;
+ int cclcnt;
+
+ ep = expbuf;
+ eof = aeof;
+ bracketp = bracket;
+ nbra = 0;
+ if ((c = getchar()) == eof)
+ {
+ if (*ep == 0)
+ error;
+ return;
+ }
+ circfl = 0;
+ if (c == '^')
+ {
+ c = getchar();
+ circfl++;
+ }
+ if (c == '*')
+ {
+ goto cerror;
+ }
+ peekc = c;
+ for (;;)
+ {
+ if (ep >= &expbuf[ESIZE])
+ {
+ goto cerror;
+ }
+ c = getchar();
+ if (c == eof)
+ {
+ *ep++ = CEOF;
+ return;
+ }
+ if (c != '*')
+ {
+ lastep = ep;
+ }
+ switch (c)
+ {
+ case '\\':
+ if ((c = getchar()) == '(')
+ {
+ if (nbra >= NBRA)
+ {
+ goto cerror;
+ }
+ *bracketp++ = nbra;
+ *ep++ = CBRA;
+ *ep++ = nbra++;
+ continue;
+ }
+ if (c == ')')
+ {
+ if (bracketp <= bracket)
+ {
+ goto cerror;
+ }
+ *ep++ = CKET;
+ *ep++ = *--bracketp;
+ continue;
+ }
+ *ep++ = CCHR;
+ if (c == '\n')
+ {
+ goto cerror;
+ }
+ *ep++ = c;
+ continue;
+
+ case '.':
+ *ep++ = CDOT;
+ continue;
+
+ case '\n':
+ goto cerror;
+
+ case '*':
+ if (*lastep == CBRA || *lastep == CKET)
+ error;
+ *lastep |= STAR;
+ continue;
+
+ case '$':
+ if ((peekc = getchar()) != eof)
+ {
+ goto defchar;
+ }
+ *ep++ = CDOL;
+ continue;
+
+ case '[':
+ *ep++ = CCL;
+ *ep++ = 0;
+ cclcnt = 1;
+ if ((c = getchar()) == '^')
+ {
+ c = getchar();
+ ep[-2] = NCCL;
+ }
+ do
+ {
+ if (c == '\n')
+ {
+ goto cerror;
+ }
+ *ep++ = c;
+ cclcnt++;
+ if (ep >= &expbuf[ESIZE])
+ {
+ goto cerror;
+ }
+ } while ((c = getchar()) != ']');
+ lastep[1] = cclcnt;
+ continue;
+
+ defchar:
+ default:
+ *ep++ = CCHR;
+ *ep++ = c;
+ }
+ }
+cerror:
+ expbuf[0] = 0;
+ error;
+}
+
+int execute(gf, addr) int gf;
+int *addr;
+{
+ register char *p1, *p2, c;
+
+ if (gf)
+ {
+ if (circfl)
+ {
+ return (0);
+ }
+ p1 = linebuf;
+ p2 = genbuf;
+ while ((*p1++ = *p2++))
+ ;
+ locs = p1 = loc2;
+ }
+ else
+ {
+ if (addr == zero)
+ {
+ return (0);
+ }
+ p1 = getline(*addr);
+ locs = 0;
+ }
+ p2 = expbuf;
+ if (circfl)
+ {
+ loc1 = p1;
+ return (advance(p1, p2));
+ }
+ /* fast check for first character */
+ if (*p2 == CCHR)
+ {
+ c = p2[1];
+ do
+ {
+ if (*p1 != c)
+ {
+ continue;
+ }
+ if (advance(p1, p2))
+ {
+ loc1 = p1;
+ return (1);
+ }
+ } while (*p1++);
+ return (0);
+ }
+ /* regular algorithm */
+ do
+ {
+ if (advance(p1, p2))
+ {
+ loc1 = p1;
+ return (1);
+ }
+ } while (*p1++);
+ return (0);
+}
+
+int advance(char *alp, char *aep)
+{
+ register char *lp, *ep, *curlp;
+
+ lp = alp;
+ ep = aep;
+ for (;;)
+ {
+ switch (*ep++)
+ {
+ case CCHR:
+ if (*ep++ == *lp++)
+ {
+ continue;
+ }
+ return (0);
+
+ case CDOT:
+ if (*lp++)
+ {
+ continue;
+ }
+ return (0);
+
+ case CDOL:
+ if (*lp == 0)
+ {
+ continue;
+ }
+ return (0);
+
+ case CEOF:
+ loc2 = lp;
+ return (1);
+
+ case CCL:
+ if (cclass(ep, *lp++, 1))
+ {
+ ep += *ep;
+ continue;
+ }
+ return (0);
+
+ case NCCL:
+ if (cclass(ep, *lp++, 0))
+ {
+ ep += *ep;
+ continue;
+ }
+ return (0);
+
+ case CBRA:
+ braslist[(int)*ep++] = lp;
+ continue;
+
+ case CKET:
+ braelist[(int)*ep++] = lp;
+ continue;
+
+ case CDOT | STAR:
+ curlp = lp;
+ while (*lp++)
+ ;
+ goto star;
+
+ case CCHR | STAR:
+ curlp = lp;
+ while (*lp++ == *ep)
+ ;
+ ep++;
+ goto star;
+
+ case CCL | STAR:
+ case NCCL | STAR:
+ curlp = lp;
+ while (cclass(ep, *lp++, ep[-1] == (CCL | STAR)))
+ ;
+ ep += *ep;
+ goto star;
+
+ star:
+ do
+ {
+ lp--;
+ if (lp == locs)
+ {
+ break;
+ }
+ if (advance(lp, ep))
+ {
+ return (1);
+ }
+ } while (lp > curlp);
+ return (0);
+
+ default:
+ error;
+ }
+ }
+}
+
+int cclass(char *aset, int ac, int af)
+{
+ register char *set, c;
+ register int n;
+
+ set = aset;
+ if ((c = ac) == 0)
+ {
+ return (0);
+ }
+ n = *set++;
+ while (--n)
+ if (*set++ == c)
+ {
+ return (af);
+ }
+ return (!af);
+}
+
+void putd()
+{
+#if 0
+ register r;
+ extern ldivr;
+
+ count[1] = ldiv(count[0], count[1], 10);
+ count[0] = 0;
+ r = ldivr;
+ if (count[1])
+ putd();
+ putchar(r + '0');
+#else
+ printf("%d", count[1]);
+#endif
+}
+
+void puts(char *as)
+{
+ register char *sp;
+
+ sp = as;
+ col = 0;
+ while (*sp)
+ putchar(*sp++);
+ putchar('\n');
+}
+
+char line[80];
+char *linp = line;
+
+void putchar(char ac)
+{
+ register char *lp;
+ register int c;
+
+ lp = linp;
+ c = ac;
+ if (listf)
+ {
+ col++;
+ if (col >= 72)
+ {
+ col = 0;
+ *lp++ = '\\';
+ *lp++ = '\n';
+ }
+ if (c == '\t')
+ {
+ c = '>';
+ goto esc;
+ }
+ if (c == '\b')
+ {
+ c = '<';
+ esc:
+ *lp++ = '-';
+ *lp++ = '\b';
+ *lp++ = c;
+ goto out;
+ }
+ if (c < ' ' && c != '\n')
+ {
+ *lp++ = '\\';
+ *lp++ = (c >> 3) + '0';
+ *lp++ = (c & 07) + '0';
+ col += 2;
+ goto out;
+ }
+ }
+ *lp++ = c;
+out:
+ if (c == '\n' || lp >= &line[64])
+ {
+ linp = line;
+ write(1, line, lp - line);
+ return;
+ }
+ linp = lp;
+}
+
+/*
+ * Get process ID routine if system call is unavailable.
+
+getpid()
+{
+ register f;
+ int b[1];
+
+ f = open("/dev/kmem", 0);
+ if(f < 0)
+ return(-1);
+ seek(f, 0140074, 0);
+ read(f, b, 2);
+ seek(f, b[0]+8, 0);
+ read(f, b, 2);
+ close(f);
+ return(b[0]);
+}
+ */
diff --git a/user/bin/hd.c b/user/bin/hd.c
new file mode 100644
index 0000000..4b5b81c
--- /dev/null
+++ b/user/bin/hd.c
@@ -0,0 +1,90 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LINE_LEN 16
+
+int main(int argc, char **argv)
+{
+ int readfd = 0;
+ if (argc == 2)
+ {
+ readfd = open(argv[1], O_RDONLY, 0666);
+ if (readfd < 0)
+ {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ return 1;
+ }
+ }
+ else if (argc > 2)
+ {
+ printf("usage: hd [file]\n");
+ return 1;
+ }
+
+ char lastbuf[LINE_LEN];
+ char curbuf[LINE_LEN];
+ int off = 0;
+ int lastrep = 0;
+ int bytes;
+
+ int i;
+ while ((bytes = read(readfd, curbuf, LINE_LEN)) > 0)
+ {
+ if (off > 0 && !memcmp(lastbuf, curbuf, LINE_LEN))
+ {
+ if (!lastrep)
+ {
+ printf("*\n");
+ lastrep = 1;
+ }
+ off += bytes;
+ continue;
+ }
+ lastrep = 0;
+ printf("%08x ", off);
+ off += bytes;
+ /* print bytes */
+ for (i = 0; i < LINE_LEN; ++i)
+ {
+ if (i < bytes)
+ {
+ printf("%02x ", (unsigned char)curbuf[i]);
+ }
+ else
+ {
+ printf(" ");
+ }
+ if (i == 7)
+ {
+ printf(" ");
+ }
+ }
+ /* show printable characters */
+ printf("|");
+ for (i = 0; i < bytes; ++i)
+ {
+ char c = curbuf[i];
+ if (c < 32 || c > 126)
+ {
+ printf(".");
+ }
+ else
+ {
+ printf("%c", c);
+ }
+ }
+ printf("|\n");
+ memcpy(lastbuf, curbuf, LINE_LEN);
+ }
+ printf("%08x\n", off);
+
+ if (readfd > 0)
+ {
+ close(readfd);
+ }
+ return 0;
+}
diff --git a/user/bin/ls.c b/user/bin/ls.c
new file mode 100644
index 0000000..e04b39f
--- /dev/null
+++ b/user/bin/ls.c
@@ -0,0 +1,108 @@
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <errno.h>
+
+static int do_ls(const char *dir)
+{
+ int fd;
+ struct dirent *dirent;
+ int nbytes;
+ char tmpbuf[256];
+ stat_t sbuf;
+
+ union {
+ struct dirent dirent;
+ char buf[4096];
+ } lsb;
+
+ fd = open(dir, O_RDONLY, 0600);
+ if (fd < 0)
+ {
+ fprintf(stderr, "ls: unable to open \"%s\": errno %d\n", dir, errno);
+ return 1;
+ }
+
+ while ((nbytes = getdents(fd, &lsb.dirent, sizeof(lsb))) > 0)
+ {
+ dirent = &lsb.dirent;
+
+ if (nbytes % sizeof(struct dirent))
+ {
+ fprintf(stderr,
+ "ls: incorrect return value from getdents (%d):"
+ " not a multiple of sizeof(struct dirent) (%ld)\n",
+ nbytes, sizeof(struct dirent));
+ return 1;
+ }
+ do
+ {
+ int reclen;
+ int size;
+
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s/%s", dir, dirent->d_name);
+ if (0 == stat(tmpbuf, &sbuf))
+ {
+ size = sbuf.st_size;
+ }
+ else
+ {
+ size = 0;
+ }
+
+ reclen = sizeof(struct dirent);
+ fprintf(stdout, "%7d %-20s %d\n", size, dirent->d_name,
+ dirent->d_ino);
+ dirent = (struct dirent *)(((char *)dirent) + reclen);
+ nbytes -= reclen;
+ } while (nbytes);
+ }
+ if (nbytes < 0)
+ {
+ if (errno == ENOTDIR)
+ {
+ fprintf(stdout, "%s\n", dir);
+ }
+ else
+ {
+ fprintf(stderr, "ls: couldn't list %s: errno %d\n", dir, errno);
+ }
+ }
+
+ if (close(fd) < 0)
+ {
+ fprintf(stderr, "ls: close %s: errno %d\n", dir, errno);
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ if (argc < 2)
+ {
+ ret = do_ls(".");
+ }
+ else if (argc < 3)
+ {
+ ret = do_ls(argv[1]);
+ }
+ else
+ {
+ int error;
+ int argn;
+
+ error = 0;
+ for (argn = 1; argn < argc; argn++)
+ {
+ fprintf(stdout, "%s:\n", argv[argn]);
+ error += do_ls(argv[argn]);
+ fprintf(stdout, "\n");
+ }
+ ret = error;
+ }
+ return ret;
+}
diff --git a/user/bin/sh.c b/user/bin/sh.c
new file mode 100644
index 0000000..e38a7f2
--- /dev/null
+++ b/user/bin/sh.c
@@ -0,0 +1,1910 @@
+#undef DEBUG_SH
+
+#ifdef DEBUG_SH
+#define dbg(x) fprintf x
+#else
+#define dbg(x)
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#define ROOT "/"
+
+#define HOME ROOT "."
+#define TMP ROOT "tmp"
+
+#define ARGV_MAX 256
+#define REDIR_MAX 10
+
+typedef struct redirect
+{
+ int r_sfd;
+ int r_dfd;
+} redirect_t;
+
+typedef struct redirect_map
+{
+ int rm_nfds;
+ redirect_t rm_redir[REDIR_MAX];
+} redirect_map_t;
+
+typedef struct ioenv
+{
+ int io_map_fd[3];
+#define io_map_file io_map_fd
+} ioenv_t;
+
+static char **my_envp;
+
+static void parse(char *line);
+
+static int execute(int argc, char *argv[], redirect_map_t *map);
+
+static void add_redirect(redirect_map_t *map, int sfd, int dfd);
+
+#define DECL_CMD(x) static int cmd_##x(int argc, char *argv[], ioenv_t *io)
+
+DECL_CMD(env);
+
+DECL_CMD(cd);
+
+DECL_CMD(help);
+
+DECL_CMD(exit);
+
+DECL_CMD(mkdir);
+
+DECL_CMD(rmdir);
+
+DECL_CMD(clear);
+
+DECL_CMD(ln);
+
+DECL_CMD(rm);
+
+DECL_CMD(mv);
+
+DECL_CMD(cat);
+
+DECL_CMD(echo);
+
+DECL_CMD(cp);
+
+DECL_CMD(sync);
+
+DECL_CMD(check);
+
+DECL_CMD(repeat);
+
+DECL_CMD(parallel);
+
+DECL_CMD(time);
+
+typedef struct
+{
+ const char *cmd_name;
+
+ int (*cmd_func)(int argc, char *argv[], ioenv_t *io);
+
+ const char *cmd_helptext;
+} cmd_t;
+
+static cmd_t builtin_cmds[] = {
+ {"?", cmd_help, "list shell commands"},
+ {"cat", cmd_cat, "display file"},
+ {"env", cmd_env, "display environment"},
+ {"cd", cmd_cd, "change directory"},
+ {"check", cmd_check, "test operating system"},
+ {"clear", cmd_clear, "clear screen"},
+ {"cp", cmd_cp, "copy file"},
+ {"echo", cmd_echo, "print arguments"},
+ {"exit", cmd_exit, "exit shell"},
+ {"help", cmd_help, "list shell commands"},
+ {"ln", cmd_ln, "link file"},
+ {"mkdir", cmd_mkdir, "create a directory"},
+ {"mv", cmd_mv, "move file"},
+ {"quit", cmd_exit, "exit shell"},
+ {"rm", cmd_rm, "remove file(s)"},
+ {"rmdir", cmd_rmdir, "remove a directory"},
+ {"sync", cmd_sync, "sync filesystems"},
+ {"repeat", cmd_repeat, "repeat a command"},
+ {"parallel", cmd_parallel, "run multiple commands in parallel"},
+ {"time", cmd_time, "time a command"},
+ {NULL, NULL, NULL}};
+
+#define builtin_stdin (&io->io_map_file[0])
+#define builtin_stdout (&io->io_map_file[1])
+#define builtin_stderr (&io->io_map_file[2])
+
+#define is_std_stream(fd) ((fd) >= 0 && (fd) <= 2)
+
+DECL_CMD(chk_sparse);
+
+DECL_CMD(chk_unlink);
+
+DECL_CMD(chk_wrnoent);
+
+DECL_CMD(chk_sbrk);
+
+DECL_CMD(chk_zero);
+
+DECL_CMD(chk_null);
+
+DECL_CMD(chk_priv);
+
+DECL_CMD(chk_malloc);
+
+static cmd_t check_cmds[] = {
+ {"null", cmd_chk_null, "read and write /dev/null"},
+ {"sbrk", cmd_chk_sbrk, "memory allocation - sbrk"},
+ {"sparse", cmd_chk_sparse, "sparse mmap writes"},
+ {"unlink", cmd_chk_unlink, "create and unlink a file"},
+ {"wrnoent", cmd_chk_wrnoent, "write to an unlinked file"},
+ {"zero", cmd_chk_zero, "read and map /dev/zero"},
+ {"priv", cmd_chk_priv, "writes to MAP_PRIVATE mapping"},
+ {"malloc", cmd_chk_malloc, "memory allocation - malloc"},
+ {NULL, NULL, NULL}};
+
+static int check_failed(const char *test, const char *cmd)
+{
+ fprintf(stderr, "%s: %s failed: %s\n", test, cmd, strerror(errno));
+ /*fprintf(stderr, "%s: %s failed: errno %d\n", test, cmd, errno);*/
+ return 1;
+}
+
+static int check_exists(const char *file)
+{
+ int fd;
+
+ fd = open(file, O_RDONLY, 0);
+ if (fd >= 0)
+ {
+ close(fd);
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+char *strdup(const char *); // darmanio: for some reason, gcc complains if I
+ // don't re-forward declare it here...
+static char **copy_args(int argc, char *argv[])
+{
+ char **args = malloc(sizeof(char *) * (argc + 1));
+ args[argc] = NULL;
+ while (--argc >= 0)
+ args[argc] = strdup(argv[argc]);
+ return args;
+}
+
+static void free_args(int argc, char *argv[])
+{
+ while (--argc >= 0)
+ free(argv[argc]);
+ free(argv);
+}
+
+DECL_CMD(chk_priv)
+{
+ const char *test = argv[0];
+ const char *tmpfile = TMP "/priv";
+ int fd;
+ void *addr;
+ const char *str = "Hello there.\n";
+ int error;
+ char *tmpstr;
+ size_t len;
+ uint32_t ii;
+ char tmpbuf[256];
+
+ if (check_exists(tmpfile))
+ {
+ fprintf(stderr, "%s: file exists: %s\n", test, tmpfile);
+ return 1;
+ }
+
+ /* Create a file with some data.
+ */
+
+ fd = open(tmpfile, O_CREAT | O_RDWR, 0);
+ if (fd < 0)
+ {
+ return check_failed(test, "open");
+ }
+
+ if (write(fd, str, strlen(str)) != (ssize_t)strlen(str))
+ {
+ error = check_failed(test, "write");
+ goto err_close;
+ }
+
+ /* Map the file MAP_PRIVATE.
+ */
+
+ len = strlen(str);
+ addr = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (addr == MAP_FAILED)
+ {
+ error = check_failed(test, "mmap");
+ goto err_close;
+ }
+
+ tmpstr = (char *)addr;
+
+ /* Verify that the string is initially in the mapping.
+ */
+
+ if (strncmp(tmpstr, str, strlen(str)))
+ {
+ fprintf(stderr, "%s: file doesn't have string\n", test);
+ error = 1;
+ goto err_unmap;
+ }
+
+ memset(addr, 0, strlen(str));
+
+ /* Verify that the string has been overwritten in the mapping.
+ */
+
+ for (ii = 0; ii < len; ii++)
+ {
+ if (tmpstr[ii] != 0)
+ {
+ fprintf(stderr, "%s: didn't write to mapping\n", test);
+ error = 1;
+ goto err_unmap;
+ }
+ }
+
+ /* Verify that the file still contains the original string.
+ */
+
+ if (lseek(fd, 0, SEEK_SET) != 0)
+ {
+ error = check_failed(test, "lseek");
+ goto err_unmap;
+ }
+
+ if ((ii = read(fd, tmpbuf, sizeof(tmpbuf))) != len)
+ {
+ fprintf(stderr, "%s: read returned %d, expecting %lu.\n", test, ii,
+ len);
+ error = 1;
+ goto err_unmap;
+ }
+
+ if (strncmp(tmpbuf, str, strlen(str)))
+ {
+ fprintf(stderr, "%s: file was changed by MAP_PRIVATE?!\n", test);
+ error = 1;
+ goto err_unmap;
+ }
+
+ error = 0;
+
+err_unmap:
+ if (munmap(addr, len) < 0)
+ {
+ error = check_failed(test, "munmap");
+ }
+err_close:
+ if (close(fd) < 0)
+ {
+ error = check_failed(test, "close");
+ }
+ if (unlink(tmpfile) < 0)
+ {
+ error = check_failed(test, "unlink");
+ }
+ return error;
+}
+
+DECL_CMD(chk_null)
+{
+ const char *test = argv[0];
+ const char *null = "/dev/null";
+ int fd;
+ int nbytes;
+ char buf[256];
+ int error;
+
+ fd = open(null, O_RDWR, 0600);
+ if (fd < 0)
+ {
+ return check_failed(test, "open");
+ }
+
+ memset(buf, 0xCC, sizeof(buf));
+
+ /* Try writing to /dev/null. Should return buffer size.
+ */
+
+ nbytes = write(fd, buf, sizeof(buf));
+ if (nbytes != sizeof(buf))
+ {
+ error = check_failed(test, "write");
+ goto err_close;
+ }
+
+ /* Try reading from /dev/null. Should return zero.
+ */
+
+ nbytes = read(fd, buf, sizeof(buf));
+ if (nbytes != 0)
+ {
+ error = check_failed(test, "read");
+ goto err_close;
+ }
+
+ error = 0;
+
+err_close:
+ if (close(fd) < 0)
+ {
+ error = check_failed(test, "close");
+ }
+ return error;
+}
+
+DECL_CMD(chk_zero)
+{
+ const char *test = argv[0];
+ void *addr;
+ int fd;
+ const char *zero = "/dev/zero";
+ char buf[256];
+ int nbytes;
+ int error;
+ uint32_t ii;
+ size_t len;
+ unsigned long *lp;
+ unsigned char *cp;
+
+ fd = open(zero, O_RDWR, 0600);
+ if (fd < 0)
+ {
+ return check_failed(test, "open");
+ }
+
+ /* Set buffer to a non-zero value, then read from /dev/zero
+ * and make sure that the buffer is cleared.
+ */
+
+ memset(buf, 0xCC, sizeof(buf));
+
+ nbytes = read(fd, buf, sizeof(buf));
+ if (nbytes != sizeof(buf))
+ {
+ error = check_failed(test, "read");
+ goto err_close;
+ }
+
+ for (ii = 0; ii < sizeof(buf); ii++)
+ {
+ if (buf[ii] != 0)
+ {
+ error = check_failed(test, "verify read");
+ goto err_close;
+ }
+ }
+
+ /* Map /dev/zero and make sure all pages are initially zero.
+ */
+
+ len = 8192 * 5;
+
+ addr = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (addr == MAP_FAILED)
+ {
+ error = check_failed(test, "mmap");
+ goto err_close;
+ }
+
+ cp = (unsigned char *)addr;
+ for (ii = 0; ii < len; ii++, cp++)
+ {
+ if (*cp != 0)
+ {
+ error = check_failed(test, "verify mmap zeros");
+ goto err_unmap;
+ }
+ }
+
+ /* ... make sure writes are allowed.
+ */
+
+ lp = (unsigned long *)addr;
+ for (ii = 0; ii < (len / sizeof(*lp)); ii++, lp++)
+ {
+ *lp = ii;
+ }
+
+ lp = (unsigned long *)addr;
+ for (ii = 0; ii < (len / sizeof(*lp)); ii++, lp++)
+ {
+ if (*lp != ii)
+ {
+ error = check_failed(test, "verify map write");
+ goto err_unmap;
+ }
+ }
+
+ error = 0;
+
+err_unmap:
+ if (munmap(addr, len) < 0)
+ {
+ error = check_failed(test, "munmap");
+ }
+err_close:
+ if (close(fd) < 0)
+ {
+ error = check_failed(test, "close");
+ }
+ return error;
+}
+
+DECL_CMD(chk_malloc)
+{
+ const char *test = argv[0];
+ void *addr;
+ int len;
+ int *tmp;
+ uint32_t ii;
+ int error;
+
+ len = 8192 + 128;
+ addr = malloc(len);
+ if (!addr)
+ {
+ return check_failed(test, "malloc");
+ }
+
+ /* Try writing to the memory.
+ */
+ tmp = (int *)addr;
+ for (ii = 0; ii < (len / sizeof(int)); ii++)
+ {
+ *tmp++ = ii;
+ }
+
+ /* Verify what we've written.
+ */
+ tmp = (int *)addr;
+ for (ii = 0; ii < (len / sizeof(int)); ii++)
+ {
+ if (*tmp++ != (int)ii)
+ {
+ fprintf(stderr, "%s: verify failed at 0x%lx\n", test,
+ (unsigned long)tmp);
+ error = 1;
+ goto out_free;
+ }
+ }
+
+ error = 0;
+out_free:
+ free(addr);
+ return error;
+}
+
+DECL_CMD(chk_sbrk)
+{
+ void *oldbrk1, *oldbrk2;
+ const void *brk_failed = (void *)-1;
+ const char *test = argv[0];
+ int len;
+ int *tmp;
+ uint32_t ii;
+
+ /* A length which is not a page multiple, yet a multiple of 8.
+ */
+ len = 8192 * 5 + 128;
+
+ /* Try allocating some memory.
+ */
+ oldbrk1 = sbrk(len);
+ if (oldbrk1 == brk_failed)
+ {
+ return check_failed(test, "sbrk alloc");
+ }
+
+ /* Try writing to the memory.
+ */
+ tmp = (int *)oldbrk1;
+ for (ii = 0; ii < (len / sizeof(int)); ii++)
+ {
+ *tmp++ = ii;
+ }
+
+ /* Try verifying what we wrote.
+ */
+ tmp = (int *)oldbrk1;
+ for (ii = 0; ii < (len / sizeof(int)); ii++)
+ {
+ if (*tmp++ != (int)ii)
+ {
+ fprintf(stderr, "%s: verify failed at 0x%lx\n", test,
+ (unsigned long)tmp);
+ return 1;
+ }
+ }
+
+ /* Try freeing the memory.
+ */
+ oldbrk2 = sbrk(-len);
+ if (oldbrk2 == brk_failed)
+ {
+ return check_failed(test, "sbrk dealloc");
+ }
+
+ /* oldbrk2 should be at least "len" greater than oldbrk1.
+ */
+ if ((unsigned long)oldbrk2 < ((unsigned long)oldbrk1 + len))
+ {
+ fprintf(stderr, "%s: sbrk didn't return old brk??\n", test);
+ return 1;
+ }
+
+ return 0;
+}
+
+DECL_CMD(chk_wrnoent)
+{
+ int fd;
+ int error;
+ int nfd;
+ const char *tmpfile = TMP "/chk_wrnoent";
+ const char *test = argv[0];
+ const char *teststr = "Hello World!";
+ char buf[256];
+
+ /* Verify that file doesn't exist.
+ */
+ if (check_exists(tmpfile))
+ {
+ fprintf(stderr, "%s: tmpfile exists\n", test);
+ return 1;
+ }
+
+ /* Create file.
+ */
+ fd = open(tmpfile, O_CREAT | O_RDWR, 0600);
+ if (fd < 0)
+ {
+ return check_failed(test, "create");
+ }
+
+ /* Unlink file.
+ */
+ error = unlink(tmpfile);
+ if (error < 0)
+ {
+ error = check_failed(test, "unlink");
+ goto out_close;
+ }
+
+ /* Verify that file is gone.
+ */
+ nfd = open(tmpfile, O_RDONLY, 0);
+ if (nfd >= 0)
+ {
+ error = check_failed(test, "open nonexistent");
+ goto out_close;
+ }
+
+ /* Try writing a string to the file and reading it back.
+ */
+ error = write(fd, teststr, strlen(teststr));
+ if (error != (ssize_t)strlen(teststr))
+ {
+ error = check_failed(test, "write teststr");
+ goto out_close;
+ }
+ error = lseek(fd, 0, SEEK_SET);
+ if (error != 0)
+ {
+ error = check_failed(test, "lseek begin");
+ goto out_close;
+ }
+ error = read(fd, buf, strlen(teststr));
+ if (error != (ssize_t)strlen(teststr))
+ {
+ error = check_failed(test, "read teststr");
+ goto out_close;
+ }
+
+ /* Verify string.
+ */
+ if (strncmp(buf, teststr, strlen(teststr)))
+ {
+ fprintf(stderr, "%s: verify string failed\n", test);
+ error = 1;
+ goto out_close;
+ }
+
+ error = 0;
+out_close:
+ if (close(fd) < 0)
+ {
+ error = check_failed(test, "close");
+ }
+ return error;
+}
+
+DECL_CMD(chk_unlink)
+{
+ int fd;
+ int error;
+ int nfd;
+ const char *tmpfile = TMP "/chk_unlink";
+ const char *test = argv[0];
+
+ /* Verify that file doesn't exist.
+ */
+ if (check_exists(tmpfile))
+ {
+ fprintf(stderr, "%s: tmpfile exists\n", test);
+ return 1;
+ }
+
+ /* Create file.
+ */
+ fd = open(tmpfile, O_CREAT | O_RDONLY, 0600);
+ if (fd < 0)
+ {
+ return check_failed(test, "create");
+ }
+
+ /* Unlink file.
+ */
+ error = unlink(tmpfile);
+ if (error < 0)
+ {
+ error = check_failed(test, "unlink");
+ goto out_close;
+ }
+
+ /* Verify that file is gone.
+ */
+ nfd = open(tmpfile, O_RDONLY, 0);
+ if (nfd >= 0)
+ {
+ error = check_failed(test, "open nonexistent");
+ goto out_close;
+ }
+
+ error = 0;
+out_close:
+ if (close(fd) < 0)
+ {
+ error = check_failed(test, "close");
+ }
+ return error;
+}
+
+DECL_CMD(chk_sparse)
+{
+ int fd;
+ const char *tmpfile = TMP "/chk_sparse";
+ const char *test = argv[0];
+ int error;
+ int seek;
+ int len = 5 * 8192;
+ void *map;
+ const char *teststr = "Hello there?";
+ char *map_ch;
+ const char *tmpstr;
+ char buf[256];
+ int ii;
+
+ /* Verify that file doesn't exist.
+ */
+ if (check_exists(tmpfile))
+ {
+ fprintf(stderr, "%s: tmpfile exists\n", test);
+ return 1;
+ }
+
+ /* Create file.
+ */
+ fd = open(tmpfile, O_CREAT | O_RDWR, 0600);
+ if (fd < 0)
+ {
+ return check_failed(test, "create");
+ }
+
+ /* Extend length, so that there's a really big set of zero
+ * blocks.
+ */
+ seek = lseek(fd, len, SEEK_SET);
+ if (seek != len)
+ {
+ error = check_failed(test, "lseek len");
+ goto out_close;
+ }
+ error = write(fd, &fd, 1);
+ if (error < 0)
+ {
+ error = check_failed(test, "write");
+ goto out_close;
+ }
+
+ /* Verify that the first few bytes are zero. This should be
+ * true, since the file should be sparse.
+ */
+ seek = lseek(fd, 0, SEEK_SET);
+ if (seek != 0)
+ {
+ error = check_failed(test, "lseek begin");
+ goto out_close;
+ }
+ error = read(fd, buf, strlen(teststr));
+ if (error != (ssize_t)strlen(teststr))
+ {
+ error = check_failed(test, "read zeros");
+ goto out_close;
+ }
+ for (ii = strlen(teststr), tmpstr = buf; ii; ii--)
+ {
+ if (*tmpstr++)
+ {
+ fprintf(stderr, "%s: verify zeros failed\n", test);
+ error = 1;
+ goto out_close;
+ }
+ }
+
+ /* Map file.
+ */
+ map = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (map == MAP_FAILED)
+ {
+ error = check_failed(test, "mmap");
+ goto out_close;
+ }
+
+ /* Write to first page of the mapping, which should be a zero
+ * block.
+ */
+ map_ch = (char *)map;
+ tmpstr = teststr;
+ while (*tmpstr)
+ *map_ch++ = *tmpstr++;
+
+ /* Unmap the file.
+ */
+ if (munmap(map, len) < 0)
+ {
+ error = check_failed(test, "munmap");
+ }
+
+ /* Read from the first page.
+ */
+ seek = lseek(fd, 0, SEEK_SET);
+ if (seek != 0)
+ {
+ error = check_failed(test, "lseek begin");
+ goto out_close;
+ }
+ error = read(fd, buf, strlen(teststr));
+ if (error != (ssize_t)strlen(teststr))
+ {
+ error = check_failed(test, "read teststr");
+ goto out_close;
+ }
+ buf[strlen(teststr)] = 0;
+
+ /* Verify the data.
+ */
+ if (strcmp(teststr, buf))
+ {
+ fprintf(stderr, "%s: verify data failed\n", test);
+ fprintf(stderr, "%s: teststr \"%s\", buf \"%s\"\n", test, teststr, buf);
+ error = 1;
+ goto out_close;
+ }
+
+ error = 0;
+out_close:
+ if (close(fd) < 0)
+ {
+ error = check_failed(test, "close");
+ }
+ if (unlink(tmpfile) < 0)
+ {
+ error = check_failed(test, "unlink");
+ }
+ return error;
+}
+
+static int check_run_one(cmd_t *cmd, ioenv_t *io)
+{
+ int retval;
+ char *argv[2];
+
+ argv[0] = (char *)cmd->cmd_name;
+ argv[1] = NULL;
+
+ fprintf(stdout, "%10s ... ", cmd->cmd_name);
+ fflush(NULL);
+ retval = (*cmd->cmd_func)(1, argv, io);
+ fprintf(stdout, "%s\n", retval ? "FAILED" : "SUCCESS");
+
+ return retval;
+}
+
+DECL_CMD(check)
+{
+ int argn;
+ cmd_t *cmd;
+ int errors;
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "usage: check <test> [...]\n\n");
+
+ fprintf(stderr, "Where <test> is either \"all\" or one of:\n");
+ for (cmd = check_cmds; cmd->cmd_name; cmd++)
+ {
+ fprintf(stderr, "%20s - %s\n", cmd->cmd_name, cmd->cmd_helptext);
+ }
+ fprintf(stderr, "\n");
+ return 1;
+ }
+
+ errors = 0;
+
+ fprintf(stdout, "Running tests:\n");
+
+ if (argc == 2 && !strcmp(argv[1], "all"))
+ {
+ for (cmd = check_cmds; cmd->cmd_name; cmd++)
+ {
+ errors += check_run_one(cmd, io);
+ }
+ return errors;
+ }
+
+ for (argn = 1; argn < argc; argn++)
+ {
+ const char *test;
+
+ test = argv[argn];
+
+ for (cmd = check_cmds; cmd->cmd_name; cmd++)
+ {
+ if (!strcmp(cmd->cmd_name, test))
+ {
+ break;
+ }
+ }
+ if (!cmd->cmd_name)
+ {
+ fprintf(stderr, "Unknown test: %s\n", test);
+ errors++;
+ continue;
+ }
+
+ errors += check_run_one(cmd, io);
+ }
+
+ return errors;
+}
+
+DECL_CMD(env)
+{
+ int i = 0;
+ if (my_envp)
+ {
+ while (my_envp[i])
+ {
+ printf("env: %s\n", my_envp[i++]);
+ }
+ }
+ return 0;
+}
+
+DECL_CMD(sync)
+{
+ sync();
+ return 0;
+}
+
+static int do_cp(ioenv_t *io, const char *cmd, const char *in_file, int in_fd,
+ const char *out_file, int out_fd)
+{
+#define buffer_sz 32768
+
+ static char buffer[buffer_sz];
+ int nbytes_in;
+ int nbytes_out;
+
+ if (is_std_stream(in_fd))
+ {
+ in_fd = io->io_map_fd[in_fd];
+ }
+ if (is_std_stream(out_fd))
+ {
+ out_fd = io->io_map_fd[out_fd];
+ }
+
+ while ((nbytes_in = read(in_fd, buffer, buffer_sz)) > 0)
+ {
+ if ((nbytes_out = write(out_fd, buffer, nbytes_in)) < 0)
+ {
+ fprintf(stderr, "%s: unable to write to %s: %s\n", cmd, out_file,
+ strerror(errno));
+ return 0;
+ }
+ }
+ if (nbytes_in < 0)
+ {
+ fprintf(stderr, "%s: unable to read from %s: %s\n", cmd, in_file,
+ strerror(errno));
+ return 0;
+ }
+ return 1;
+
+#undef buffer_sz
+}
+
+DECL_CMD(cp)
+{
+ const char *src;
+ const char *dest;
+ int src_fd;
+ int dest_fd;
+ int error;
+
+ if (argc != 3)
+ {
+ fprintf(stderr, "usage: cp <src> <dest>\n");
+ return 1;
+ }
+
+ src = argv[1];
+ dest = argv[2];
+
+ src_fd = open(src, O_RDONLY, 0);
+ if (src_fd < 0)
+ {
+ fprintf(stderr, "cp: unable to open %s: %s\n", src, strerror(errno));
+ return 1;
+ }
+
+ dest_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (dest_fd < 0)
+ {
+ fprintf(stderr, "cp: unable to open %s: %s\n", dest, strerror(errno));
+ return 1;
+ }
+
+ error = 0;
+ if (!do_cp(io, "cp", src, src_fd, dest, dest_fd))
+ {
+ error = 1;
+ }
+
+ close(src_fd);
+ close(dest_fd);
+ return error;
+}
+
+DECL_CMD(echo)
+{
+ int argn;
+ int out_fd = io->io_map_fd[STDOUT_FILENO];
+ ssize_t error;
+
+ // Note: We use 128 here because it is the size of the line discipline buffer.
+ // I.e., as large as any of the arguments to echo could be.
+#define ECHO_BUF_SIZE 128
+ char buf[ECHO_BUF_SIZE];
+
+ // If *no* arguments are passed to echo, write a single newline.
+ if (argc == 1)
+ {
+ error = write(out_fd, "\n", 1);
+ if (error < 0)
+ {
+ fprintf(stderr, "echo: unable to write `\\n`\n");
+ return error;
+ }
+ }
+
+ // Loop over provided arguments, printing each with a trailing space (except
+ // the final argument, which has a trailing newline)
+ for (argn = 1; argn < argc; argn++)
+ {
+ char *trailing = argn == argc - 1 ? "\n" : " ";
+ snprintf(buf, ECHO_BUF_SIZE, "%s%s", argv[argn], trailing);
+
+ error = write(out_fd, buf, strlen(buf));
+ if (error < 0)
+ {
+ fprintf(stderr, "echo: unable to write `%s`\n", buf);
+ return error;
+ }
+ }
+
+#undef ECHO_BUF_SIZE
+ return 0;
+}
+
+DECL_CMD(cat)
+{
+ const char *file;
+ int argn;
+ int fd;
+ int error;
+
+ if (argc == 1)
+ {
+ return !do_cp(io, "cat", "<stdin>", 0, "<stdout>", 1);
+ }
+
+ error = 0;
+
+ for (argn = 1; argn < argc; argn++)
+ {
+ file = argv[argn];
+ fd = open(file, O_RDONLY, 0);
+ if (fd < 0)
+ {
+ fprintf(stderr, "cat: unable to open %s: %s\n", file,
+ strerror(errno));
+ error = 1;
+ continue;
+ }
+ if (!do_cp(io, "cat", file, fd, "<stdout>", 1))
+ {
+ error = 1;
+ }
+ close(fd);
+ }
+
+ return error;
+}
+
+DECL_CMD(mv)
+{
+ const char *src;
+ const char *dest;
+
+ if (argc != 3)
+ {
+ fprintf(stderr, "usage: mv <src> <dest>\n");
+ return 1;
+ }
+
+ src = argv[1];
+ dest = argv[2];
+
+ if (rename(src, dest) < 0)
+ {
+ fprintf(stderr, "mv: unable to move %s to %s: %s\n", src, dest,
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+DECL_CMD(rm)
+{
+ int argn;
+ const char *file;
+ int error = 0;
+
+ if (argc == 1)
+ {
+ fprintf(stderr, "usage: rm <file> [...]\n");
+ return 1;
+ }
+
+ for (argn = 1; argn < argc; argn++)
+ {
+ file = argv[argn];
+ if (unlink(file) < 0)
+ {
+ fprintf(stderr, "rm: unable to remove %s: %s\n", file,
+ strerror(errno));
+ error = 1;
+ }
+ }
+ return error;
+}
+
+DECL_CMD(ln)
+{
+ const char *src;
+ const char *dest;
+
+ if (argc != 3)
+ {
+ fprintf(stderr, "usage: ln <src> <dest>\n");
+ return 1;
+ }
+
+ src = argv[1];
+ dest = argv[2];
+
+ if (link(src, dest) < 0)
+ {
+ fprintf(stderr, "ln: couldn't link %s to %s: %s\n", dest, src,
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+DECL_CMD(mkdir)
+{
+ const char *dir;
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "usage: mkdir <directory>\n");
+ return 1;
+ }
+
+ dir = argv[1];
+ if (mkdir(dir, 0777) < 0)
+ {
+ fprintf(stderr, "mkdir: couldn't create %s: %s\n", dir,
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+DECL_CMD(clear)
+{
+#define ESC "\x1B"
+ fprintf(stdout, ESC "[H" ESC "[J");
+#undef ESC
+ return 0;
+}
+
+DECL_CMD(rmdir)
+{
+ const char *dir;
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "usage: rmdir <directory>\n");
+ return 1;
+ }
+
+ dir = argv[1];
+ if (rmdir(dir) < 0)
+ {
+ fprintf(stderr, "rmdir: couldn't remove %s: %s\n", dir,
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+DECL_CMD(exit)
+{
+ exit(0);
+ return 1;
+}
+
+DECL_CMD(help)
+{
+ cmd_t *cmd;
+
+ fprintf(stdout, "Shell commands:\n");
+
+ for (cmd = builtin_cmds; cmd->cmd_name; cmd++)
+ {
+ fprintf(stdout, "%20s - %s\n", cmd->cmd_name, cmd->cmd_helptext);
+ }
+
+ return 0;
+}
+
+DECL_CMD(cd)
+{
+ const char *dir;
+
+ if (argc > 2)
+ {
+ fprintf(stderr, "usage: cd <dir>\n");
+ return 1;
+ }
+
+ if (argc == 1)
+ {
+ dir = HOME;
+ }
+ else
+ {
+ dir = argv[1];
+ }
+
+ if (chdir(dir) < 0)
+ {
+ fprintf(stderr, "sh: couldn't cd to %s: %s\n", dir, strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+DECL_CMD(repeat)
+{
+ long ntimes;
+
+ if (argc < 3)
+ {
+ fprintf(stderr, "usage: repeat <ntimes> command [args ...]\n");
+ return 1;
+ }
+
+ ntimes = strtol(argv[1], NULL, 10);
+ if (ntimes <= 0)
+ {
+ fprintf(stderr, "repeat: <ntimes> must be non-zero\n");
+ return 1;
+ }
+
+ while (ntimes--)
+ {
+ redirect_map_t map;
+ int ii;
+
+ char **new_argv = copy_args(argc - 2, &argv[2]);
+ map.rm_nfds = 0;
+
+ for (ii = 0; ii < 3; ii++)
+ {
+ int fd;
+
+ fd = dup(io->io_map_fd[ii]);
+ if (fd < 0)
+ {
+ fprintf(stderr,
+ "repeat: dup(%d) failed: "
+ "%s\n",
+ io->io_map_fd[ii], strerror(errno));
+ return 1;
+ }
+
+ add_redirect(&map, fd, ii);
+ }
+
+ execute(argc - 2, new_argv, &map);
+
+ free_args(argc - 2, new_argv);
+ }
+
+ return 0;
+}
+
+DECL_CMD(parallel)
+{
+ int i, cmdbegin, ncmds = 0;
+ char **cmd_argvs[32];
+ int cmd_argcs[32];
+ int cmd_pids[32];
+
+ if (argc < 2)
+ {
+ fprintf(stderr,
+ "usage: parallel <cmd1> [args] -- <cmd2> [args] [-- ...]\n");
+ return 1;
+ }
+
+ /* Parse commands, dividing up argv */
+ for (cmdbegin = (i = 1); i < argc; i++)
+ {
+ if (!strcmp(argv[i], "--"))
+ {
+ /* Command delimiter - make sure command non-empty */
+ if (cmdbegin == i)
+ {
+ fprintf(stderr, "empty command\n");
+ return 1;
+ }
+ argv[i] = NULL;
+
+ cmd_argcs[ncmds] = i - cmdbegin;
+ cmd_argvs[ncmds] = &argv[cmdbegin];
+ ncmds++;
+ if (ncmds > 32)
+ {
+ fprintf(stderr, "too many commands\n");
+ return 1;
+ }
+ cmdbegin = i + 1;
+ }
+ }
+ if (cmdbegin == argc)
+ {
+ fprintf(stderr, "empty command\n");
+ return 1;
+ }
+ cmd_argcs[ncmds] = argc - cmdbegin;
+ cmd_argvs[ncmds] = &argv[cmdbegin];
+ ncmds++;
+ if (ncmds > 32)
+ {
+ fprintf(stderr, "too many commands\n");
+ return 1;
+ }
+
+ /* Fork and execute each command */
+ for (i = 0; i < ncmds; i++)
+ {
+ if (0 == (cmd_pids[i] = fork()))
+ {
+ int status, fd, ii;
+ /* Build weird map thing (as in repeat) */
+ redirect_map_t map;
+ map.rm_nfds = 0;
+ for (ii = 0; ii < 3; ii++)
+ {
+ if (0 > (fd = dup(io->io_map_fd[ii])))
+ {
+ exit(1);
+ }
+ add_redirect(&map, fd, ii);
+ }
+ /* Execute and return its status */
+ exit(execute(cmd_argcs[i], cmd_argvs[i], &map));
+ }
+ }
+ /* Wait for each command */
+ int status;
+ for (i = 0; i < ncmds; i++)
+ {
+ wait(&status);
+ }
+ /* Return last status */
+ return status;
+}
+
+DECL_CMD(time)
+{
+ if (argc < 1)
+ {
+ fprintf(stderr, "usage: time [cmd]\n");
+ return 1;
+ }
+ if (argc == 1)
+ {
+ fprintf(stdout, "time: %lu units\n", time(NULL));
+ return 0;
+ }
+
+ redirect_map_t map;
+ ;
+ map.rm_nfds = 0;
+ for (unsigned i = 0; i < 3; i++)
+ {
+ int fd = dup(io->io_map_fd[i]);
+ if (fd < 0)
+ exit(fd);
+ add_redirect(&map, fd, i);
+ }
+
+ time_t start = time(NULL);
+ int ret = execute(argc - 1, &argv[1], &map);
+ time_t end = time(NULL);
+
+ fprintf(stdout, "time: %lu units\n", end - start);
+ return ret;
+}
+
+static int do_redirect(redirect_map_t *map)
+{
+ int ii;
+ int newfd, oldfd;
+
+ for (ii = 0; ii < map->rm_nfds; ii++)
+ {
+ oldfd = map->rm_redir[ii].r_sfd;
+ newfd = map->rm_redir[ii].r_dfd;
+
+ dbg((stderr, "do_redirect: dup2(%d,%d)\n", oldfd, newfd));
+
+ if (dup2(oldfd, newfd) < 0)
+ {
+ fprintf(stderr,
+ "do_redirect: dup2() failed: "
+ "%s\n",
+ strerror(errno));
+ return -1;
+ }
+ close(oldfd);
+ }
+ return 0;
+}
+
+static void cleanup_redirects(redirect_map_t *map)
+{
+ int ii;
+
+ for (ii = 0; ii < map->rm_nfds; ii++)
+ {
+ close(map->rm_redir[ii].r_sfd);
+ }
+}
+
+static void build_ioenv(redirect_map_t *map, ioenv_t *io)
+{
+ int ii;
+
+ /* Map stdin, stdout, stderr to themselves. */
+ for (ii = 0; ii < 3; ii++)
+ {
+ io->io_map_fd[ii] = ii;
+ }
+
+ /* Execute redirection mappings. */
+ for (ii = 0; ii < map->rm_nfds; ii++)
+ {
+ int sfd = map->rm_redir[ii].r_sfd;
+ int dfd = map->rm_redir[ii].r_dfd;
+ if (dfd >= 0 && dfd <= 2)
+ {
+ io->io_map_fd[dfd] = sfd;
+ }
+ }
+}
+
+static void destroy_ioenv(ioenv_t *io) {}
+
+static int builtin_exec(cmd_t *cmd, int argc, char *argv[], ioenv_t *io)
+{
+ return (*cmd->cmd_func)(argc, argv, io);
+}
+
+static int execute(int argc, char *argv[], redirect_map_t *map)
+{
+ int status, pid;
+ cmd_t *cmd;
+
+ for (cmd = builtin_cmds; cmd->cmd_name; cmd++)
+ {
+ if (!strcmp(cmd->cmd_name, argv[0]))
+ {
+ break;
+ }
+ }
+ if (cmd->cmd_name)
+ {
+ ioenv_t io;
+
+ build_ioenv(map, &io);
+ status = builtin_exec(cmd, argc, argv, &io);
+ destroy_ioenv(&io);
+ cleanup_redirects(map);
+ return 0;
+ }
+
+ if (!(pid = fork()))
+ {
+ if (do_redirect(map) < 0)
+ {
+ exit(1);
+ }
+
+ execve(argv[0], argv, my_envp);
+
+ char *search_directories[] = {"/usr/bin/", "/bin/", "/sbin/"};
+ char buf[256];
+
+ for (unsigned i = 0;
+ errno == ENOENT && i < sizeof(search_directories) / sizeof(char *);
+ i++)
+ {
+ snprintf(buf, sizeof(buf), "%s/%s", search_directories[i], argv[0]);
+ execve(buf, argv, my_envp);
+ }
+ if (errno == ENOENT)
+ {
+ fprintf(stderr, "sh: command not found: %s\n", argv[0]);
+ }
+ else
+ {
+ fprintf(stderr, "sh: exec failed for %s: %s\n", argv[0],
+ strerror(errno));
+ }
+ exit(errno);
+ }
+ else
+ {
+ if (0 > pid)
+ {
+ fprintf(stderr, "sh: fork failed errno = %d\n", errno);
+ }
+ }
+
+ cleanup_redirects(map);
+ int ret = wait(&status);
+ if (status == EFAULT)
+ {
+ fprintf(stderr, "sh: child process accessed invalid memory\n");
+ }
+ // free_args(argc, argv);
+
+ return ret;
+}
+
+#define sh_isredirect(ch) ((ch) == '>' || (ch) == '<')
+
+static int parse_redirect_dfd(char *line, char **start_p)
+{
+ char *start;
+
+ start = *start_p;
+
+ if (start == line)
+ {
+ return -1;
+ }
+
+ /* Skip redirect symbol. */
+ *start = 0;
+ start--;
+
+ /* Go backwards, skipping whitespace. */
+ while ((start != line) && isspace(*start))
+ start--;
+ if (start == line)
+ {
+ return -1;
+ }
+
+ /* Go backwards, scanning digits. */
+ while ((start != line) && isdigit(*start))
+ start--;
+ if (!(isspace(*start) || isdigit(*start)))
+ {
+ return -1;
+ }
+
+ /* This is the descriptor number. */
+ *start_p = start;
+ return strtol(start, NULL, 10);
+}
+
+static int redirect_default_fd(int type)
+{
+ if (type == '<')
+ {
+ return 0;
+ }
+ else if (type == '>')
+ {
+ return 1;
+ }
+ else
+ {
+ fprintf(stderr, "redirect_default_fd: Eh?\n");
+ return -1;
+ }
+}
+
+static void add_redirect(redirect_map_t *map, int sfd, int dfd)
+{
+ dbg((stderr, "add_redirect: %d -> %d\n", sfd, dfd));
+ map->rm_redir[map->rm_nfds].r_sfd = sfd;
+ map->rm_redir[map->rm_nfds].r_dfd = dfd;
+ ++map->rm_nfds;
+}
+
+static int parse_redirect_dup(char *line, redirect_map_t *map, int dfd,
+ int mode, char *start, char **end_p)
+{
+ int real_sfd, sfd;
+ char *sfdstr;
+
+ /* Skip whitespace. */
+ while (*start && isspace(*start))
+ start++;
+ if (!*start)
+ {
+ fprintf(stderr, "sh: bad redirect at end of line\n");
+ return -1;
+ }
+
+ sfdstr = start;
+
+ /* Scan digits. */
+ if (!isdigit(*start))
+ {
+ fprintf(stderr, "sh: parse error in dup redirect: 1\n");
+ return -1;
+ }
+ while (*start && isdigit(*start))
+ start++;
+ if (*start && !isspace(*start))
+ {
+ fprintf(stderr, "sh: parse error in dup redirect: 2\n");
+ return -1;
+ }
+
+ /* Got a descriptor. */
+ *start = 0;
+ real_sfd = strtol(sfdstr, NULL, 10);
+
+ dbg((stderr, "redirect_dup: %d -> %d\n", real_sfd, dfd));
+
+ sfd = dup(real_sfd);
+ if (sfd < 0)
+ {
+ fprintf(stderr, "sh: invalid file descriptor: %d\n", real_sfd);
+ return -1;
+ }
+
+ add_redirect(map, sfd, dfd);
+
+ *end_p = start + 1;
+ return 0;
+}
+
+static int parse_redirect_norm(char *line, redirect_map_t *map, int dfd,
+ int mode, char *start, char **end_p)
+{
+ int sfd;
+ char *path;
+
+ /* Skip initial whitespace. */
+ while (*start && isspace(*start))
+ start++;
+ if (!*start)
+ {
+ fprintf(stderr, "sh: bad redirect at end of line\n");
+ return -1;
+ }
+
+ path = start;
+
+ /* Scan pathname. */
+ while (*start && !isspace(*start))
+ start++;
+ *start = 0;
+
+ dbg((stderr, "redirect_norm: %s -> %d\n", path, dfd));
+
+ sfd = open(path, mode, 0666);
+ if (sfd < 0)
+ {
+ fprintf(stderr, "sh: unable to open %s: %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ add_redirect(map, sfd, dfd);
+
+ *end_p = start + 1;
+ return 0;
+}
+
+static int parse_redirects(char *line, redirect_map_t *map)
+{
+ char *tmp;
+
+ map->rm_nfds = 0;
+
+ tmp = line;
+ for (;;)
+ {
+ char *start, *end;
+ int type, dup, append;
+ int mode;
+ int dfd;
+
+ dup = 0;
+ append = 0;
+
+ /* Find first redirect symbol. */
+ while (*tmp && !sh_isredirect(*tmp))
+ tmp++;
+ if (!*tmp)
+ {
+ break;
+ }
+
+ start = tmp;
+ type = *tmp;
+
+ /* Parse the redirect.
+ */
+
+ /* Destination file descriptor.
+ */
+ dfd = parse_redirect_dfd(line, &start);
+ if (dfd < 0)
+ {
+ dfd = redirect_default_fd(type);
+ }
+
+ /* Look for append or dup.
+ */
+ tmp++;
+ if (*tmp == '>')
+ {
+ if (type != '>')
+ {
+ fprintf(stderr, "sh: parse error at %c%c\n", type, *tmp);
+ return -1;
+ }
+ append = 1;
+ tmp++;
+ }
+ if (*tmp == '&')
+ {
+ dup = 1;
+ tmp++;
+ }
+
+ /* Calculate open mode for file.
+ */
+ if (type == '<')
+ {
+ mode = O_RDONLY;
+ }
+ else if (type == '>')
+ {
+ mode = O_WRONLY | O_CREAT;
+ if (append)
+ {
+ mode |= O_APPEND;
+ }
+ else
+ {
+ mode |= O_TRUNC;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "sh: bad type in redirect: %c\n", type);
+ return -1;
+ }
+
+ /* Parse the rest of the redirection.
+ */
+ if (dup)
+ {
+ if (parse_redirect_dup(line, map, dfd, mode, tmp, &end) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ if (parse_redirect_norm(line, map, dfd, mode, tmp, &end) < 0)
+ {
+ return -1;
+ }
+ }
+
+ /* Clear the redirect from the string.
+ */
+ while (start < end)
+ *start++ = ' ';
+
+ tmp = end;
+ }
+
+ return 0;
+}
+
+static void parse(char *line)
+{
+ char *argv[ARGV_MAX];
+ int argc;
+ char *tmp;
+ size_t len;
+ redirect_map_t map;
+
+ argc = 0;
+ tmp = line;
+
+ len = strlen(line);
+ if (line[len - 1] == '\n')
+ {
+ line[len - 1] = 0;
+ }
+
+ if (parse_redirects(line, &map) < 0)
+ {
+ return;
+ }
+
+ for (;;)
+ {
+ /* Ignore leading whitespace.
+ */
+ while (*tmp && isspace(*tmp))
+ tmp++;
+ if (!*tmp)
+ {
+ break;
+ }
+
+ argv[argc++] = tmp;
+
+ /* Token is everything up to trailing whitespace.
+ */
+ while (*tmp && !isspace(*tmp))
+ tmp++;
+ if (!*tmp)
+ {
+ break;
+ }
+
+ /* Null-terminate token.
+ */
+ *tmp++ = 0;
+ }
+
+ argv[argc] = NULL;
+
+ if (!argc)
+ {
+ return;
+ }
+
+ execute(argc, argv, &map);
+}
+
+#define LINEBUF_SIZE 1024
+static char buf1[LINEBUF_SIZE] = {NULL};
+static char buf2[LINEBUF_SIZE] = {NULL};
+
+int main(int argc, char *argv[], char *envp[])
+{
+ static char *linebuf = buf1;
+ static char *prev_linebuf = buf2;
+
+ ssize_t nbytes;
+ char prompt[64];
+
+ my_envp = envp;
+
+ snprintf(prompt, sizeof(prompt), "weenix -> ");
+
+ fprintf(stdout, "%s", prompt);
+ fflush(NULL);
+ while ((nbytes = read(0, linebuf, LINEBUF_SIZE)) > 0)
+ {
+ linebuf[nbytes] = 0;
+
+ if (nbytes == 3 && linebuf[0] == '!' && linebuf[1] == '!' &&
+ linebuf[2] == '\n')
+ {
+ fprintf(stdout, "%s\n", prev_linebuf);
+ parse(prev_linebuf);
+ }
+ else
+ {
+ parse(linebuf);
+ char *tmp = linebuf;
+ linebuf = prev_linebuf;
+ prev_linebuf = tmp;
+ }
+ fprintf(stdout, "%s", prompt);
+ fflush(NULL);
+ }
+
+ fprintf(stdout, "exit\n");
+
+#ifdef __static__
+ exit(0);
+#endif
+ return 0;
+}
diff --git a/user/bin/sl.c b/user/bin/sl.c
new file mode 100644
index 0000000..a8f62c7
--- /dev/null
+++ b/user/bin/sl.c
@@ -0,0 +1,502 @@
+/*========================================
+ * sl.c: SL version 5.03
+ * Copyright 1993,1998,2014-2015
+ * Toyoda Masashi
+ * (mtoyoda@acm.org)
+ * Last Modified: 2014/06/03
+ *========================================
+ */
+/* sl version 5.03 : Fix some more compiler warnings. */
+/* by Ryan Jacobs 2015/01/19 */
+/* sl version 5.02 : Fix compiler warnings. */
+/* by Jeff Schwab 2014/06/03 */
+/* sl version 5.01 : removed cursor and handling of IO */
+/* by Chris Seymour 2014/01/03 */
+/* sl version 5.00 : add -c option */
+/* by Toyoda Masashi 2013/05/05 */
+/* sl version 4.00 : add C51, usleep(40000) */
+/* by Toyoda Masashi 2002/12/31 */
+/* sl version 3.03 : add usleep(20000) */
+/* by Toyoda Masashi 1998/07/22 */
+/* sl version 3.02 : D51 flies! Change options. */
+/* by Toyoda Masashi 1993/01/19 */
+/* sl version 3.01 : Wheel turns smoother */
+/* by Toyoda Masashi 1992/12/25 */
+/* sl version 3.00 : Add d(D51) option */
+/* by Toyoda Masashi 1992/12/24 */
+/* sl version 2.02 : Bug fixed.(dust remains in screen) */
+/* by Toyoda Masashi 1992/12/17 */
+/* sl version 2.01 : Smoke run and disappear. */
+/* Change '-a' to accident option. */
+/* by Toyoda Masashi 1992/12/16 */
+/* sl version 2.00 : Add a(all),l(long),F(Fly!) options. */
+/* by Toyoda Masashi 1992/12/15 */
+/* sl version 1.02 : Add turning wheel. */
+/* by Toyoda Masashi 1992/12/14 */
+/* sl version 1.01 : Add more complex smoke. */
+/* by Toyoda Masashi 1992/12/14 */
+/* sl version 1.00 : SL runs vomiting out smoke. */
+/* by Toyoda Masashi 1992/12/11 */
+
+/*========================================
+ * sl.h: SL version 5.02
+ * Copyright 1993,2002,2014
+ * Toyoda Masashi
+ * (mtoyoda@acm.org)
+ * Last Modified: 2014/06/03
+ *========================================
+ */
+#include <stdio.h>
+#include <unistd.h>
+
+#define ERR 0
+#define OK 1
+#define LINES 25
+#define COLS 80
+
+#define D51HEIGHT 10
+#define D51FUNNEL 7
+#define D51LENGTH 83
+#define D51PATTERNS 6
+
+#define D51STR1 " ==== ________ ___________ "
+#define D51STR2 " _D _| |_______/ \\__I_I_____===__|_________| "
+#define D51STR3 " |(_)--- | H\\________/ | | =|___ ___| "
+#define D51STR4 " / | | H | | | | ||_| |_|| "
+#define D51STR5 " | | | H |__--------------------| [___] | "
+#define D51STR6 " | ________|___H__/__|_____/[][]~\\_______| | "
+#define D51STR7 " |/ | |-----------I_____I [][] [] D |=======|__ "
+
+#define D51WHL11 "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ "
+#define D51WHL12 " |/-=|___|= || || || |_____/~\\___/ "
+#define D51WHL13 " \\_/ \\O=====O=====O=====O_/ \\_/ "
+
+#define D51WHL21 "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ "
+#define D51WHL22 " |/-=|___|=O=====O=====O=====O |_____/~\\___/ "
+#define D51WHL23 " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ "
+
+#define D51WHL31 "__/ =| o |=-O=====O=====O=====O \\ ____Y___________|__ "
+#define D51WHL32 " |/-=|___|= || || || |_____/~\\___/ "
+#define D51WHL33 " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ "
+
+#define D51WHL41 "__/ =| o |=-~O=====O=====O=====O\\ ____Y___________|__ "
+#define D51WHL42 " |/-=|___|= || || || |_____/~\\___/ "
+#define D51WHL43 " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ "
+
+#define D51WHL51 "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ "
+#define D51WHL52 " |/-=|___|= O=====O=====O=====O|_____/~\\___/ "
+#define D51WHL53 " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ "
+
+#define D51WHL61 "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ "
+#define D51WHL62 " |/-=|___|= || || || |_____/~\\___/ "
+#define D51WHL63 " \\_/ \\_O=====O=====O=====O/ \\_/ "
+
+#define D51DEL " "
+
+#define COAL01 " "
+#define COAL02 " "
+#define COAL03 " _________________ "
+#define COAL04 " _| \\_____A "
+#define COAL05 " =| | "
+#define COAL06 " -| | "
+#define COAL07 "__|________________________|_ "
+#define COAL08 "|__________________________|_ "
+#define COAL09 " |_D__D__D_| |_D__D__D_| "
+#define COAL10 " \\_/ \\_/ \\_/ \\_/ "
+
+#define COALDEL " "
+
+#define LOGOHEIGHT 6
+#define LOGOFUNNEL 4
+#define LOGOLENGTH 84
+#define LOGOPATTERNS 6
+
+#define LOGO1 " ++ +------ "
+#define LOGO2 " || |+-+ | "
+#define LOGO3 " /---------|| | | "
+#define LOGO4 " + ======== +-+ | "
+
+#define LWHL11 " _|--O========O~\\-+ "
+#define LWHL12 "//// \\_/ \\_/ "
+
+#define LWHL21 " _|--/O========O\\-+ "
+#define LWHL22 "//// \\_/ \\_/ "
+
+#define LWHL31 " _|--/~O========O-+ "
+#define LWHL32 "//// \\_/ \\_/ "
+
+#define LWHL41 " _|--/~\\------/~\\-+ "
+#define LWHL42 "//// \\_O========O "
+
+#define LWHL51 " _|--/~\\------/~\\-+ "
+#define LWHL52 "//// \\O========O/ "
+
+#define LWHL61 " _|--/~\\------/~\\-+ "
+#define LWHL62 "//// O========O_/ "
+
+#define LCOAL1 "____ "
+#define LCOAL2 "| \\@@@@@@@@@@@ "
+#define LCOAL3 "| \\@@@@@@@@@@@@@_ "
+#define LCOAL4 "| | "
+#define LCOAL5 "|__________________| "
+#define LCOAL6 " (O) (O) "
+
+#define LCAR1 "____________________ "
+#define LCAR2 "| ___ ___ ___ ___ | "
+#define LCAR3 "| |_| |_| |_| |_| | "
+#define LCAR4 "|__________________| "
+#define LCAR5 "|__________________| "
+#define LCAR6 " (O) (O) "
+
+#define DELLN " "
+
+#define C51HEIGHT 11
+#define C51FUNNEL 7
+#define C51LENGTH 87
+#define C51PATTERNS 6
+
+#define C51DEL " "
+
+#define C51STR1 " ___ "
+#define C51STR2 " _|_|_ _ __ __ ___________"
+#define C51STR3 " D__/ \\_(_)___| |__H__| |_____I_Ii_()|_________|"
+#define C51STR4 " | `---' |:: `--' H `--' | |___ ___| "
+#define C51STR5 " +|~~~~~~~~++::~~~~~~~H~~+=====+~~~~~~|~~||_| |_|| "
+#define C51STR6 " || | :: H +=====+ | |:: ...| "
+#define C51STR7 "| | _______|_::-----------------[][]-----| | "
+
+#define C51WH61 "| /~~ || |-----/~~~~\\ /[I_____I][][] --|||_______|__"
+#define C51WH62 "------'|oOo|==[]=- || || | ||=======_|__"
+#define C51WH63 "/~\\____|___|/~\\_| O=======O=======O |__|+-/~\\_| "
+#define C51WH64 "\\_/ \\_/ \\____/ \\____/ \\____/ \\_/ "
+
+#define C51WH51 "| /~~ || |-----/~~~~\\ /[I_____I][][] --|||_______|__"
+#define C51WH52 "------'|oOo|===[]=- || || | ||=======_|__"
+#define C51WH53 "/~\\____|___|/~\\_| O=======O=======O |__|+-/~\\_| "
+#define C51WH54 "\\_/ \\_/ \\____/ \\____/ \\____/ \\_/ "
+
+#define C51WH41 "| /~~ || |-----/~~~~\\ /[I_____I][][] --|||_______|__"
+#define C51WH42 "------'|oOo|===[]=- O=======O=======O | ||=======_|__"
+#define C51WH43 "/~\\____|___|/~\\_| || || |__|+-/~\\_| "
+#define C51WH44 "\\_/ \\_/ \\____/ \\____/ \\____/ \\_/ "
+
+#define C51WH31 "| /~~ || |-----/~~~~\\ /[I_____I][][] --|||_______|__"
+#define C51WH32 "------'|oOo|==[]=- O=======O=======O | ||=======_|__"
+#define C51WH33 "/~\\____|___|/~\\_| || || |__|+-/~\\_| "
+#define C51WH34 "\\_/ \\_/ \\____/ \\____/ \\____/ \\_/ "
+
+#define C51WH21 "| /~~ || |-----/~~~~\\ /[I_____I][][] --|||_______|__"
+#define C51WH22 "------'|oOo|=[]=- O=======O=======O | ||=======_|__"
+#define C51WH23 "/~\\____|___|/~\\_| || || |__|+-/~\\_| "
+#define C51WH24 "\\_/ \\_/ \\____/ \\____/ \\____/ \\_/ "
+
+#define C51WH11 "| /~~ || |-----/~~~~\\ /[I_____I][][] --|||_______|__"
+#define C51WH12 "------'|oOo|=[]=- || || | ||=======_|__"
+#define C51WH13 "/~\\____|___|/~\\_| O=======O=======O |__|+-/~\\_| "
+#define C51WH14 "\\_/ \\_/ \\____/ \\____/ \\____/ \\_/ "
+
+void add_smoke(int y, int x);
+void add_man(int y, int x);
+int add_C51(int x);
+int add_D51(int x);
+int add_sl(int x);
+void option(char *str);
+int my_mvaddstr(int y, int x, char *str);
+
+int ACCIDENT = 0;
+int LOGO = 0;
+int FLY = 0;
+int C51 = 0;
+
+int mvaddch(int y, int x, char c)
+{
+ printf("\033[%d;%dH%c", y, x, c);
+ // fflush(stdout);
+
+ return OK;
+}
+
+int my_mvaddstr(int y, int x, char *str)
+{
+ for (; x < 0; ++x, ++str)
+ if (*str == '\0')
+ return ERR;
+ for (; *str != '\0'; ++str, ++x)
+ if (mvaddch(y, x, *str) == ERR)
+ return ERR;
+ return OK;
+}
+
+void option(char *str)
+{
+ while (*str != '\0')
+ {
+ switch (*str++)
+ {
+ case 'a':
+ ACCIDENT = 1;
+ break;
+ case 'F':
+ FLY = 1;
+ break;
+ case 'l':
+ LOGO = 1;
+ break;
+ case 'c':
+ C51 = 1;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int x, i;
+
+ for (i = 1; i < argc; ++i)
+ {
+ if (*argv[i] == '-')
+ {
+ option(argv[i] + 1);
+ }
+ }
+
+ printf("\033[?25l");
+ // initscr();
+ // signal(SIGINT, SIG_IGN);
+ // noecho();
+ // curs_set(0);
+ // nodelay(stdscr, TRUE);
+ // leaveok(stdscr, TRUE);
+ // scrollok(stdscr, FALSE);
+
+ for (x = COLS - 1;; --x)
+ {
+ if (LOGO == 1)
+ {
+ if (add_sl(x) == ERR)
+ break;
+ }
+ else if (C51 == 1)
+ {
+ if (add_C51(x) == ERR)
+ break;
+ }
+ else
+ {
+ if (add_D51(x) == ERR)
+ break;
+ }
+ // getch();
+ // refresh();
+ fflush(stdout);
+ //size_t x = time(NULL);
+ // while(time(NULL) < x) {__asm__ ( "pause;" );}
+
+ /*for (int i = 0; i < 5000; i++)
+ {
+ __asm__("pause;");
+ }*/
+ usleep(200000);
+
+ printf("\033[2J\033[1;1H");
+ fflush(stdout);
+ }
+
+ printf("\033[?25h");
+ fflush(stdout);
+ // mvcur(0, COLS - 1, LINES - 1, 0);
+ // endwin();
+
+ return 0;
+}
+
+int add_sl(int x)
+{
+ static char *sl[LOGOPATTERNS][LOGOHEIGHT + 1] = {
+ {LOGO1, LOGO2, LOGO3, LOGO4, LWHL11, LWHL12, DELLN},
+ {LOGO1, LOGO2, LOGO3, LOGO4, LWHL21, LWHL22, DELLN},
+ {LOGO1, LOGO2, LOGO3, LOGO4, LWHL31, LWHL32, DELLN},
+ {LOGO1, LOGO2, LOGO3, LOGO4, LWHL41, LWHL42, DELLN},
+ {LOGO1, LOGO2, LOGO3, LOGO4, LWHL51, LWHL52, DELLN},
+ {LOGO1, LOGO2, LOGO3, LOGO4, LWHL61, LWHL62, DELLN}};
+
+ static char *coal[LOGOHEIGHT + 1] = {LCOAL1, LCOAL2, LCOAL3, LCOAL4,
+ LCOAL5, LCOAL6, DELLN};
+
+ static char *car[LOGOHEIGHT + 1] = {LCAR1, LCAR2, LCAR3, LCAR4,
+ LCAR5, LCAR6, DELLN};
+
+ int i, y, py1 = 0, py2 = 0, py3 = 0;
+
+ if (x < -LOGOLENGTH)
+ return ERR;
+ y = LINES / 2 - 3;
+
+ if (FLY == 1)
+ {
+ y = (x / 6) + LINES - (COLS / 6) - LOGOHEIGHT;
+ py1 = 2;
+ py2 = 4;
+ py3 = 6;
+ }
+ for (i = 0; i <= LOGOHEIGHT; ++i)
+ {
+ my_mvaddstr(y + i, x, sl[(LOGOLENGTH + x) / 3 % LOGOPATTERNS][i]);
+ my_mvaddstr(y + i + py1, x + 21, coal[i]);
+ my_mvaddstr(y + i + py2, x + 42, car[i]);
+ my_mvaddstr(y + i + py3, x + 63, car[i]);
+ }
+ if (ACCIDENT == 1)
+ {
+ add_man(y + 1, x + 14);
+ add_man(y + 1 + py2, x + 45);
+ add_man(y + 1 + py2, x + 53);
+ add_man(y + 1 + py3, x + 66);
+ add_man(y + 1 + py3, x + 74);
+ }
+ add_smoke(y - 1, x + LOGOFUNNEL);
+ return OK;
+}
+
+int add_D51(int x)
+{
+ static char *d51[D51PATTERNS][D51HEIGHT + 1] = {
+ {D51STR1, D51STR2, D51STR3, D51STR4, D51STR5, D51STR6, D51STR7,
+ D51WHL11, D51WHL12, D51WHL13, D51DEL},
+ {D51STR1, D51STR2, D51STR3, D51STR4, D51STR5, D51STR6, D51STR7,
+ D51WHL21, D51WHL22, D51WHL23, D51DEL},
+ {D51STR1, D51STR2, D51STR3, D51STR4, D51STR5, D51STR6, D51STR7,
+ D51WHL31, D51WHL32, D51WHL33, D51DEL},
+ {D51STR1, D51STR2, D51STR3, D51STR4, D51STR5, D51STR6, D51STR7,
+ D51WHL41, D51WHL42, D51WHL43, D51DEL},
+ {D51STR1, D51STR2, D51STR3, D51STR4, D51STR5, D51STR6, D51STR7,
+ D51WHL51, D51WHL52, D51WHL53, D51DEL},
+ {D51STR1, D51STR2, D51STR3, D51STR4, D51STR5, D51STR6, D51STR7,
+ D51WHL61, D51WHL62, D51WHL63, D51DEL}};
+ static char *coal[D51HEIGHT + 1] = {COAL01, COAL02, COAL03, COAL04,
+ COAL05, COAL06, COAL07, COAL08,
+ COAL09, COAL10, COALDEL};
+
+ int y, i, dy = 0;
+
+ if (x < -D51LENGTH)
+ return ERR;
+ y = LINES / 2 - 5;
+
+ if (FLY == 1)
+ {
+ y = (x / 7) + LINES - (COLS / 7) - D51HEIGHT;
+ dy = 1;
+ }
+ for (i = 0; i <= D51HEIGHT; ++i)
+ {
+ my_mvaddstr(y + i, x, d51[(D51LENGTH + x) % D51PATTERNS][i]);
+ my_mvaddstr(y + i + dy, x + 53, coal[i]);
+ }
+ if (ACCIDENT == 1)
+ {
+ add_man(y + 2, x + 43);
+ add_man(y + 2, x + 47);
+ }
+ add_smoke(y - 1, x + D51FUNNEL);
+ return OK;
+}
+
+int add_C51(int x)
+{
+ static char *c51[C51PATTERNS][C51HEIGHT + 1] = {
+ {C51STR1, C51STR2, C51STR3, C51STR4, C51STR5, C51STR6, C51STR7, C51WH11,
+ C51WH12, C51WH13, C51WH14, C51DEL},
+ {C51STR1, C51STR2, C51STR3, C51STR4, C51STR5, C51STR6, C51STR7, C51WH21,
+ C51WH22, C51WH23, C51WH24, C51DEL},
+ {C51STR1, C51STR2, C51STR3, C51STR4, C51STR5, C51STR6, C51STR7, C51WH31,
+ C51WH32, C51WH33, C51WH34, C51DEL},
+ {C51STR1, C51STR2, C51STR3, C51STR4, C51STR5, C51STR6, C51STR7, C51WH41,
+ C51WH42, C51WH43, C51WH44, C51DEL},
+ {C51STR1, C51STR2, C51STR3, C51STR4, C51STR5, C51STR6, C51STR7, C51WH51,
+ C51WH52, C51WH53, C51WH54, C51DEL},
+ {C51STR1, C51STR2, C51STR3, C51STR4, C51STR5, C51STR6, C51STR7, C51WH61,
+ C51WH62, C51WH63, C51WH64, C51DEL}};
+ static char *coal[C51HEIGHT + 1] = {COALDEL, COAL01, COAL02, COAL03,
+ COAL04, COAL05, COAL06, COAL07,
+ COAL08, COAL09, COAL10, COALDEL};
+
+ int y, i, dy = 0;
+
+ if (x < -C51LENGTH)
+ return ERR;
+ y = LINES / 2 - 5;
+
+ if (FLY == 1)
+ {
+ y = (x / 7) + LINES - (COLS / 7) - C51HEIGHT;
+ dy = 1;
+ }
+ for (i = 0; i <= C51HEIGHT; ++i)
+ {
+ my_mvaddstr(y + i, x, c51[(C51LENGTH + x) % C51PATTERNS][i]);
+ my_mvaddstr(y + i + dy, x + 55, coal[i]);
+ }
+ if (ACCIDENT == 1)
+ {
+ add_man(y + 3, x + 45);
+ add_man(y + 3, x + 49);
+ }
+ add_smoke(y - 1, x + C51FUNNEL);
+ return OK;
+}
+
+void add_man(int y, int x)
+{
+ static char *man[2][2] = {{"", "(O)"}, {"Help!", "\\O/"}};
+ int i;
+
+ for (i = 0; i < 2; ++i)
+ {
+ my_mvaddstr(y + i, x, man[(LOGOLENGTH + x) / 12 % 2][i]);
+ }
+}
+
+void add_smoke(int y, int x)
+#define SMOKEPTNS 16
+{
+ static struct smokes
+ {
+ int y, x;
+ int ptrn, kind;
+ } S[1000];
+ static int sum = 0;
+ static char *Smoke[2][SMOKEPTNS] = {
+ {"( )", "( )", "( )", "( )", "( )", "( )", "( )", "( )",
+ "()", "()", "O", "O", "O", "O", "O", " "},
+ {"(@@@)", "(@@@@)", "(@@@@)", "(@@@)", "(@@)", "(@@)", "(@)", "(@)",
+ "@@", "@@", "@", "@", "@", "@", "@", " "}};
+ static char *Eraser[SMOKEPTNS] = {
+ " ", " ", " ", " ", " ", " ", " ", " ",
+ " ", " ", " ", " ", " ", " ", " ", " "};
+ static int dy[SMOKEPTNS] = {2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ static int dx[SMOKEPTNS] = {-2, -1, 0, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 3, 3, 3};
+ int i;
+
+ if (x % 4 == 0)
+ {
+ for (i = 0; i < sum; ++i)
+ {
+ my_mvaddstr(S[i].y, S[i].x, Eraser[S[i].ptrn]);
+ S[i].y -= dy[S[i].ptrn];
+ S[i].x += dx[S[i].ptrn];
+ S[i].ptrn += (S[i].ptrn < SMOKEPTNS - 1) ? 1 : 0;
+ my_mvaddstr(S[i].y, S[i].x, Smoke[S[i].kind][S[i].ptrn]);
+ }
+ my_mvaddstr(y, x, Smoke[sum % 2][0]);
+ S[sum].y = y;
+ S[sum].x = x;
+ S[sum].ptrn = 0;
+ S[sum].kind = sum % 2;
+ sum++;
+ }
+} \ No newline at end of file
diff --git a/user/bin/sleep.c b/user/bin/sleep.c
new file mode 100644
index 0000000..6b0d485
--- /dev/null
+++ b/user/bin/sleep.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define SECONDS_TO_MICROSECONDS 1000000
+
+void help(int argc, char *argv[])
+{
+ fprintf(stderr, "usage: %s [-u (micrseconds)] <time>\n", argv[0]);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc > 3 || argc == 1)
+ {
+ help(argc, argv);
+ }
+
+ uint64_t tm = 0;
+
+ int use_usec = 0;
+ if (argc > 2)
+ {
+ if (strcmp(argv[1], "-u") == 0)
+ {
+ tm = atoi(argv[2]);
+ use_usec = 1;
+ }
+ else
+ {
+ help(argc, argv);
+ }
+ }
+
+ if (!use_usec)
+ {
+ tm = atoi(argv[1]);
+ tm *= SECONDS_TO_MICROSECONDS;
+ }
+
+ return usleep(tm);
+} \ No newline at end of file
diff --git a/user/bin/stat.c b/user/bin/stat.c
new file mode 100644
index 0000000..b9fe47f
--- /dev/null
+++ b/user/bin/stat.c
@@ -0,0 +1,49 @@
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+const char *modestr(int mode)
+{
+ switch (mode)
+ {
+ case S_IFCHR:
+ return "Character device";
+ case S_IFBLK:
+ return "Block device";
+ case S_IFDIR:
+ return "Directory";
+ case S_IFREG:
+ return "Regular file";
+ case S_IFLNK:
+ return "Symbolic link";
+ default:
+ return "Unknown";
+ }
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2)
+ {
+ printf("usage: stat file\n");
+ return 1;
+ }
+
+ stat_t ss;
+ int rc = stat(argv[1], &ss);
+ if (rc == -1)
+ {
+ printf("stat: %s\n", strerror(errno));
+ return 1;
+ }
+
+ printf(" File: %s\n", argv[1]);
+ printf(" Type: %s\n", modestr(ss.st_mode));
+ printf(" Inode: %d\n", ss.st_ino);
+ printf("Link count: %d\n", ss.st_nlink);
+ printf(" Size: %d\n", ss.st_size);
+ printf(" Blocks: %d\n", ss.st_blocks);
+ return 0;
+}
diff --git a/user/bin/uname.c b/user/bin/uname.c
new file mode 100644
index 0000000..942f330
--- /dev/null
+++ b/user/bin/uname.c
@@ -0,0 +1,93 @@
+/*
+ * FILE: uname.c
+ * AUTHOR: kma
+ * DESCR: uname. whee!
+ */
+
+#include <stdio.h>
+#include <sys/utsname.h>
+
+char *TAS =
+ "weenix brought to you by:\n"
+ "1998: jal, dep, kma, mcc, cd, tor\n"
+ "1999: mbe, tc, kma, mahrens, tor\n"
+ "2000: ahl, mahrens, mba, pdemoreu, it\n"
+ "2001: pgriess, pdemoreu, gc3, rmanches, dg\n"
+ "2002: kit, eschrock\n"
+ "2005: afenn\n"
+ "2006: afenn\n"
+ "2007: dap, joel\n"
+ "and the number 0xe\n";
+
+static int doflag(int c);
+
+static struct utsname un;
+
+int main(int argc, char **argv)
+{
+ int ii;
+
+ uname(&un);
+
+ for (ii = 1; ii < argc; ii++)
+ {
+ if (argv[ii][0] == '-')
+ {
+ char *str;
+ str = &argv[ii][1];
+ while (*str)
+ {
+ if (doflag(*str++) < 0)
+ {
+ goto usage;
+ }
+ }
+ }
+ }
+
+ if (argc == 1)
+ {
+ doflag('s');
+ }
+ fprintf(stdout, "\n");
+ return 0;
+
+usage:
+ return 1;
+}
+
+static int doflag(int c)
+{
+ switch (c)
+ {
+ case 'a':
+ printf("%s", TAS);
+ printf("%s ", un.sysname);
+ printf("%s ", un.nodename);
+ printf("%s ", un.release);
+ printf("%s ", un.version);
+ printf("%s ", un.machine);
+ break;
+ case 's':
+ printf("%s", un.sysname);
+ break;
+ case 'n':
+ printf("%s", un.nodename);
+ break;
+ case 'r':
+ printf("%s", un.release);
+ break;
+ case 'T':
+ printf("%s", TAS);
+ break;
+ case 'v':
+ printf("%s", un.version);
+ break;
+ case 'm':
+ printf("%s", un.machine);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}