From: Ben Pfaff <blp@cs.stanford.edu>
Date: Mon, 1 Nov 2004 05:33:30 +0000 (+0000)
Subject: Add more tests.
X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ae20a83cc5ed14f276f2292d99079370562addc9;p=pintos-anon

Add more tests.
Fix - in makefile var bug reported by Jim.
Make pintos util able to format and put in one step.
---

diff --git a/grading/userprog/.cvsignore b/grading/userprog/.cvsignore
index a438335..73d1273 100644
--- a/grading/userprog/.cvsignore
+++ b/grading/userprog/.cvsignore
@@ -1 +1,2 @@
 *.d
+*.dsk
diff --git a/grading/userprog/Makefile b/grading/userprog/Makefile
index 25ba857..e95e13f 100644
--- a/grading/userprog/Makefile
+++ b/grading/userprog/Makefile
@@ -1,12 +1,17 @@
 SRCDIR = ../../src
 
 SINGLETONS = \
+	$(addprefix args-, argc argv0 argvn single multiple dbl-space) \
+	$(addprefix sc-, bad-sp bad-arg boundary) \
+	halt exit \
 	$(addprefix create-, normal empty null bad-ptr long exists bound) \
-	$(addprefix args-, argc argv0 argvn single multiple dbl-space)
+	$(addprefix open-, normal missing boundary empty null bad-ptr twice) \
+	$(addprefix close-, normal twice stdin stdout bad-fd) \
+	$(addprefix read-, normal bad-ptr boundary zero stdout bad-fd)
 
 define SINGLETON_PROG
 PROGS += $(1)
-$(1)_SRC = $(1).c
+$(subst -,_,$(1))_SRC = $(1).c
 endef
 
 $(foreach prog,$(SINGLETONS),$(eval $(call SINGLETON_PROG,$(prog))))
@@ -14,14 +19,8 @@ DISKS = $(patsubst %,%.dsk,$(PROGS))
 
 disks: $(DISKS)
 
-PINTOS = ../../src/utils/pintos
-OS_DISK = ../../src/userprog/build/os.dsk
 %.dsk: %
-	rm -f $@.tmp
-	$(PINTOS) make-disk $@.tmp 2
-	$(PINTOS) -v --os-disk=$(OS_DISK) --fs-disk=$@.tmp run -f -q
-	$(PINTOS) -v --os-disk=$(OS_DISK) --fs-disk=$@.tmp put $<
-	mv $@.tmp $@
+	./prep-disk $<
 
 clean::
 	rm -f $(DISKS)
diff --git a/grading/userprog/close-bad-fd.c b/grading/userprog/close-bad-fd.c
new file mode 100644
index 0000000..cdea3c5
--- /dev/null
+++ b/grading/userprog/close-bad-fd.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  printf ("(close-bad-fd) begin\n");
+  close (0xc0101234);
+  printf ("(close-bad-fd) end\n");
+  return 0;
+}
diff --git a/grading/userprog/close-bad-fd.exp b/grading/userprog/close-bad-fd.exp
new file mode 100644
index 0000000..5ac59da
--- /dev/null
+++ b/grading/userprog/close-bad-fd.exp
@@ -0,0 +1,6 @@
+(close-bad-fd) begin
+(close-bad-fd) end
+close-bad-fd: exit(0)
+--OR--
+(close-bad-fd) begin
+close-bad-fd: exit(-1)
diff --git a/grading/userprog/close-normal.c b/grading/userprog/close-normal.c
new file mode 100644
index 0000000..2b5e95d
--- /dev/null
+++ b/grading/userprog/close-normal.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  int handle;
+  printf ("(close-normal) begin\n");
+  handle = open ("sample.txt");
+  if (handle < 2)
+    printf ("(close-normal) fail: open() returned %d\n", handle);
+  close (handle);
+  printf ("(close-normal) end\n");
+  return 0;
+}
diff --git a/grading/userprog/close-normal.exp b/grading/userprog/close-normal.exp
new file mode 100644
index 0000000..f3be9bc
--- /dev/null
+++ b/grading/userprog/close-normal.exp
@@ -0,0 +1,3 @@
+(close-normal) begin
+(close-normal) end
+close-normal: exit(0)
diff --git a/grading/userprog/close-stdin.c b/grading/userprog/close-stdin.c
new file mode 100644
index 0000000..0ead7f0
--- /dev/null
+++ b/grading/userprog/close-stdin.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  printf ("(close-stdin) begin\n");
+  close (0);
+  printf ("(close-stdin) end\n");
+  return 0;
+}
diff --git a/grading/userprog/close-stdin.exp b/grading/userprog/close-stdin.exp
new file mode 100644
index 0000000..093d1df
--- /dev/null
+++ b/grading/userprog/close-stdin.exp
@@ -0,0 +1,3 @@
+(close-stdin) begin
+(close-stdin) end
+close-stdin: exit(0)
diff --git a/grading/userprog/close-stdout.c b/grading/userprog/close-stdout.c
new file mode 100644
index 0000000..1c74ce3
--- /dev/null
+++ b/grading/userprog/close-stdout.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  printf ("(close-stdout) begin\n");
+  close (1);
+  printf ("(close-stdout) end\n");
+  return 0;
+}
diff --git a/grading/userprog/close-stdout.exp b/grading/userprog/close-stdout.exp
new file mode 100644
index 0000000..8ed30ae
--- /dev/null
+++ b/grading/userprog/close-stdout.exp
@@ -0,0 +1,3 @@
+(close-stdout) begin
+(close-stdout) end
+close-stdout: exit(0)
diff --git a/grading/userprog/close-twice.c b/grading/userprog/close-twice.c
new file mode 100644
index 0000000..2514286
--- /dev/null
+++ b/grading/userprog/close-twice.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  int handle;
+  printf ("(close-twice) begin\n");
+  handle = open ("sample.txt");
+  if (handle < 2)
+    printf ("(close-twice) fail: open() returned %d\n", handle);
+  close (handle);
+  close (handle);
+  printf ("(close-twice) end\n");
+  return 0;
+}
diff --git a/grading/userprog/close-twice.exp b/grading/userprog/close-twice.exp
new file mode 100644
index 0000000..deb3d32
--- /dev/null
+++ b/grading/userprog/close-twice.exp
@@ -0,0 +1,6 @@
+(close-twice) begin
+(close-twice) end
+close-twice: exit(0)
+--OR--
+(close-twice) begin
+close-twice: exit(-1)
diff --git a/grading/userprog/exit.c b/grading/userprog/exit.c
new file mode 100644
index 0000000..6be04df
--- /dev/null
+++ b/grading/userprog/exit.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  printf ("(exit) begin\n");
+  exit (57);
+  printf ("(exit) fail\n");
+  return 0;
+}
diff --git a/grading/userprog/exit.exp b/grading/userprog/exit.exp
new file mode 100644
index 0000000..00e83e8
--- /dev/null
+++ b/grading/userprog/exit.exp
@@ -0,0 +1,2 @@
+(exit) begin
+exit: exit(57)
diff --git a/grading/userprog/halt.c b/grading/userprog/halt.c
new file mode 100644
index 0000000..e3e4abf
--- /dev/null
+++ b/grading/userprog/halt.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  printf ("(halt) begin\n");
+  halt ();
+  printf ("(halt) fail\n");
+  return 0;
+}
diff --git a/grading/userprog/halt.exp b/grading/userprog/halt.exp
new file mode 100644
index 0000000..d421b32
--- /dev/null
+++ b/grading/userprog/halt.exp
@@ -0,0 +1 @@
+(halt) begin
diff --git a/grading/userprog/open-bad-ptr.c b/grading/userprog/open-bad-ptr.c
new file mode 100644
index 0000000..71bd119
--- /dev/null
+++ b/grading/userprog/open-bad-ptr.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  printf ("(open-bad-ptr) begin\n");
+  open ((char *) 0xc0101234);
+  printf ("(open-bad-ptr) end\n");
+  return 0;
+}
diff --git a/grading/userprog/open-bad-ptr.exp b/grading/userprog/open-bad-ptr.exp
new file mode 100644
index 0000000..b0376a3
--- /dev/null
+++ b/grading/userprog/open-bad-ptr.exp
@@ -0,0 +1,6 @@
+(open-bad-ptr) begin
+(open-bad-ptr) end
+open-bad-ptr: exit(0)
+--OR--
+(open-bad-ptr) begin
+open-bad-ptr: exit(-1)
diff --git a/grading/userprog/open-boundary.c b/grading/userprog/open-boundary.c
new file mode 100644
index 0000000..374d405
--- /dev/null
+++ b/grading/userprog/open-boundary.c
@@ -0,0 +1,26 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+
+static char *
+mk_boundary_string (const char *src) 
+{
+  static char dst[8192];
+  char *p = dst + (4096 - (uintptr_t) dst % 4096 - strlen (src) / 2);
+  strlcpy (p, src, 4096);
+  return p;
+}
+
+int
+main (void) 
+{
+  int handle;
+
+  printf ("(open-boundary) begin\n");
+  handle = open (mk_boundary_string ("sample.txt"));
+  if (handle < 2)
+    printf ("(open-boundary) fail: open() returned %d\n", handle);
+  printf ("(open-boundary) end\n");
+  return 0;
+}
diff --git a/grading/userprog/open-boundary.exp b/grading/userprog/open-boundary.exp
new file mode 100644
index 0000000..bc1d311
--- /dev/null
+++ b/grading/userprog/open-boundary.exp
@@ -0,0 +1,3 @@
+(open-boundary) begin
+(open-boundary) end
+open-boundary: exit(0)
diff --git a/grading/userprog/open-empty.c b/grading/userprog/open-empty.c
new file mode 100644
index 0000000..2529bce
--- /dev/null
+++ b/grading/userprog/open-empty.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  int handle;
+  printf ("(open-empty) begin\n");
+  handle = open ("");
+  if (handle != -1)
+    printf ("(open-empty) fail: open() returned %d\n", handle);
+  printf ("(open-empty) end\n");
+  return 0;
+}
diff --git a/grading/userprog/open-empty.exp b/grading/userprog/open-empty.exp
new file mode 100644
index 0000000..9944192
--- /dev/null
+++ b/grading/userprog/open-empty.exp
@@ -0,0 +1,3 @@
+(open-empty) begin
+(open-empty) end
+open-empty: exit(0)
diff --git a/grading/userprog/open-missing.c b/grading/userprog/open-missing.c
new file mode 100644
index 0000000..e7a2b84
--- /dev/null
+++ b/grading/userprog/open-missing.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  int handle;
+  printf ("(open-missing) begin\n");
+  handle = open ("no-such-file");
+  if (handle != -1)
+    printf ("(open-missing) fail: open() returned %d\n", handle);
+  printf ("(open-missing) end\n");
+  return 0;
+}
diff --git a/grading/userprog/open-missing.exp b/grading/userprog/open-missing.exp
new file mode 100644
index 0000000..9e52e13
--- /dev/null
+++ b/grading/userprog/open-missing.exp
@@ -0,0 +1,3 @@
+(open-missing) begin
+(open-missing) end
+open-missing: exit(0)
diff --git a/grading/userprog/open-normal.c b/grading/userprog/open-normal.c
new file mode 100644
index 0000000..74dbbf4
--- /dev/null
+++ b/grading/userprog/open-normal.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  int handle;
+  printf ("(open-normal) begin\n");
+  handle = open ("sample.txt");
+  if (handle < 2)
+    printf ("(open-normal) fail: open() returned %d\n", handle);
+  printf ("(open-normal) end\n");
+  return 0;
+}
diff --git a/grading/userprog/open-normal.exp b/grading/userprog/open-normal.exp
new file mode 100644
index 0000000..7087931
--- /dev/null
+++ b/grading/userprog/open-normal.exp
@@ -0,0 +1,3 @@
+(open-normal) begin
+(open-normal) end
+open-normal: exit(0)
diff --git a/grading/userprog/open-null.c b/grading/userprog/open-null.c
new file mode 100644
index 0000000..5d6cc5c
--- /dev/null
+++ b/grading/userprog/open-null.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  printf ("(open-null) begin\n");
+  open (NULL);
+  printf ("(open-null) end\n");
+  return 0;
+}
diff --git a/grading/userprog/open-null.exp b/grading/userprog/open-null.exp
new file mode 100644
index 0000000..fa8dc57
--- /dev/null
+++ b/grading/userprog/open-null.exp
@@ -0,0 +1,6 @@
+(open-null) begin
+(open-null) end
+open-null: exit(0)
+--OR--
+(open-null) begin
+open-null: exit(-1)
diff --git a/grading/userprog/open-twice.c b/grading/userprog/open-twice.c
new file mode 100644
index 0000000..f029fe5
--- /dev/null
+++ b/grading/userprog/open-twice.c
@@ -0,0 +1,22 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  int h1, h2;
+  printf ("(open-twice) begin\n");
+
+  h1 = open ("sample.txt");
+  if (h1 < 2)
+    printf ("(open-twice) fail: open() returned %d first time\n", h1);
+
+  h2 = open ("sample.txt");
+  if (h2 < 2)
+    printf ("(open-twice) fail: open() returned %d second time\n", h2);
+  if (h1 == h2)
+    printf ("(open-twice) fail: open() returned %d both times\n", h1);
+
+  printf ("(open-twice) end\n");
+  return 0;
+}
diff --git a/grading/userprog/open-twice.exp b/grading/userprog/open-twice.exp
new file mode 100644
index 0000000..650c0d3
--- /dev/null
+++ b/grading/userprog/open-twice.exp
@@ -0,0 +1,3 @@
+(open-twice) begin
+(open-twice) end
+open-twice: exit(0)
diff --git a/grading/userprog/prep-disk b/grading/userprog/prep-disk
new file mode 100755
index 0000000..c775a4e
--- /dev/null
+++ b/grading/userprog/prep-disk
@@ -0,0 +1,57 @@
+#! /usr/bin/perl -w
+
+use strict;
+use Getopt::Long;
+
+my ($pintos) = "pintos";
+my ($os_disk) = "../../src/userprog/build/os.dsk";
+my ($fs_disk);
+my ($test);
+
+GetOptions ("os-disk=s" => \$os_disk,
+	    "fs-disk=s" => \$fs_disk,
+	    "test=s" => \$test,
+	    "help" => sub { usage (0) })
+    or die "option parsing failed; use --help for help\n";
+
+if (!defined ($test)) {
+    die "test name expected; use --help for help\n"
+	if @ARGV != 1;
+    $test = shift @ARGV;
+} elsif (@ARGV != 0) {
+    die "can't have non-option arg with --test\n";
+}
+
+$fs_disk = "$test.dsk" if !defined $fs_disk;
+
+if (! -e $os_disk) {
+    print STDERR "$os_disk: stat: $!\n";
+    print STDERR "perhaps you should `make' in ../../src/userprog?\n";
+    exit 1;
+}
+
+our ($formatted) = 0;
+
+unlink $fs_disk;
+xsystem ("$pintos make-disk '$fs_disk' 2");
+put_file ("$test");
+put_file ("sample.txt")
+    if grep ($_ eq $test,
+	     qw (open-normal open-boundary open-twice
+		 close-normal close-twice
+		 read-normal read-bad-ptr read-boundary read-zero
+		 write-normal write-boundary write-zero));		 
+
+sub put_file {
+    my ($fn) = @_;
+    my ($cmd) = "$pintos -v --os-disk='$os_disk' --fs-disk='$fs_disk' put";
+    $cmd .= " -f", $formatted = 1 if !$formatted;
+    $cmd .= " '$fn'";
+    xsystem ($cmd);
+}
+
+sub xsystem {
+    my ($cmd) = @_;
+    print "$cmd\n";
+    system ($cmd) == 0 || die "command failed\n";
+}
diff --git a/grading/userprog/read-bad-fd.c b/grading/userprog/read-bad-fd.c
new file mode 100644
index 0000000..eb9700d
--- /dev/null
+++ b/grading/userprog/read-bad-fd.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  char buf;
+  printf ("(read-bad-fd) begin\n");
+  read (0xc0101234, &buf, 1);
+  printf ("(read-bad-fd) end\n");
+  return 0;
+}
diff --git a/grading/userprog/read-bad-fd.exp b/grading/userprog/read-bad-fd.exp
new file mode 100644
index 0000000..0c30c9a
--- /dev/null
+++ b/grading/userprog/read-bad-fd.exp
@@ -0,0 +1,6 @@
+(read-bad-fd) begin
+(read-bad-fd) end
+read-bad-fd: exit(0)
+--OR--
+(read-bad-fd) begin
+read-bad-fd: exit(-1)
diff --git a/grading/userprog/read-bad-ptr.c b/grading/userprog/read-bad-ptr.c
new file mode 100644
index 0000000..860e485
--- /dev/null
+++ b/grading/userprog/read-bad-ptr.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  int handle;
+  printf ("(read-bad-ptr) begin\n");
+
+  handle = open ("sample.txt");
+  if (handle < 2)
+    printf ("(read-bad-ptr) fail: open() returned %d\n", handle);
+
+  read (handle, (char *) 0xc0101234, 123);
+  
+  printf ("(read-bad-ptr) end\n");
+  return 0;
+}
diff --git a/grading/userprog/read-bad-ptr.exp b/grading/userprog/read-bad-ptr.exp
new file mode 100644
index 0000000..fe4d3ed
--- /dev/null
+++ b/grading/userprog/read-bad-ptr.exp
@@ -0,0 +1,6 @@
+(read-bad-ptr) begin
+(read-bad-ptr) end
+read-bad-ptr: exit(0)
+--OR--
+(read-bad-ptr) begin
+read-bad-ptr: exit(-1)
diff --git a/grading/userprog/read-boundary.c b/grading/userprog/read-boundary.c
new file mode 100644
index 0000000..7992730
--- /dev/null
+++ b/grading/userprog/read-boundary.c
@@ -0,0 +1,49 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+
+char expected[] = {
+  "Amazing Electronic Fact: If you scuffed your feet long enough without\n"
+  "touching anything, you would build up so many electrons that your\n"
+  "finger would explode!  But this is nothing to worry about unless you\n"
+  "have carpeting.\n" 
+};
+
+
+
+static char *
+mk_boundary_string (const char *src) 
+{
+  static char dst[8192];
+  char *p = dst + (4096 - (uintptr_t) dst % 4096 - strlen (src) / 2);
+  strlcpy (p, src, 4096);
+  return p;
+}
+
+int
+main (void) 
+{
+  int handle;
+  int byte_cnt;
+  char *actual_p;
+
+  actual_p = mk_boundary_string (expected);
+
+  printf ("(read-boundary) begin\n");
+
+  handle = open ("sample.txt");
+  if (handle < 2)
+    printf ("(read-boundary) fail: open() returned %d\n", handle);
+
+  byte_cnt = read (handle, actual_p, sizeof expected - 1);
+  if (byte_cnt != sizeof expected - 1)
+    printf ("(read-boundary) fail: read() returned %d instead of %d\n",
+            byte_cnt, sizeof expected - 1);
+  else if (strcmp (expected, actual_p))
+    printf ("(read-boundary) fail: expected text differs from actual:\n%s",
+            actual_p);
+  
+  printf ("(read-boundary) end\n");
+  return 0;
+}
diff --git a/grading/userprog/read-boundary.exp b/grading/userprog/read-boundary.exp
new file mode 100644
index 0000000..cbbcda8
--- /dev/null
+++ b/grading/userprog/read-boundary.exp
@@ -0,0 +1,3 @@
+(read-boundary) begin
+(read-boundary) end
+read-boundary: exit(0)
diff --git a/grading/userprog/read-normal.c b/grading/userprog/read-normal.c
new file mode 100644
index 0000000..4bb7679
--- /dev/null
+++ b/grading/userprog/read-normal.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+
+char expected[] = {
+  "Amazing Electronic Fact: If you scuffed your feet long enough without\n"
+  "touching anything, you would build up so many electrons that your\n"
+  "finger would explode!  But this is nothing to worry about unless you\n"
+  "have carpeting.\n" 
+};
+
+char actual[sizeof expected];
+
+int
+main (void) 
+{
+  int handle, byte_cnt;
+  printf ("(read-normal) begin\n");
+
+  handle = open ("sample.txt");
+  if (handle < 2)
+    printf ("(read-normal) fail: open() returned %d\n", handle);
+
+  byte_cnt = read (handle, actual, sizeof actual - 1);
+  if (byte_cnt != sizeof actual - 1)
+    printf ("(read-normal) fail: read() returned %d instead of %d\n",
+            byte_cnt, sizeof actual - 1);
+  else if (strcmp (expected, actual))
+    printf ("(read-normal) fail: expected text differs from actual:\n%s",
+            actual);
+  
+  printf ("(read-normal) end\n");
+  return 0;
+}
diff --git a/grading/userprog/read-normal.exp b/grading/userprog/read-normal.exp
new file mode 100644
index 0000000..e45dc51
--- /dev/null
+++ b/grading/userprog/read-normal.exp
@@ -0,0 +1,3 @@
+(read-normal) begin
+(read-normal) end
+read-normal: exit(0)
diff --git a/grading/userprog/read-stdout.c b/grading/userprog/read-stdout.c
new file mode 100644
index 0000000..df0274c
--- /dev/null
+++ b/grading/userprog/read-stdout.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  char buf;
+  printf ("(read-stdout) begin\n");
+  read (1, &buf, 1);
+  printf ("(read-stdout) end\n");
+  return 0;
+}
diff --git a/grading/userprog/read-stdout.exp b/grading/userprog/read-stdout.exp
new file mode 100644
index 0000000..63c1d52
--- /dev/null
+++ b/grading/userprog/read-stdout.exp
@@ -0,0 +1,6 @@
+(read-stdout) begin
+(read-stdout) end
+read-stdout: exit(0)
+--OR--
+(read-stdout) begin
+read-stdout: exit(-1)
diff --git a/grading/userprog/read-zero.c b/grading/userprog/read-zero.c
new file mode 100644
index 0000000..7f9ec82
--- /dev/null
+++ b/grading/userprog/read-zero.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (void) 
+{
+  int handle, byte_cnt;
+  char buf;
+  printf ("(read-zero) begin\n");
+
+  handle = open ("sample.txt");
+  if (handle < 2)
+    printf ("(read-zero) fail: open() returned %d\n", handle);
+
+  buf = 123;
+  byte_cnt = read (handle, &buf, 0);
+  if (byte_cnt != 0)
+    printf ("(read-zero) fail: read() returned %d instead of 0\n", byte_cnt);
+  else if (buf != 123)
+    printf ("(read-zero) fail: 0-byte read() modified buffer\n");
+  
+  printf ("(read-zero) end\n");
+  return 0;
+}
diff --git a/grading/userprog/read-zero.exp b/grading/userprog/read-zero.exp
new file mode 100644
index 0000000..f1b438a
--- /dev/null
+++ b/grading/userprog/read-zero.exp
@@ -0,0 +1,3 @@
+(read-zero) begin
+(read-zero) end
+read-zero: exit(0)
diff --git a/grading/userprog/run-tests b/grading/userprog/run-tests
index e7a31ac..3781649 100755
--- a/grading/userprog/run-tests
+++ b/grading/userprog/run-tests
@@ -37,11 +37,18 @@ sub usage {
 }
 
 # Default set of tests.
-@TESTS = qw (create-normal create-empty create-null create-bad-ptr 
+@TESTS = qw (args-argc args-argv0 args-argvn args-single args-multiple
+	     args-dbl-space
+	     sc-bad-sp sc-bad-arg sc-boundary
+	     halt exit
+	     create-normal create-empty create-null create-bad-ptr 
 	     create-long create-exists create-bound
-	     args-argc args-argv0 args-argvn args-single args-multiple
-	     args-dbl-space)
-    unless @TESTS > 0;
+	     open-normal open-missing open-boundary open-empty open-null
+	     open-bad-ptr open-twice
+	     close-normal close-twice close-stdin close-stdout close-bad-fd
+	     read-normal read-bad-ptr read-boundary read-zero read-stdout
+	     read-bad-fd
+	     ) unless @TESTS > 0;
 
 our (%args);
 for my $key ('args-argc', 'args-argv0', 'args-argvn', 'args-multiple') {
@@ -623,7 +630,7 @@ sub compare_output {
 	# They differ.  Output a diff.
 	my (@diff) = "";
 	my ($d) = Algorithm::Diff->new (\@expected, \@actual);
-	my ($all_additions) = 1;
+	my ($not_fuzzy_match) = 0;
 	while ($d->Next ()) {
 	    my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2));
 	    if ($d->Same ()) {
@@ -631,11 +638,13 @@ sub compare_output {
 	    } else {
 		push (@diff, map ("- $_", $d->Items (1))) if $d->Items (1);
 		push (@diff, map ("+ $_", $d->Items (2))) if $d->Items (2);
-		$all_additions = 0 if $d->Items (1);
+		if ($d->Items (1)
+		    || grep (/\($test\)|exit\(-?\d+\)/, $d->Items (2))) {
+		    $not_fuzzy_match = 1;
+		}
 	    }
 	}
-
-	$fuzzy_match = 1 if $all_additions;
+	$fuzzy_match = 1 if !$not_fuzzy_match;
 
 	$details .= "Differences in `diff -u' format:\n";
 	$details .= join ('', @diff);
diff --git a/grading/userprog/sample.txt b/grading/userprog/sample.txt
new file mode 100644
index 0000000..9e0dd45
--- /dev/null
+++ b/grading/userprog/sample.txt
@@ -0,0 +1,4 @@
+Amazing Electronic Fact: If you scuffed your feet long enough without
+touching anything, you would build up so many electrons that your
+finger would explode!  But this is nothing to worry about unless you
+have carpeting.
diff --git a/grading/userprog/sc-bad-arg.c b/grading/userprog/sc-bad-arg.c
index 8bbd8ea..e6019a6 100644
--- a/grading/userprog/sc-bad-arg.c
+++ b/grading/userprog/sc-bad-arg.c
@@ -5,7 +5,7 @@ int
 main (void) 
 {
   printf ("(sc-bad-arg) begin\n"); 
-  asm volatile ("mov $0xbffffffc, %%esp; movl $%0, (%%esp); int $0x30"
+  asm volatile ("mov $0xbffffffc, %%esp; movl %0, (%%esp); int $0x30"
                 :
                 : "i" (SYS_exit));
   printf ("(sc-bad-arg) end\n"); 
diff --git a/grading/userprog/sc-bad-sp.c b/grading/userprog/sc-bad-sp.c
index 9d996e9..d7a0f82 100644
--- a/grading/userprog/sc-bad-sp.c
+++ b/grading/userprog/sc-bad-sp.c
@@ -5,7 +5,7 @@ int
 main (void) 
 {
   printf ("(sc-bad-sp) begin\n"); 
-  asm volatile ("mov $0xc0101234, %%esp; int $0x30");
+  asm volatile ("mov $0xc0101234, %esp; int $0x30");
   printf ("(sc-bad-sp) end\n"); 
   return 0;
 }
diff --git a/grading/userprog/sc-boundary.c b/grading/userprog/sc-boundary.c
new file mode 100644
index 0000000..401e437
--- /dev/null
+++ b/grading/userprog/sc-boundary.c
@@ -0,0 +1,28 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall-nr.h>
+
+static void *
+mk_boundary_array (void) 
+{
+  static char dst[8192];
+  return dst + (4096 - (uintptr_t) dst % 4096);
+}
+
+int
+main (void) 
+{
+  int *p;
+  
+  printf ("(sc-boundary) begin\n");
+  p = mk_boundary_array ();
+  p--;
+  p[0] = SYS_exit;
+  p[1] = 42;
+  asm volatile ("mov %0, %%esp; int $0x30"
+                :
+                : "g" (p));
+  printf ("(sc-boundary) failed\n");
+  return 1;
+}
diff --git a/grading/userprog/sc-boundary.exp b/grading/userprog/sc-boundary.exp
new file mode 100644
index 0000000..1f38d87
--- /dev/null
+++ b/grading/userprog/sc-boundary.exp
@@ -0,0 +1,2 @@
+(sc-boundary) begin
+sc-boundary: exit(42)
diff --git a/grading/userprog/tests.txt b/grading/userprog/tests.txt
index 73027a4..ce7c393 100644
--- a/grading/userprog/tests.txt
+++ b/grading/userprog/tests.txt
@@ -37,8 +37,9 @@ System calls: open
   -2 open-boundary: pass name of file crossing page boundary
   -1 open-empty: pass empty string to open system call
   -1 open-null: pass null pointer to open system call
+  -1 open-bad-ptr: pass invalid pointer to open system call
   -1 open-twice: open the same file twice
-Score: /9
+Score: /10
 
 System calls: close
   -2 close-normal: close an open file in the most normal way
diff --git a/src/Makefile.userprog b/src/Makefile.userprog
index 6b3c250..4cdf078 100644
--- a/src/Makefile.userprog
+++ b/src/Makefile.userprog
@@ -27,18 +27,19 @@ LIB_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(LIB_SRC)))
 LIB_DEP = $(patsubst %.o,%.d,$(LIB_OBJ))
 LIB = lib/user/entry.o libc.a
 
-PROGS_SRC = $(foreach prog,$(PROGS),$($(prog)_SRC))
+PROGS_SRC = $(foreach prog,$(PROGS),$($(subst -,_,$(prog))_SRC))
 PROGS_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(PROGS_SRC)))
 PROGS_DEP = $(patsubst %.o,%.d,$(PROGS_OBJ))
 
 all: $(PROGS)
 
 define TEMPLATE
-$(1)_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$($(1)_SRC)))
-$(1): $$($(1)_OBJ) $$(LIB)
+$(2)_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$($(2)_SRC)))
+$(1): $$($(2)_OBJ) $$(LIB)
+	$$(CC) $$(LDFLAGS) $$^ $$(LDLIBS) -o $$@
 endef
 
-$(foreach prog,$(PROGS),$(eval $(call TEMPLATE,$(prog))))
+$(foreach prog,$(PROGS),$(eval $(call TEMPLATE,$(prog),$(subst -,_,$(prog)))))
 
 $(PROGS): $(LIB)
 
diff --git a/src/utils/pintos b/src/utils/pintos
index 6bb9f09..798708e 100755
--- a/src/utils/pintos
+++ b/src/utils/pintos
@@ -11,15 +11,27 @@ our ($vga);
 our ($jitter);
 
 use Getopt::Long qw(:config require_order bundling);
-GetOptions ("bochs|qemu|gsx" => \&set_sim,
-	    "no-debug|monitor|gdb" => \&set_debug,
+GetOptions ("sim=s" => sub { set_sim (@_) },
+	    "bochs" => sub { set_sim ("bochs") },
+	    "qemu" => sub { set_sim ("qemu") },
+	    "gsx" => sub { set_sim ("gsx") },
+
+	    "debug=s" => sub { set_debug (@_) },
+	    "no-debug" => sub { set_debug ("no-debug") },
+	    "monitor" => sub { set_debug ("monitor") },
+	    "gdb" => sub { set_debug ("gdb") },
+	    
 	    "run|get|put|make-disk" => \&cmd_option,
+	    
 	    "m|memory=i" => \$mem,
 	    "j|jitter=i" => \$jitter,
+	    
 	    "v|no-vga" => sub { set_vga ('none'); },
 	    "s|no-serial" => sub { $serial_out = 0; },
 	    "t|terminal" => sub { set_vga ('terminal'); },
+	    
 	    "h|help" => sub { usage (0); },
+
 	    "0|os-disk|disk-0|hda=s" => \$disks[0],
 	    "1|fs-disk|disk-1|hdb=s" => \$disks[1],
 	    "2|scratch-disk|disk-2|hdc=s" => \$disks[2],
@@ -31,15 +43,17 @@ $debug = "no-debug" if !defined $debug;
 $vga = "window" if !defined $vga;
 
 sub set_sim {
-    my ($option) = @_;
-    die "--$option conflicts with --$sim\n" if defined $sim;
-    our ($sim) = $option;
+    my ($new_sim) = @_;
+    die "--$new_sim conflicts with --$sim\n"
+	if defined ($sim) && $sim ne $new_sim;
+    $sim = $new_sim;
 }
 
 sub set_debug {
-    my ($option) = @_;
-    die "--$option conflicts with --$debug\n" if defined $debug;
-    our ($debug) = $option;
+    my ($new_debug) = @_;
+    die "--$new_debug conflicts with --$debug\n"
+	if defined ($debug) && $debug ne $new_debug;
+    $debug = $new_debug;
 }
 
 sub set_vga {
@@ -47,12 +61,10 @@ sub set_vga {
     if (defined ($vga) && $vga ne $new_vga) {
 	print "warning: conflicting vga display options\n";
     }
-    our ($vga) = $new_vga;
+    $vga = $new_vga;
 }
 
 sub cmd_option {
-    our ($cmd) = @_;
-
     # Force an end to option processing, as with --.
     die ("!FINISH");
 }
@@ -70,6 +82,13 @@ if ($cmd eq 'run') {
 
     create_disk ($file, int ($mb * 1008));
 } elsif ($cmd eq 'put') {
+    # Take a -f option to combine formatting with putting.
+    my ($format) = 0;
+    if (@ARGV > 0 && $ARGV[0] eq '-f') {
+	shift @ARGV;
+	$format = 1;
+    }
+
     usage () if @ARGV != 1 && @ARGV != 2;
     my ($hostfn, $guestfn) = @ARGV;
     $guestfn = $hostfn if !defined $guestfn;
@@ -80,7 +99,9 @@ if ($cmd eq 'run') {
     copy_pad ($hostfn, "scratch.dsk", 512);
 
     # Do copy.
-    run_vm ("-ci", $guestfn, $size, "-q");
+    my (@cmd) = ("-ci", $guestfn, $size, "-q");
+    unshift (@cmd, "-f") if $format;
+    run_vm (@cmd);
 } elsif ($cmd eq 'get') {
     usage () if @ARGV != 1 && @ARGV != 2;
     my ($guestfn, $hostfn) = @ARGV;