File system project updates:
[pintos-anon] / src / examples / pwd.c
diff --git a/src/examples/pwd.c b/src/examples/pwd.c
new file mode 100644 (file)
index 0000000..b2441f5
--- /dev/null
@@ -0,0 +1,152 @@
+/* pwd.c
+   
+   Prints the absolute name of the present working directory. */
+
+#include <syscall.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+static bool getcwd (char *cwd, size_t cwd_size);
+
+int
+main (void) 
+{
+  char cwd[128];
+  if (getcwd (cwd, sizeof cwd)) 
+    {
+      printf ("%s\n", cwd);
+      return 0; 
+    }
+  else 
+    {
+      printf ("error\n");
+      return 1; 
+    }
+}
+\f
+/* Stores the inode number for FILE_NAME in *INUM.
+   Returns true if successful, false if the file could not be
+   opened. */
+static bool
+get_inumber (const char *file_name, int *inum) 
+{
+  int fd = open (file_name);
+  if (fd >= 0) 
+    {
+      *inum = inumber (fd);
+      close (fd);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Prepends PREFIX to the characters stored in the final *DST_LEN
+   bytes of the DST_SIZE-byte buffer that starts at DST.
+   Returns true if successful, false if adding that many
+   characters, plus a null terminator, would overflow the buffer.
+   (No null terminator is actually added or depended upon, but
+   its space is accounted for.) */
+static bool
+prepend (const char *prefix,
+         char *dst, size_t *dst_len, size_t dst_size) 
+{
+  size_t prefix_len = strlen (prefix);
+  if (prefix_len + *dst_len + 1 <= dst_size) 
+    {
+      *dst_len += prefix_len;
+      memcpy ((dst + dst_size) - *dst_len, prefix, prefix_len);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Stores the current working directory, as a null-terminated
+   string, in the CWD_SIZE bytes in CWD.
+   Returns true if successful, false on error.  Errors include
+   system errors, directory trees deeper than MAX_LEVEL levels,
+   and insufficient space in CWD. */
+static bool
+getcwd (char *cwd, size_t cwd_size) 
+{
+  size_t cwd_len = 0;   
+  
+#define MAX_LEVEL 20
+  char name[MAX_LEVEL * 3 + 1 + READDIR_MAX_LEN + 1];
+  char *namep;
+
+  int child_inum;
+
+  /* Make sure there's enough space for at least "/". */
+  if (cwd_size < 2)
+    return false;
+
+  /* Get inumber for current directory. */
+  if (!get_inumber (".", &child_inum))
+    return false;
+
+  namep = name;
+  for (;;)
+    {
+      int parent_inum, parent_fd;
+
+      /* Compose "../../../..", etc., in NAME. */
+      if ((namep - name) > MAX_LEVEL * 3)
+        return false;
+      *namep++ = '.';
+      *namep++ = '.';
+      *namep = '\0';
+
+      /* Open directory. */
+      parent_fd = open (name);
+      if (parent_fd < 0)
+        return false;
+      *namep++ = '/';
+
+      /* If parent and child have the same inumber,
+         then we've arrived at the root. */
+      parent_inum = inumber (parent_fd);
+      if (parent_inum == child_inum)
+        break;
+
+      /* Find name of file in parent directory with the child's
+         inumber. */
+      for (;;)
+        {
+          int test_inum;
+          if (!readdir (parent_fd, namep) || !get_inumber (name, &test_inum)) 
+            {
+              close (parent_fd);
+              return false; 
+            }
+          if (test_inum == child_inum)
+            break;
+        }
+      close (parent_fd);
+
+      /* Prepend "/name" to CWD. */
+      if (!prepend (namep - 1, cwd, &cwd_len, cwd_size))
+        return false;
+
+      /* Move up. */
+      child_inum = parent_inum;
+    }
+
+  /* Finalize CWD. */
+  if (cwd_len > 0) 
+    {
+      /* Move the string to the beginning of CWD,
+         and null-terminate it. */
+      memmove (cwd, (cwd + cwd_size) - cwd_len, cwd_len);
+      cwd[cwd_len] = '\0';
+    }
+  else 
+    {
+      /* Special case for the root. */
+      strlcpy (cwd, "/", cwd_size); 
+    }
+  
+  return true;
+}