From c63f340d90800895f007de64b7d2d14624263331 Mon Sep 17 00:00:00 2001 From: nthnluu Date: Sun, 28 Jan 2024 21:20:27 -0500 Subject: Created student weenix repository --- user/.gitignore | 8 + user/Makefile | 210 + user/README | 3 + user/bin/.gitignore | 4 + user/bin/ed.c | 1757 ++++++++ user/bin/hd.c | 90 + user/bin/ls.c | 108 + user/bin/sh.c | 1910 +++++++++ user/bin/sl.c | 502 +++ user/bin/sleep.c | 44 + user/bin/stat.c | 49 + user/bin/uname.c | 93 + user/hamlet | 7882 +++++++++++++++++++++++++++++++++++ user/include/ctype.h | 1 + user/include/dirent.h | 1 + user/include/errno.h | 1 + user/include/fcntl.h | 1 + user/include/limits.h | 1 + user/include/lseek.h | 1 + user/include/pthread/pthread.h | 137 + user/include/stdarg.h | 1 + user/include/stddef.h | 28 + user/include/stdio.h | 63 + user/include/stdlib.h | 60 + user/include/string.h | 47 + user/include/sys/mman.h | 1 + user/include/sys/stat.h | 1 + user/include/sys/types.h | 1 + user/include/sys/utsname.h | 1 + user/include/test/test.h | 51 + user/include/unistd.h | 101 + user/include/weenix/config.h | 1 + user/include/weenix/debug.h | 12 + user/include/weenix/syscall.h | 1 + user/include/weenix/trap.h | 26 + user/lib/ld-weenix/asm.h | 37 + user/lib/ld-weenix/bs_x86_64.S | 58 + user/lib/ld-weenix/elf.h | 2597 ++++++++++++ user/lib/ld-weenix/ldalloc.c | 66 + user/lib/ld-weenix/ldalloc.h | 4 + user/lib/ld-weenix/ldnames.c | 63 + user/lib/ld-weenix/ldnames.h | 23 + user/lib/ld-weenix/ldreloc_x86_64.c | 184 + user/lib/ld-weenix/ldresolve.c | 119 + user/lib/ld-weenix/ldresolve.h | 35 + user/lib/ld-weenix/ldstart.c | 573 +++ user/lib/ld-weenix/ldtypes.h | 60 + user/lib/ld-weenix/ldutil.c | 72 + user/lib/ld-weenix/ldutil.h | 39 + user/lib/ld-weenix/smacros.h | 40 + user/lib/libc/entry.S | 17 + user/lib/libc/errno.c | 3 + user/lib/libc/malloc.c | 1326 ++++++ user/lib/libc/printf.c | 169 + user/lib/libc/quad.c | 414 ++ user/lib/libc/rand.c | 22 + user/lib/libc/scanf.c | 73 + user/lib/libc/stream.c | 11 + user/lib/libc/string.c | 509 +++ user/lib/libc/strtol.c | 162 + user/lib/libc/syscall.c | 391 ++ user/lib/libc/vsnprintf.c | 566 +++ user/lib/libc/vsscanf.c | 447 ++ user/lib/libtest/test.c | 202 + user/sbin/.gitignore | 2 + user/sbin/halt.c | 7 + user/sbin/init.c | 115 + user/test/stuff | 3 + user/usr/bin/args.c | 36 + user/usr/bin/hello.c | 17 + user/usr/bin/kshell.c | 8 + user/usr/bin/segfault.S | 12 + user/usr/bin/spin.c | 10 + user/usr/bin/tests/eatinodes.c | 90 + user/usr/bin/tests/eatmem.c | 190 + user/usr/bin/tests/elf_test-64.c | 38 + user/usr/bin/tests/forkbomb.c | 84 + user/usr/bin/tests/forktest.c | 19 + user/usr/bin/tests/linkermagic.h | 232 ++ user/usr/bin/tests/memtest.c | 816 ++++ user/usr/bin/tests/mm.h | 1 + user/usr/bin/tests/page.h | 1 + user/usr/bin/tests/pipetest.c | 191 + user/usr/bin/tests/prime.c | 107 + user/usr/bin/tests/s5fstest.c | 332 ++ user/usr/bin/tests/stress.c | 476 +++ user/usr/bin/tests/vfstest.c | 1172 ++++++ user/usr/bin/wc.c | 114 + 88 files changed, 25553 insertions(+) create mode 100644 user/.gitignore create mode 100644 user/Makefile create mode 100644 user/README create mode 100644 user/bin/.gitignore create mode 100644 user/bin/ed.c create mode 100644 user/bin/hd.c create mode 100644 user/bin/ls.c create mode 100644 user/bin/sh.c create mode 100644 user/bin/sl.c create mode 100644 user/bin/sleep.c create mode 100644 user/bin/stat.c create mode 100644 user/bin/uname.c create mode 100644 user/hamlet create mode 120000 user/include/ctype.h create mode 120000 user/include/dirent.h create mode 120000 user/include/errno.h create mode 120000 user/include/fcntl.h create mode 120000 user/include/limits.h create mode 120000 user/include/lseek.h create mode 100644 user/include/pthread/pthread.h create mode 120000 user/include/stdarg.h create mode 100644 user/include/stddef.h create mode 100644 user/include/stdio.h create mode 100644 user/include/stdlib.h create mode 100644 user/include/string.h create mode 120000 user/include/sys/mman.h create mode 120000 user/include/sys/stat.h create mode 120000 user/include/sys/types.h create mode 120000 user/include/sys/utsname.h create mode 100644 user/include/test/test.h create mode 100644 user/include/unistd.h create mode 120000 user/include/weenix/config.h create mode 100644 user/include/weenix/debug.h create mode 120000 user/include/weenix/syscall.h create mode 100644 user/include/weenix/trap.h create mode 100644 user/lib/ld-weenix/asm.h create mode 100644 user/lib/ld-weenix/bs_x86_64.S create mode 100644 user/lib/ld-weenix/elf.h create mode 100644 user/lib/ld-weenix/ldalloc.c create mode 100644 user/lib/ld-weenix/ldalloc.h create mode 100644 user/lib/ld-weenix/ldnames.c create mode 100644 user/lib/ld-weenix/ldnames.h create mode 100644 user/lib/ld-weenix/ldreloc_x86_64.c create mode 100644 user/lib/ld-weenix/ldresolve.c create mode 100644 user/lib/ld-weenix/ldresolve.h create mode 100644 user/lib/ld-weenix/ldstart.c create mode 100644 user/lib/ld-weenix/ldtypes.h create mode 100644 user/lib/ld-weenix/ldutil.c create mode 100644 user/lib/ld-weenix/ldutil.h create mode 100644 user/lib/ld-weenix/smacros.h create mode 100644 user/lib/libc/entry.S create mode 100644 user/lib/libc/errno.c create mode 100644 user/lib/libc/malloc.c create mode 100644 user/lib/libc/printf.c create mode 100644 user/lib/libc/quad.c create mode 100644 user/lib/libc/rand.c create mode 100644 user/lib/libc/scanf.c create mode 100644 user/lib/libc/stream.c create mode 100644 user/lib/libc/string.c create mode 100644 user/lib/libc/strtol.c create mode 100644 user/lib/libc/syscall.c create mode 100644 user/lib/libc/vsnprintf.c create mode 100644 user/lib/libc/vsscanf.c create mode 100644 user/lib/libtest/test.c create mode 100644 user/sbin/.gitignore create mode 100644 user/sbin/halt.c create mode 100644 user/sbin/init.c create mode 100644 user/test/stuff create mode 100644 user/usr/bin/args.c create mode 100644 user/usr/bin/hello.c create mode 100644 user/usr/bin/kshell.c create mode 100644 user/usr/bin/segfault.S create mode 100644 user/usr/bin/spin.c create mode 100644 user/usr/bin/tests/eatinodes.c create mode 100644 user/usr/bin/tests/eatmem.c create mode 100644 user/usr/bin/tests/elf_test-64.c create mode 100644 user/usr/bin/tests/forkbomb.c create mode 100644 user/usr/bin/tests/forktest.c create mode 100644 user/usr/bin/tests/linkermagic.h create mode 100644 user/usr/bin/tests/memtest.c create mode 120000 user/usr/bin/tests/mm.h create mode 120000 user/usr/bin/tests/page.h create mode 100644 user/usr/bin/tests/pipetest.c create mode 100644 user/usr/bin/tests/prime.c create mode 100644 user/usr/bin/tests/s5fstest.c create mode 100644 user/usr/bin/tests/stress.c create mode 100644 user/usr/bin/tests/vfstest.c create mode 100644 user/usr/bin/wc.c (limited to 'user') diff --git a/user/.gitignore b/user/.gitignore new file mode 100644 index 0000000..e19f6c3 --- /dev/null +++ b/user/.gitignore @@ -0,0 +1,8 @@ +# cscope +cscope.files +cscope.in.out +cscope.out +cscope.po.out + +disk*.img +*.exec diff --git a/user/Makefile b/user/Makefile new file mode 100644 index 0000000..e2f0418 --- /dev/null +++ b/user/Makefile @@ -0,0 +1,210 @@ +SHELL := /bin/sh + +# XXX why is this here? +CFLAGS := + +include ../Global.mk + +DISK_IMAGE := disk0.img +STAGING_DIR := .staging + +.PHONY: all clean + +all: $(DISK_IMAGE) + +######## +# compile step +######## + +# CFLAGS_LIB: separate set of CFLAGS used for shared libraries (which, for +# example, need -fpic to build, but other executables can't be built with +# -fpic) +CFLAGS_LIB := $(CFLAGS) -fpic + +# XXX is this being defined multiple times? +ASFLAGS := -D__ASSEMBLY__ +ASFLAGS := $(ASFLAGS) -D__x86_64__ + + +# - all source files are compiled to an object file of the same name +# (no .c file and .S file shares the same prefix, so we have no conflicts) +# - for each .o, make picks the appropriate rule to generate it (only one of +# the two rules has dependencies that exist) +# - pass -fpic only to shared libraries + +lib/%.o: lib/%.c + @ echo " Compiling \"user/$<\"..." + @ $(CC) -c $< -o $@ $(CFLAGS_LIB) + +%.o: %.c + @ echo " Compiling \"user/$<\"..." + @ $(CC) -c $< -o $@ $(CFLAGS) + +lib/%.o: lib/%.S + @ echo " Compiling \"user/$<\"..." + @ $(CC) -c $< -o $@ $(ASFLAGS) $(CFLAGS_LIB) + +%.o: %.S + @ echo " Compiling \"user/$<\"..." + @ $(CC) -c $< -o $@ $(ASFLAGS) $(CFLAGS) + +######## +# link step for static libraries and shared libraries +######## +LDFLAGS := -m elf_x86_64 -z nodefaultlib -Llib + +# - there are 3 libraries: libc, ld-weenix, libtest +# - each library is built from the set of object files contained in its +# directory; this set is generated from the list of sources by replacing +# the .c suffix (or .S suffix) with a .o suffix +# - shared libraries are built with ld -shared, static libraries are built +# with ar + +# 1. libc + +LIBC_SOURCES := $(wildcard lib/libc/*.[cS]) +LIBC_OBJECTS := $(addsuffix .o,$(basename $(LIBC_SOURCES))) + +lib/libc.so: $(LIBC_OBJECTS) + @ echo " Linking for \"user/$@\"..." + @ $(LD) -o $@ $^ $(LDFLAGS) -shared -soname=/lib/libc.so \ +--dynamic-linker /lib/ld-weenix.so + +lib/libc.a: $(LIBC_OBJECTS) + @ echo " Creating \"user/$@\"..." + @ $(AR) crs $@ $^ + +# 2. libtest + +LIBTEST_SOURCES := $(wildcard lib/libtest/*.[cS]) +LIBTEST_OBJECTS := $(addsuffix .o,$(basename $(LIBTEST_SOURCES))) + +lib/libtest.so: $(LIBTEST_OBJECTS) lib/libc.so + @ echo " Linking for \"user/$@\"..." + @ $(LD) -o $@ $^ $(LDFLAGS) -shared -soname=/lib/libtest.so -lc + +lib/libtest.a: $(LIBTEST_OBJECTS) + @ echo " Creating \"user/$@\"..." + @ $(AR) crs $@ $^ + +# 3. ld-weenix + +LDWEENIX_SOURCES := $(wildcard lib/ld-weenix/*.[cS]) +LDWEENIX_OBJECTS := $(addsuffix .o,$(basename $(LDWEENIX_SOURCES))) + +lib/ld-weenix.so: $(LDWEENIX_OBJECTS) lib/libc.a + @ echo " Linking for \"user/$@\"..." + @ $(LD) -o $@ $(LDWEENIX_OBJECTS) $(LDFLAGS) -shared \ +-soname=/lib/ld-weenix.so -Bsymbolic -e _bootstrap -static -lc + +######## +# link step for executables +######## + +# - all executables are generated from a single .o file +# - executables are either statically or dynamically linked, and get built +# with a ".exec" suffix (the suffix makes the rule definition easier, and +# gets removed in the staging step) +# - all executables build with libc +# - statically-linked executables build with entry.o + +# 1. executables that require libtest: + +ifeq ($(DYNAMIC),0) +usr/bin/%.exec: usr/bin/tests/%.o lib/libc.a lib/libc/entry.o lib/libtest.a + @ echo " Linking for \"user/$@\" (static)..." + @ $(LD) -o $@ $< lib/libc/entry.o $(LDFLAGS) -static \ +-e __libc_static_entry -lc -ltest +else +usr/bin/%.exec: usr/bin/tests/%.o lib/libc.so lib/libtest.so + @ echo " Linking for \"user/$@\" (dynamic)..." + @ $(LD) -o $@ $< $(LDFLAGS) -e main --dynamic-linker /lib/ld-weenix.so \ +-lc -ltest +endif + +# 2. all other executables + +ifeq ($(DYNAMIC),0) +%.exec: %.o lib/libc.a lib/libc/entry.o + @ echo " Linking for \"user/$@\" (static)..." + @ $(LD) -o $@ $< lib/libc/entry.o $(LDFLAGS) -static \ +-e __libc_static_entry -lc +else +%.exec: %.o lib/libc.so + @ echo " Linking for \"user/$@\" (dynamic)..." + @ $(LD) -o $@ $< $(LDFLAGS) -e main --dynamic-linker /lib/ld-weenix.so \ +-lc +endif + +######## +# generate the disk image layout +######## + +# - below is an explicit list of files that will reside on the disk image +# - all of these files get staged in the staging directory +# - executables get their ".exec" suffix stripped off in this step + +# TODO also create make /tmp directory (some userspace test expects it) +BASE_TARGETS := README hamlet test/stuff +ifeq ($(DYNAMIC),0) +LIB_TARGETS := lib/libc.a lib/libtest.a +else +LIB_TARGETS := lib/ld-weenix.so lib/libc.a lib/libc.so lib/libtest.a \ +lib/libtest.so +endif + +EXEC_TARGETS := bin/ed bin/ls bin/sl bin/sleep bin/sh bin/uname bin/hd bin/stat \ +sbin/halt sbin/init \ +usr/bin/args usr/bin/hello usr/bin/kshell usr/bin/segfault usr/bin/spin \ +usr/bin/eatmem usr/bin/forkbomb usr/bin/memtest usr/bin/stress usr/bin/vfstest \ +usr/bin/wc usr/bin/forktest usr/bin/eatinodes usr/bin/pipetest usr/bin/s5fstest \ +usr/bin/elf_test-64 usr/bin/prime +DIR_TARGETS := tmp + +EXEC_SUFFIX := .exec +EXEC_TARGETS_WITH_SUFFIX := $(addsuffix $(EXEC_SUFFIX),$(EXEC_TARGETS)) + +# don't include any executables in the disk until VM +ifeq ($(VM),1) +TARGETS := $(BASE_TARGETS) $(LIB_TARGETS) $(EXEC_TARGETS_WITH_SUFFIX) $(DIR_TARGETS) +else +TARGETS := $(BASE_TARGETS) +endif + +# TODO get rid of "mkdir -p", "cp --parents" (they are not portable) +$(STAGING_DIR): $(TARGETS) + @ echo " Staging initial disk contents..." + @ mkdir -p $(STAGING_DIR) + @ touch $(STAGING_DIR) + @ cp --parents --recursive -- $? $(STAGING_DIR) +# below: strip .exec suffix from binaries +# (portable implementation of "rename 's/\.exec//' $?") + @ cd $(STAGING_DIR) \ +&& for i in `ls $? | grep "\.exec"`; do \ +mv $$i `echo $$i | cut -f1 -d.`; \ +done + +$(DIR_TARGETS) : + @ echo " Creating directory \"$@\"..." + @ mkdir -p $@ + +######## +# build the disk image +######## + +$(DISK_IMAGE): $(STAGING_DIR) + @ echo " Running fsmaker to create \"user/$@\"..." + @ echo " Disk Blocks: $(DISK_BLOCKS)" + @ echo " Disk Inodes: $(DISK_INODES)" + @ $(PYTHON) ../tools/fsmaker/sh.py $@ -e "format -b $(DISK_BLOCKS) -i $(DISK_INODES) -d $<" + @ rm "../$(DISK_IMAGE)" 2>/dev/null && echo " Removing obsolete $(DISK_IMAGE)" || true + +######## +# clean +######## + +clean: + find . -name "*.o" -type f -delete + rm -f $(DISK_IMAGE) $(LIB_TARGETS) $(EXEC_TARGETS_WITH_SUFFIX) + rmdir $(DIR_TARGETS) || true + rm -rf $(STAGING_DIR) diff --git a/user/README b/user/README new file mode 100644 index 0000000..d7f3b01 --- /dev/null +++ b/user/README @@ -0,0 +1,3 @@ +Congratulations! If you're reading this using your own +file system, you've made it pretty far. Keep on truckin'. + --Your staff 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 +#include +#include +#include + +/* + * 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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#include + +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 +#include +#include +#include +#include +#include +#include +#include + +#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 [...]\n\n"); + + fprintf(stderr, "Where 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 \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", "", 0, "", 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, "", 1)) + { + error = 1; + } + close(fd); + } + + return error; +} + +DECL_CMD(mv) +{ + const char *src; + const char *dest; + + if (argc != 3) + { + fprintf(stderr, "usage: mv \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 [...]\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 \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 \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 \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 \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 command [args ...]\n"); + return 1; + } + + ntimes = strtol(argv[1], NULL, 10); + if (ntimes <= 0) + { + fprintf(stderr, "repeat: 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 [args] -- [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 +#include + +#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 +#include +#include +#include + +#define SECONDS_TO_MICROSECONDS 1000000 + +void help(int argc, char *argv[]) +{ + fprintf(stderr, "usage: %s [-u (micrseconds)]