/* filetools.C (former paths.C) - part of LyX project General path-mangling functions Copyright (C) 1996 Ivan Schreter Parts Copyright (C) 1996 Dirk Niggemann Parts Copyright (C) 1985, 1990, 1993 Free Software Foundation, Inc. Parts Copyright (C) 1996 Asger Alstrup See also filetools.H. lyx-filetool.C : tools functions for file/path handling this file is part of LyX, the High Level Word Processor copyright (C) 1995-1996, Matthias Ettrich and the LyX Team */ #include #include #include #include // I know it's OS/2 specific (SMiyata) #ifdef __GNUG__ #pragma implementation "filetools.h" #endif #include "filetools.h" #include "lyx_gui_misc.h" #include "FileInfo.h" #include "pathstack.h" // I know it's OS/2 specific (SMiyata) #include "gettext.h" // Which part of this is still necessary? (JMarc). #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif // $Id: filetools.C,v 1.1 1999/09/27 18:44:37 larsbj Exp $ #if !defined(lint) && !defined(WITH_WARNINGS) static char vcid[] = "$Id: filetools.C,v 1.1 1999/09/27 18:44:37 larsbj Exp $"; #endif /* lint */ extern LString system_lyxdir; extern LString build_lyxdir; extern LString user_lyxdir; extern LString system_tempdir; bool IsLyXFilename(LString const & filename) { return filename.contains(".lyx"); } bool IsSGMLFilename(LString const & filename) { return filename.contains(".sgml"); } // Substitutes spaces with underscores in filename (and path) LString SpaceLess(LString const & file) { LString name = OnlyFilename(file); LString path = OnlyPath(file); for (int i=0; id_name; if (temp=="." || temp=="..") continue; LString unlinkpath = AddName (path, temp); lyxerr.debug("Deleting file: " + unlinkpath); if (remove (unlinkpath.c_str())) WriteFSAlert (_("Error! Could not remove file:"), unlinkpath); } closedir (dir); return 0; } static LString CreateTmpDir (LString const & tempdir, LString const & mask) { LString tmpfl = TmpFileName(tempdir, mask); if ((tmpfl.empty()) || mkdir (tmpfl.c_str(), 0777)) { WriteFSAlert(_("Error! Couldn't create temporary directory:"), tempdir); return LString(); } return MakeAbsPath(tmpfl); } static int DestroyTmpDir (LString const & tmpdir, bool Allfiles) { if ((Allfiles) && (DeleteAllFilesInDir (tmpdir))) return -1; if (rmdir(tmpdir.c_str())) { #ifdef __EMX__ if (errno == EBUSY) { chdir(user_lyxdir.c_str()); // They are in the same drive. if (!rmdir(tmpdir.c_str())) return 0; } #endif WriteFSAlert(_("Error! Couldn't delete temporary directory:"), tmpdir); return -1; } return 0; } LString CreateBufferTmpDir (LString const & pathfor) { return CreateTmpDir (pathfor, "lyx_bufrtmp"); } int DestroyBufferTmpDir (LString const & tmpdir) { return DestroyTmpDir (tmpdir, true); } LString CreateLyXTmpDir (LString const & deflt) { LString t; if ((!deflt.empty()) && (deflt!="/tmp")) { if (mkdir (deflt.c_str(), 0777)) { #ifdef __EMX__ PathPush(user_lyxdir); #endif t = CreateTmpDir (deflt.c_str(), "lyx_tmp"); #ifdef __EMX__ PathPop(); #endif return t; } else return deflt; } else { #ifdef __EMX__ PathPush(user_lyxdir); #endif t = CreateTmpDir ("/tmp", "lyx_tmp"); #ifdef __EMX__ PathPop(); #endif return t; } } int DestroyLyXTmpDir (LString const & tmpdir) { return DestroyTmpDir (tmpdir, false); // Why false? } // Creates directory. Returns true if succesfull bool createDirectory(LString const & path, int permission) { LString temp = CleanupPath(path); // Cut off trailing /s temp.strip('/'); if (temp.empty()) { WriteAlert(_("Internal error!"), _("Call to createDirectory with invalid name")); return false; } if (mkdir(temp.c_str(), permission)) { WriteFSAlert (_("Error! Couldn't create directory:"), temp); return false; } return true; } // Returns current working directory LString GetCWD () { int n = 256; // Assume path is less than 256 chars char * err; char * tbuf = new char [n]; LString result; // Safe. Hopefully all getcwds behave this way! while (((err = getcwd (tbuf, n)) == NULL) && (errno == ERANGE)) { // Buffer too small, double the buffersize and try again delete[] tbuf; n = 2*n; tbuf = new char [n]; } if (err) result = tbuf; delete[] tbuf; return result; } // Strip filename from path name LString OnlyPath(LString const &Filename) { // If empty filename, return empty if (Filename.empty()) return Filename; // Find last / or start of filename int j = Filename.length() - 1; LString temp = CleanupPath(Filename); for (; j > 0 && temp[j] != '/'; j--); if (temp[j] != '/') return "./"; else { // Strip to pathname return temp.substring(0, j); } } // Convert relative path into absolute path based on a basepath. // If relpath is absolute, just use that. // If basepath is empty, use CWD as base. LString MakeAbsPath(LString const &RelPath, LString const &BasePath) { // checks for already absolute path if (AbsolutePath(RelPath)) #ifdef __EMX__ if(RelPath[0]!='/' || RelPath[0]!='\\') #endif return RelPath; // Copies given paths LString TempRel = CleanupPath(RelPath); LString TempBase; if (!BasePath.empty()) { #ifndef __EMX__ TempBase = BasePath; #else char* with_drive = new char[_MAX_PATH]; _abspath(with_drive, BasePath.c_str(), _MAX_PATH); TempBase = with_drive; delete[] with_drive; #endif } else TempBase = GetCWD(); //safer_getcwd(); #ifdef __EMX__ if (AbsolutePath(TempRel)) return TempBase.substring(0,1) + TempRel; #endif // Handle /./ at the end of the path while(TempBase.suffixIs("/./")) TempBase.substring(0,TempBase.length()-3); // processes relative path LString RTemp = TempRel; LString Temp; while (!RTemp.empty()) { // Split by next / RTemp.split(Temp, '/'); if (Temp==".") continue; if (Temp=="..") { // Remove one level of TempBase int i = TempBase.length()-2; #ifndef __EMX__ if (i<0) i=0; while (i>0 && TempBase[i] != '/') i--; if (i>0) #else if (i<2) i=2; while (i>2 && TempBase[i] != '/') i--; if (i>2) #endif TempBase.substring(0, i); else TempBase += '/'; } else { // Add this piece to TempBase if (!TempBase.suffixIs('/')) TempBase += '/'; TempBase += Temp; } } // returns absolute path return TempBase; } // Correctly append filename to the pathname. // If pathname is '.', then don't use pathname. // Chops any path of filename. LString AddName(LString const &Path, LString const &FileName) { // Get basename LString Basename = OnlyFilename(FileName); LString buf; if (Path != "." && Path != "./" && !Path.empty()) { buf = CleanupPath(Path); if (!Path.suffixIs('/')) buf += '/'; } return buf + Basename; } // Strips path from filename LString OnlyFilename(LString const &Filename) { // If empty filename, return empty if (Filename.empty()) return Filename; int j; // Find last / or start of filename LString temp = CleanupPath(Filename); for (j=Filename.length()-1; temp[j] != '/' && j>0; j--); // Skip potential / if (j!=0) j++; // Strip to basename return temp.substring(j, temp.length()-1); } // Is a filename/path absolute? bool AbsolutePath(LString const &path) { #ifndef __EMX__ return (!path.empty() && path[0]=='/'); #else return (!path.empty() && (path[0]=='/' || (isalpha((unsigned char) path[0]) && path[1]==':'))); #endif } // Create absolute path. If impossible, don't do anything // Supports ./ and ~/. Later we can add support for ~logname/. (Asger) LString ExpandPath(LString const &path) { // checks for already absolute path LString RTemp = ReplaceEnvironmentPath(path); if (AbsolutePath(RTemp)) return RTemp; LString Temp; LString copy(RTemp); // Split by next / RTemp.split(Temp, '/'); if (Temp==".") { return GetCWD() + '/' + RTemp; } else if (Temp=="~") { return getEnvPath("HOME") + '/' + RTemp; } else if (Temp=="..") { return MakeAbsPath(copy); } else // Don't know how to handle this return copy; } // Normalize a path // Constracts path/../path // Can't handle "../../" or "/../" (Asger) LString NormalizePath(LString const &path) { LString TempBase; LString RTemp; LString Temp; if (AbsolutePath(path)) RTemp = path; else // Make implicit current directory explicit RTemp = "./" +path; while (!RTemp.empty()) { // Split by next / RTemp.split(Temp, '/'); if (Temp==".") { TempBase = "./"; } else if (Temp=="..") { // Remove one level of TempBase int i = TempBase.length()-2; while (i>0 && TempBase[i] != '/') i--; if (i>=0 && TempBase[i] == '/') TempBase.substring(0, i); else TempBase = "../"; } else { TempBase += Temp + '/'; } } // returns absolute path return TempBase; } LString CleanupPath(LString const &path) { #ifdef __EMX__ /* SMiyata: This should fix searchpath bug. */ LString temppath(path); temppath.subst('\\', '/'); temppath.subst("//", "/"); temppath.lowercase(); return temppath; #else // On unix, nothing to do return path; #endif } // // Search ${...} as Variable-Name inside the string and replace it with // the denoted environmentvariable // Allow Variables according to // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}' // LString ReplaceEnvironmentPath(LString const &path) { // // CompareChar: Environmentvariables starts with this character // PathChar: Next path component start with this character // while CompareChar found do: // Split String with PathChar // Search Environmentvariable // if found: Replace Strings // const char CompareChar = '$'; const char FirstChar = '{'; const char EndChar = '}'; const char UnderscoreChar = '_'; const LString EndString(EndChar); const LString FirstString(FirstChar); const LString CompareString(CompareChar); const LString RegExp("*}*"); // Exist EndChar inside a String? if (path.empty()) return path; // nothing to do. // first: Search for a '$' - Sign. LString copy(path); LString result1(copy); // for split-calls LString result0 = copy.split(result1, CompareChar); while (!result0.empty()) { LString copy1(result0); // contains String after $ // Check, if there is an EndChar inside original String. if (!copy1.regexMatch(RegExp)) { // No EndChar inside. So we are finished result1 += CompareString + result0; result0 = LString(); continue; } LString res1; LString res0 = copy1.split(res1, EndChar); // Now res1 holds the environmentvariable // First, check, if Contents is ok. if (res1.empty()) { // No environmentvariable. Continue Loop. result1 += CompareString + FirstString; result0 = res0; continue; } // check contents of res1 const char *res1_contents = res1.c_str(); if (*res1_contents != FirstChar) { // Again No Environmentvariable result1 += CompareString; result0 = res0; } // Check for variable names // Situation ${} is detected as "No Environmentvariable" const char *cp1 = res1_contents+1; bool result = isalpha((unsigned char) *cp1) || (*cp1 == UnderscoreChar); ++cp1; while (*cp1 && result) { result = isalnum((unsigned char) *cp1) || (*cp1 == UnderscoreChar); ++cp1; } if (!result) { // no correct variable name result1 += CompareString + res1 + EndString; result0 = res0.split(res1, CompareChar); result1 += res1; continue; } char *env = getenv(res1_contents+1); if (env) { // Congratulations. Environmentvariable found result1 += env; } else { result1 += CompareString + res1 + EndString; } // Next $-Sign? result0 = res0.split(res1, CompareChar); result1 += res1; } return result1; } // ReplaceEnvironmentPath // Make relative path out of two absolute paths LString MakeRelPath(LString const & abspath0, LString const & basepath0) // Makes relative path out of absolute path. If it is deeper than basepath, // it's easy. If basepath and abspath share something (they are all deeper // than some directory), it'll be rendered using ..'s. If they are completely // different, then the absolute path will be used as relative path. { // This is a hack. It should probaly be done in another way. Lgb. LString abspath = CleanupPath(abspath0); LString basepath = CleanupPath(basepath0); if (abspath.empty()) return ""; const int abslen = abspath.length(); const int baselen = basepath.length(); // Find first different character int i = 0; while (i < abslen && i < baselen && abspath[i] == basepath[i]) ++i; // Go back to last / if (i < abslen && i < baselen || (i=0 && oldname[dot]!='.' && oldname[dot]!='/'; dot--); if (dot==-1 || oldname[dot]!='.') // If no extension was found, we use the end of the string dot = n; else // Remove the dot, because the new extension includes it dot--; // "path" points to last / or 0 if path is wanted int path = 0; if (no_path) { for (path=dot; path && oldname[path]!='/';path--); if (oldname[path]=='/') path++; } else path = 0; LString result = oldname; result.substring(path,dot); if (!ext.empty()) result += ext; return result; } // Creates a nice compact path for displaying LString MakeDisplayPath (LString const & path, int threshold) { const int l1 = path.length(); // First, we try a relative path compared to home LString home = getEnvPath("HOME"); LString relhome = MakeRelPath(path, home); int l2 = relhome.length(); LString prefix; // If we backup from home or don't have a relative path, // this try is no good if (relhome.prefixIs("../") || AbsolutePath(relhome)) { // relative path was no good, just use the original path relhome = path; l2 = l1; } else { prefix = "~/"; } // Is the path too long? if (l2 > threshold) { // Yes, shortend it prefix += ".../"; LString temp; while (relhome.length()>threshold) relhome.split(temp, '/'); // Did we shortend everything away? if (relhome.empty()) { // Yes, filename in itself is too long. // Pick the start and the end of the filename. relhome = OnlyFilename(path); LString head = relhome; head.substring(0, threshold/2 - 3); LString tail = relhome; l2 = tail.length(); tail.substring(l2 - threshold/2 -2, l2 - 1); relhome = head + "..." + tail; } } return prefix + relhome; } bool LyXReadLink(LString const & File, LString & Link) { char LinkBuffer[512]; // Should be PATH_MAX but that needs autconf support int nRead; nRead = readlink(File.c_str(), LinkBuffer,sizeof(LinkBuffer)-1); if (nRead <= 0) return false; LinkBuffer[nRead] = 0; Link = LinkBuffer; return true; }