// // Filename expansion routines for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2017 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this // file is missing or damaged, see the license at: // // https://www.fltk.org/COPYING.php // // Please see the following page on how to report bugs and issues: // // https://www.fltk.org/bugs.php // /* expand a file name by prepending current directory, deleting . and .. (not really correct for symbolic links) between the prepended current directory. Use $PWD if it exists. Returns true if any changes were made. */ #include #include #include #include "Fl_System_Driver.H" #include #include "flstring.h" static inline int isdirsep(char c) {return c == '/';} /** Makes a filename absolute from a relative filename. \code #include [..] fl_chdir("/var/tmp"); fl_filename_absolute(out, sizeof(out), "foo.txt"); // out="/var/tmp/foo.txt" fl_filename_absolute(out, sizeof(out), "./foo.txt"); // out="/var/tmp/foo.txt" fl_filename_absolute(out, sizeof(out), "../log/messages"); // out="/var/log/messages" \endcode \param[out] to resulting absolute filename \param[in] tolen size of the absolute filename buffer \param[in] from relative filename \return 0 if no change, non zero otherwise */ int fl_filename_absolute(char *to, int tolen, const char *from) { return Fl::system_driver()->filename_absolute(to, tolen, from); } /** \cond DriverDev \addtogroup DriverDeveloper \{ */ int Fl_System_Driver::filename_absolute(char *to, int tolen, const char *from) { if (isdirsep(*from) || *from == '|') { strlcpy(to, from, tolen); return 0; } char *a; char *temp = new char[tolen]; const char *start = from; a = fl_getcwd(temp, tolen); if (!a) { strlcpy(to, from, tolen); delete[] temp; return 0; } a = temp+strlen(temp); /* remove trailing '/' in current working directory */ if (isdirsep(*(a-1))) a--; /* remove intermediate . and .. names: */ while (*start == '.') { if (start[1]=='.' && (isdirsep(start[2]) || start[2]==0) ) { // found "..", remove the last directory segment form cwd char *b; for (b = a-1; b >= temp && !isdirsep(*b); b--) {/*empty*/} if (b < temp) break; a = b; if (start[2]==0) start += 2; // Skip to end of path else start += 3; // Skip over dir separator } else if (isdirsep(start[1])) { // found "./" in path, just skip it start += 2; } else if (!start[1]) { // found "." at end of path, just skip it start ++; break; } else break; } *a++ = '/'; strlcpy(a,start,tolen - (a - temp)); strlcpy(to, temp, tolen); delete[] temp; return 1; } /** \} \endcond */ /** Makes a filename relative to the current working directory. \code #include [..] fl_chdir("/var/tmp/somedir"); // set cwd to /var/tmp/somedir [..] char out[FL_PATH_MAX]; fl_filename_relative(out, sizeof(out), "/var/tmp/somedir/foo.txt"); // out="foo.txt", return=1 fl_filename_relative(out, sizeof(out), "/var/tmp/foo.txt"); // out="../foo.txt", return=1 fl_filename_relative(out, sizeof(out), "foo.txt"); // out="foo.txt", return=0 (no change) fl_filename_relative(out, sizeof(out), "./foo.txt"); // out="./foo.txt", return=0 (no change) fl_filename_relative(out, sizeof(out), "../foo.txt"); // out="../foo.txt", return=0 (no change) \endcode \param[out] to resulting relative filename \param[in] tolen size of the relative filename buffer \param[in] from absolute filename \return 0 if no change, non zero otherwise */ int // O - 0 if no change, 1 if changed fl_filename_relative(char *to, // O - Relative filename int tolen, // I - Size of "to" buffer const char *from) // I - Absolute filename { char cwd_buf[FL_PATH_MAX]; // Current directory // get the current directory and return if we can't if (!fl_getcwd(cwd_buf, sizeof(cwd_buf))) { strlcpy(to, from, tolen); return 0; } return fl_filename_relative(to, tolen, from, cwd_buf); } /** Makes a filename relative to any other directory. \param[out] to resulting relative filename \param[in] tolen size of the relative filename buffer \param[in] from absolute filename \param[in] base relative to this absolute path \return 0 if no change, non zero otherwise */ int // O - 0 if no change, 1 if changed fl_filename_relative(char *to, // O - Relative filename int tolen, // I - Size of "to" buffer const char *from, // I - Absolute filename const char *base) { // I - Find path relative to this path return Fl::system_driver()->filename_relative(to, tolen, from, base); } /** \cond DriverDev \addtogroup DriverDeveloper \{ */ int // O - 0 if no change, 1 if changed Fl_System_Driver::filename_relative(char *to, // O - Relative filename int tolen, // I - Size of "to" buffer const char *from, // I - Absolute filename const char *base) // I - Find path relative to this path { char *newslash; // Directory separator const char *slash; // Directory separator char *cwd = 0L, *cwd_buf = 0L; if (base) cwd = cwd_buf = fl_strdup(base); // return if "from" is not an absolute path if (from[0] == '\0' || !isdirsep(*from)) { strlcpy(to, from, tolen); if (cwd_buf) free(cwd_buf); return 0; } // return if "cwd" is not an absolute path if (!cwd || cwd[0] == '\0' || !isdirsep(*cwd)) { strlcpy(to, from, tolen); if (cwd_buf) free(cwd_buf); return 0; } // test for the exact same string and return "." if so if (!strcmp(from, cwd)) { strlcpy(to, ".", tolen); free(cwd_buf); return (1); } // compare both path names until we find a difference for (slash = from, newslash = cwd; *slash != '\0' && *newslash != '\0'; slash ++, newslash ++) if (isdirsep(*slash) && isdirsep(*newslash)) continue; else if (*slash != *newslash) break; // skip over trailing slashes if ( *newslash == '\0' && *slash != '\0' && !isdirsep(*slash) &&(newslash==cwd || !isdirsep(newslash[-1])) ) newslash--; // now go back to the first character of the first differing paths segment while (!isdirsep(*slash) && slash > from) slash --; if (isdirsep(*slash)) slash ++; // do the same for the current dir if (isdirsep(*newslash)) newslash --; if (*newslash != '\0') while (!isdirsep(*newslash) && newslash > cwd) newslash --; // prepare the destination buffer to[0] = '\0'; to[tolen - 1] = '\0'; // now add a "previous dir" sequence for every following slash in the cwd while (*newslash != '\0') { if (isdirsep(*newslash)) strlcat(to, "../", tolen); newslash ++; } // finally add the differing path from "from" strlcat(to, slash, tolen); free(cwd_buf); return 1; } /** \} \endcond */