* able to go there. */
char *get_full_path(const char *origpath)
{
- char *d_here, *d_there = NULL;
+ struct stat fileinfo;
+ char *d_here, *d_there, *d_there_file = NULL;
+ const char *last_slash;
+ bool path_only;
if (origpath == NULL)
return NULL;
- /* Get the current directory. */
+ /* Get the current directory. If it doesn't exist, back up and try
+ * again until we get a directory that does exist. */
d_here = charalloc(PATH_MAX + 1);
d_here = getcwd(d_here, PATH_MAX + 1);
- if (d_here != NULL) {
- const char *last_slash;
- char *d_there_file = NULL;
- bool path_only;
- struct stat fileinfo;
+ while (d_here == NULL) {
+ if (chdir("..") == -1)
+ break;
+
+ d_here = getcwd(d_here, PATH_MAX + 1);
+ }
+ /* If we succeeded, canonicalize it in d_here. */
+ if (d_here != NULL) {
align(&d_here);
/* If the current directory isn't "/", tack a slash onto the end
d_here = charealloc(d_here, strlen(d_here) + 2);
strcat(d_here, "/");
}
+ /* Otherwise, set d_here to "". */
+ } else
+ d_here = mallocstrcpy(NULL, "");
- d_there = real_dir_from_tilde(origpath);
+ d_there = real_dir_from_tilde(origpath);
- assert(d_there != NULL);
+ assert(d_there != NULL);
- /* Stat d_there. If stat() fails, assume that d_there refers to
- * a new file that hasn't been saved to disk yet. Set path_only
- * to TRUE if d_there refers to a directory, and FALSE if
- * d_there refers to a file. */
- path_only = !stat(d_there, &fileinfo) &&
- S_ISDIR(fileinfo.st_mode);
+ /* If stat()ing d_there fails, assume that d_there refers to a new
+ * file that hasn't been saved to disk yet. Set path_only to TRUE
+ * if d_there refers to a directory, and FALSE otherwise. */
+ path_only = stat(d_there, &fileinfo) == 0 &&
+ S_ISDIR(fileinfo.st_mode);
- /* If path_only is TRUE, make sure d_there ends in a slash. */
- if (path_only) {
- size_t d_there_len = strlen(d_there);
+ /* If path_only is TRUE, make sure d_there ends in a slash. */
+ if (path_only) {
+ size_t d_there_len = strlen(d_there);
- if (d_there[d_there_len - 1] != '/') {
- d_there = charealloc(d_there, d_there_len + 2);
- strcat(d_there, "/");
- }
+ if (d_there[d_there_len - 1] != '/') {
+ d_there = charealloc(d_there, d_there_len + 2);
+ strcat(d_there, "/");
}
+ }
- /* Search for the last slash in d_there. */
- last_slash = strrchr(d_there, '/');
+ /* Search for the last slash in d_there. */
+ last_slash = strrchr(d_there, '/');
- /* If we didn't find one, then make sure the answer is in the
- * format "d_here/d_there". */
- if (last_slash == NULL) {
- assert(!path_only);
+ /* If we didn't find one, then make sure the answer is in the format
+ * "d_here/d_there". */
+ if (last_slash == NULL) {
+ assert(!path_only);
- d_there_file = d_there;
- d_there = d_here;
+ d_there_file = d_there;
+ d_there = d_here;
+ } else {
+ /* If path_only is FALSE, then save the filename portion of the
+ * answer (everything after the last slash) in d_there_file. */
+ if (!path_only)
+ d_there_file = mallocstrcpy(NULL, last_slash + 1);
+
+ /* And remove the filename portion of the answer from
+ * d_there. */
+ null_at(&d_there, last_slash - d_there + 1);
+
+ /* chdir() to the path specified in d_there. */
+ if (chdir(d_there) == -1) {
+ free(d_there);
+ d_there = NULL;
} else {
- /* If path_only is FALSE, then save the filename portion of
- * the answer, everything after the last slash, in
- * d_there_file. */
- if (!path_only)
- d_there_file = mallocstrcpy(NULL, last_slash + 1);
-
- /* And remove the filename portion of the answer from
- * d_there. */
- null_at(&d_there, last_slash - d_there + 1);
-
- /* Go to the path specified in d_there. */
- if (chdir(d_there) == -1) {
- free(d_there);
- d_there = NULL;
- } else {
- /* Get the full path and save it in d_there. */
- free(d_there);
+ free(d_there);
- d_there = charalloc(PATH_MAX + 1);
- d_there = getcwd(d_there, PATH_MAX + 1);
+ /* Get the full path. */
+ d_there = charalloc(PATH_MAX + 1);
+ d_there = getcwd(d_there, PATH_MAX + 1);
- if (d_there != NULL) {
- align(&d_there);
+ /* If we succeeded, canonicalize it in d_there. */
+ if (d_there != NULL) {
+ align(&d_there);
- if (strcmp(d_there, "/") != 0) {
- /* Make sure d_there ends in a slash. */
- d_there = charealloc(d_there,
- strlen(d_there) + 2);
- strcat(d_there, "/");
- }
- } else
- /* If we couldn't get the full path, set path_only
- * to TRUE so that we clean up correctly, free all
- * allocated memory, and return NULL. */
- path_only = TRUE;
-
- /* Finally, go back to the path specified in d_here,
- * where we were before. */
- chdir(d_here);
- }
+ /* If the current directory isn't "/", tack a slash onto
+ * the end of it. */
+ if (strcmp(d_there, "/") != 0) {
+ d_there = charealloc(d_there, strlen(d_there) + 2);
+ strcat(d_there, "/");
+ }
+ } else
+ /* Otherwise, set path_only to TRUE, so that we clean up
+ * correctly, free all allocated memory, and return
+ * NULL. */
+ path_only = TRUE;
+
+ /* Finally, go back to the path specified in d_here,
+ * where we were before. We don't check for a chdir()
+ * error, since we can do nothing then. */
+ chdir(d_here);
/* Free d_here, since we're done using it. */
free(d_here);
}
- /* At this point, if path_only is FALSE and d_there exists,
+ /* At this point, if path_only is FALSE and d_there isn't NULL,
* d_there contains the path portion of the answer and
* d_there_file contains the filename portion of the answer. If
- * this is the case, tack d_there_file onto the end of
- * d_there, so that d_there contains the complete answer. */
+ * this is the case, tack the latter onto the end of the former.
+ * d_there will then contain the complete answer. */
if (!path_only && d_there != NULL) {
d_there = charealloc(d_there, strlen(d_there) +
strlen(d_there_file) + 1);