diff --git a/cat.c b/cat.c
index 6a54adf..0b98c74 100644
--- a/cat.c
+++ b/cat.c
@@ -2,19 +2,17 @@
 #include "stat.h"
 #include "user.h"
 
-char buf[513];
+char buf[512];
 
 void
 rfile(int fd)
 {
-  int cc;
+  int n;
 
-  while((cc = read(fd, buf, sizeof(buf) - 1)) > 0){
-    buf[cc] = '\0';
-    puts(buf);
-  }
-  if(cc < 0){
-    puts("cat: read error\n");
+  while((n = read(fd, buf, sizeof(buf))) > 0)
+    write(1, buf, n);
+  if(n < 0){
+    printf(1, "cat: read error\n");
     exit();
   }
 }
@@ -26,19 +24,16 @@ main(int argc, char *argv[])
 
   if(argc <= 1) {
     rfile(0);
-  } else {
-    for(i = 1; i < argc; i++){
-      fd = open(argv[i], 0);
-      if(fd < 0){
-        puts("cat: cannot open ");
-        puts(argv[i]);
-        puts("\n");
-        exit();
-      }
-      rfile(fd);
-      close(fd);
-    }
+    exit();
   }
 
+  for(i = 1; i < argc; i++){
+    if((fd = open(argv[i], 0)) < 0){
+      printf(1, "cat: cannot open %s\n", argv[i]);
+      exit();
+    }
+    rfile(fd);
+    close(fd);
+  }
   exit();
 }
diff --git a/echo.c b/echo.c
index 7bf3a45..806dee0 100644
--- a/echo.c
+++ b/echo.c
@@ -7,10 +7,7 @@ main(int argc, char *argv[])
 {
   int i;
 
-  for(i = 1; i < argc; i++){
-    puts(argv[i]);
-    puts(" ");
-  }
-  puts("\n");
+  for(i = 1; i < argc; i++)
+    printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n");
   exit();
 }
diff --git a/init.c b/init.c
index fbd4497..f8ad7b6 100644
--- a/init.c
+++ b/init.c
@@ -21,18 +21,18 @@ main(void)
   dup(0);  // stderr
 
   for(;;){
-    puts("init: starting sh\n");
+    printf(1, "init: starting sh\n");
     pid = fork();
     if(pid < 0){
-      puts("init: fork failed\n");
+      printf(1, "init: fork failed\n");
       exit();
     }
     if(pid == 0){
       exec("sh", sh_args);
-      puts("init: exec sh failed\n");
+      printf(1, "init: exec sh failed\n");
       exit();
     }
     while((wpid=wait()) >= 0 && wpid != pid)
-      puts("zombie!\n");
+      printf(1, "zombie!\n");
   }
 }
diff --git a/ls.c b/ls.c
index cf22a45..b6ddd7f 100644
--- a/ls.c
+++ b/ls.c
@@ -3,83 +3,83 @@
 #include "user.h"
 #include "fs.h"
 
-void
-pname(char *n)
+char*
+fmtname(char *path)
 {
-  int i;
+  static char buf[DIRSIZ+1];
+  char *p;
+  
+  // Find first character after last slash.
+  for(p=path+strlen(path); p >= path && *p != '/'; p--)
+    ;
+  p++;
+  
+  // Return blank-padded name.
+  if(strlen(p) >= DIRSIZ)
+    return p;
+  memmove(buf, p, strlen(p));
+  memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
+  return buf;
+}
 
-  for(i = 0; (i < DIRSIZ) && (n[i] != '\0') ; i++) {
-      printf(1, "%c", n[i]);
+void
+ls(char *path)
+{
+  char buf[512], *p;
+  int fd;
+  struct dirent de;
+  struct stat st;
+  
+  if((fd = open(path, 0)) < 0){
+    printf(2, "ls: cannot open %s\n", path);
+    return;
   }
-  for(; i < DIRSIZ; i++)
-    printf(1, " ");
+  
+  if(fstat(fd, &st) < 0){
+    printf(2, "ls: cannot stat %s\n", path);
+    close(fd);
+    return;
+  }
+  
+  switch(st.type){
+  case T_FILE:
+    printf(1, "%s %d %d %d\n", fmtname(path), st.type, st.ino, st.size);
+    break;
+  
+  case T_DIR:
+    if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
+      printf(1, "ls: path too long\n");
+      break;
+    }
+    strcpy(buf, path);
+    p = buf+strlen(buf);
+    *p++ = '/';
+    while(read(fd, &de, sizeof(de)) == sizeof(de)){
+      if(de.inum == 0)
+        continue;
+      memmove(p, de.name, DIRSIZ);
+      p[DIRSIZ] = 0;
+      if(stat(buf, &st) < 0){
+        printf(1, "ls: cannot stat %s\n", buf);
+        continue;
+      }
+      printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
+    }
+    break;
+  }
+  close(fd);
 }
 
 int
 main(int argc, char *argv[])
 {
-  char buf[512], *p;
-  int fd;
-  uint off, sz;
-  struct dirent de;
-  struct stat st;
+  int i;
 
-  if(argc > 2){
-    puts("Usage: ls [dir]\n");
+  if(argc < 2){
+    ls(".");
     exit();
   }
-
-  if(argc == 2) {
-    fd = open(argv[1], 0);
-    if(fd < 0){
-      printf(2, "ls: cannot open %s\n", argv[1]);
-      exit();
-    }
-  } else {
-    fd = open(".", 0);
-    if(fd < 0){
-      printf(2, "ls: cannot open .\n");
-      exit();
-    }
-  }
-
-  if(fstat(fd, &st) < 0) {
-    printf(2, "ls: cannot stat dir\n");
-    exit();
-  }
-
-  switch(st.type) {
-  case T_FILE:
-    pname(argv[1]);
-    printf(1, "%d %d %d\n", st.type, st.ino, st.size);
-    break;
-  case T_DIR:
-    sz = st.size;
-    for(off = 0; off < sz; off += sizeof(de)) {
-      if(read(fd, &de, sizeof(de)) != sizeof(de)) {
-        printf(1, "ls: read error\n");
-        break;
-      }
-      if(de.inum != 0) {
-        p = buf;
-        if(argc == 2) {			  
-          strcpy(p, argv[1]);
-          p += strlen(p);
-          if(*(p-1) != '/')
-            *p++ = '/';
-        }
-        memmove(p, de.name, DIRSIZ);
-        p[DIRSIZ] = 0;
-        if(stat(buf, &st) < 0) {
-          printf(1, "stat: failed %s\n", de.name);
-          continue;
-        }
-        pname(de.name);
-        printf(1, "%d %d %d\n", st.type, de.inum, st.size);
-      }
-    }
-    break;
-  }
-  close(fd);
+  for(i=1; i<argc; i++)
+    ls(argv[i]);
   exit();
 }
diff --git a/sh.c b/sh.c
index 6aa8824..1b517ef 100644
--- a/sh.c
+++ b/sh.c
@@ -51,7 +51,7 @@ main(void)
 int
 getcmd(char *buf, int nbuf)
 {
-  puts("$ ");
+  printf(2, "$ ");
   memset(buf, 0, nbuf);
   gets(buf, nbuf);
   if(buf[0] == 0) // EOF
diff --git a/ulib.c b/ulib.c
index 29aa644..ed2542d 100644
--- a/ulib.c
+++ b/ulib.c
@@ -3,12 +3,6 @@
 #include "fcntl.h"
 #include "user.h"
 
-int
-puts(char *s)
-{
-  return write(1, s, strlen(s));
-}
-
 char*
 strcpy(char *s, char *t)
 {
diff --git a/user.h b/user.h
index 7d2e596..bd6f729 100644
--- a/user.h
+++ b/user.h
@@ -21,7 +21,6 @@ char* sbrk(int);
 
 // ulib.c
 int stat(char*, struct stat*);
-int puts(char*);
 char* strcpy(char*, char*);
 void *memmove(void*, void*, int);
 char* strchr(const char*, char c);