diff --git a/src/examples/pwd.c b/src/examples/pwd.c new file mode 100644 index 0000000000000000000000000000000000000000..328c149b2ebd2cab7b06c19b0191974115b3c929 --- /dev/null +++ b/src/examples/pwd.c @@ -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 EXIT_SUCCESS; + } + else + { + printf ("error\n"); + return EXIT_FAILURE; + } +} + +/* 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; +}