/*------------- Telecommunications & Signal Processing Lab ------------- McGill University Routine: int FLfullName (const char Fname[], char Fullname[]) Purpose: Expand a file name path Description: This routine takes an input file name with path and tries to expand it to form a file name with an absolute path. The typical use of this routine is to log the full file name of a file that has already been opened, and hence for which the path is well defined and readable. The basic strategy is to take the path and try to change the current working directory to this path. If this is unsuccessful, the rightmost component of the path is removed and the process is repeated. When (if) successful, the leftmost components are replaced by the current working directory. This results in a resolved, cleaned up path name. The rightmost components are appended to this to form the full path name. Note that the rightmost components may not been resolved, either because the directories specified therein do not exist on the current host or are unreadable. Note that ~ characters in the file name are treated as ordinary characters. Parameters: <- int FLfullName Number of characters in the output string -> const char Fname[] Input character string with the file name path <- char Fullname[] Output string with the file name with resolved path name. This string is at most FILENAME_MAX characters long not including the terminating null character. This string will be the input file name path if the path cannot be resolved. Author / revision: P. Kabal Copyright (C) 1995 $Revision: 1.24 $ $Date: 1995/06/01 11:00:23 $ ----------------------------------------------------------------------*/ static char rcsid[] = "$Id: FLfullName.c 1.24 1995/06/01 AFsp-V2R1 $"; #include #include #include #ifdef _MSDOS # ifndef MSDOS # define MSDOS 1 /* For MSVC with /Za option */ # endif #endif #ifdef MSDOS # ifndef unix # define USE__NAME # endif #endif #ifdef USE__NAME # include # define CHDIR _chdir # define GETCWD _getcwd #else # include # define CHDIR chdir # define GETCWD getcwd #endif #ifdef MSDOS # ifndef unix # define MSDOS_SEP # endif #endif #ifdef MSDOS_SEP # define DIR_SEP_CHAR '\\' #else # define DIR_SEP_CHAR '/' #endif int FLfullName (Fname, Fullname) const char Fname[]; char Fullname[]; { char tname[FILENAME_MAX+2]; /* getcwd needs space for max + 2 chars */ char Bname[FILENAME_MAX+1]; char Dname[FILENAME_MAX+2]; char Dsave[FILENAME_MAX+2]; char ddname[FILENAME_MAX+1]; int status; int n; char Home[FILENAME_MAX+1]; #ifdef unix char *p; #endif /* Return an empty string if the input string is empty */ if (Fname[0] == '\0') { Fullname[0]= '\0'; n = 0; return n; } /* Initial separation into directory string and null file name string */ Bname[0] = '\0'; STcopyMax (Fname, Dname, FILENAME_MAX); /* Save the current working directory and make sure we can get back here */ if (GETCWD (Dsave, FILENAME_MAX+2) == NULL || CHDIR (Dsave) != 0) { n = STcopyMax (Dname, Fullname, FILENAME_MAX); return n; } /* Try to "resolve" the path name by doing a chdir to the directory and then getting the resulting path name with a getcwd. In this way, we follow symbolic links. The resulting directory string from getcwd hopefully has been cleaned up of things like imbedded "./" and "../" strings. If the directory is not readable (or does not exist), an error results. In that case, peel off one level of directory and try again. This should eventually give a readable directory: the path name has a leading "/" or ends up being ".". Note that with these peeled off directories we do not get the benefit of having getcwd clean up the directory string. */ ddname[0] = '\0'; while (Dname[0] != '\0' && (status = CHDIR (Dname)) != 0 && strcmp (Dname, ddname) != 0) { STcopyMax (Dname, ddname, FILENAME_MAX); FLbaseName (ddname, tname); FLjoinNames (tname, Bname, Bname); FLdirName (ddname, Dname); } /* Found a directory with a successful chdir */ if ((Dname[0] == '\0' || status == 0) && GETCWD (Dname, FILENAME_MAX+2) != NULL) { #ifdef unix /* On systems which use automounted disks, the returned current working directory may contain a /tmp_mnt/ directory component. Yet the user can refer to the directory without that component. Here we look for such a component. If found, we remove it and verify that a chdir to that modified directory refers to the directory. */ p = strstr (Dname, "/tmp_mnt/"); if (p != NULL) { n = (int) (p - Dname) + 1; STcopyNMax (Dname, ddname, n, FILENAME_MAX); STcatMax (&Dname[n+8], ddname, FILENAME_MAX); if (CHDIR (ddname) == 0 && GETCWD (tname, FILENAME_MAX+2) != NULL && strcmp (Dname, tname) == 0) STcopyMax (ddname, Dname, FILENAME_MAX); } #endif /* The user's home directory may be a symbolic link. Usually the name stored in the environment variable HOME is the preferred way to refer to the home directory. Here we try to replace the leading file name components by the home directory name. */ FLhomeDir ("", Home); if (Home[0] != '\0' && CHDIR (Home) == 0 && GETCWD (tname, FILENAME_MAX+2) != NULL) { n = strlen (tname); if (strncmp (Dname, tname, (size_t) n) == 0) { if (Dname[n] == DIR_SEP_CHAR) FLjoinNames (Home, &Dname[n+1], Dname); else if (Dname[n] == '\0') STcopyMax (Home, Dname, FILENAME_MAX); } } /* Form the output name */ n = FLjoinNames (Dname, Bname, Fullname); } else /* No success, all we can do is return the input string */ n = STcopyMax (Fname, Fullname, FILENAME_MAX); /* Restore the original working directory */ if (CHDIR (Dsave) != 0) UTwarn ("FLfullName - Unable to reset current working directory"); return n; }