/* pwd.c Prints the absolute name of the present working directory. */ #include #include #include #include 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; }