SSL filetools.C
Interaktion und Portierbarkeitunbekannt
/* 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 <config.h>
#include <stdlib.h> #include <ctype.h> #include <errno.h> // I know it's OS/2 specific (SMiyata)
#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 <dirent.h> # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if HAVE_SYS_NDIR_H # include <sys/ndir.h> # endif # if HAVE_SYS_DIR_H # include <sys/dir.h> # endif # if HAVE_NDIR_H # include <ndir.h> # endif #endif
// Substitutes spaces with underscores in filename (and path)
LString SpaceLess(LString const & file)
{
LString name = OnlyFilename(file);
LString path = OnlyPath(file); // Substitute chars that LaTeX can't handle with safe ones
name.subst('~','-');
name.subst('$','S');
name.subst('%','_');
name.subst('\'','_');
// Substitute some more chars that LaTeX or /bin/sh can't handle -ck-
name.subst('`', '_');
name.subst('"', '_');
name.subst('&', 'G');
name.subst('|', 'I');
name.subst(';', ':');
name.subst('(', 'c');
name.subst(')', 'C');
name.subst('<', 'k');
name.subst('>', 'K');
LString temp = AddName(path, name); // Replace spaces with underscores, also in directory
temp.subst(' ','_');
return temp;
}
/// Returns an unique name to be used as a temporary file.
LString TmpFileName(LString const & dir, LString const & mask)
{// With all these temporary variables, it should be safe enough :-) (JMarc)
LString tmpdir; if (dir.empty())
tmpdir = system_tempdir; else
tmpdir = dir;
LString tmpfl = AddName(tmpdir, mask);
// find a uniq postfix for the filename... // using the pid, and...
tmpfl += int(getpid()); // a short string...
LString ret;
FileInfo fnfo; for (int a='a'; a<= 'z'; a++) for (int b='a'; b<= 'z'; b++) for (int c='a'; c<= 'z'; c++) { // if this is not enough I have no idea what // to do.
ret = tmpfl + char(a) + char(b) + char(c); // check if the file exist if (!fnfo.newFile(ret).exist()) return ret;
}
lyxerr.print("Not able to find a uniq tmpfile name."); return LString();
}
// Is a file readable ? bool IsFileReadable (LString const & path)
{
FileInfo file(path); if (file.isOK() && file.readable()) returntrue; else returnfalse;
}
// Is a file read_only? // return 1 read-write // 0 read_only // -1 error (doesn't exist, no access, anything else) int IsFileWriteable (LString const & path)
{
FilePtr fp(path, FilePtr::update); if (!fp()) { if ((errno == EACCES) || (errno == EROFS)) { //fp = FilePtr(path, FilePtr::read);
fp.reopen(path, FilePtr::read); if (fp()) { return 0;
}
} return -1;
} return 1;
}
//returns 1: dir writeable // 0: not writeable // -1: error- couldn't find out int IsDirWriteable (LString const & path)
{
LString tmpfl = TmpFileName(path);
if (tmpfl.empty()) {
WriteFSAlert(_("LyX Internal Error!"),
_("Could not test if directory is writeable")); return -1;
} else {
FilePtr fp(tmpfl, FilePtr::truncate); if (!fp()) { if (errno == EACCES) { return 0;
} else {
WriteFSAlert(_("LyX Internal Error!"),
_("Cannot open directory test file")); return -1;
}
}
} if (remove (tmpfl.c_str())) {
WriteFSAlert(_("LyX Internal Error!"),
_("Created test file but cannot remove it?")); return -1;
} return 1;
}
// Uses a string of paths separated by ";"s to find a file to open. // Can't cope with pathnames with a ';' in them. Returns full path to file. // If path entry begins with $$LyX/, use system_lyxdir // If path entry begins with $$User/, use user_lyxdir // Example: "$$User/doc;$$LyX/doc"
LString FileOpenSearch (LString const & path, LString const & name,
LString const & ext)
{
LString real_file, path_element;
LString tmppath = path; bool notfound = true;
tmppath.split(path_element, ';');
while (notfound && !path_element.empty()) {
if (!path_element.suffixIs('/'))
path_element+='/';
path_element.subst("$$LyX",system_lyxdir);
path_element.subst("$$User",user_lyxdir);
// Returns the real name of file name in directory path, with optional // extension ext.
LString FileSearch(LString const & path, LString const & name,
LString const & ext)
{
LString fullname;
LString tmp;
// if `name' is an absolute path, we ignore the setting of `path' // Expand Environmentvariables in 'name'
LString tmpname = ReplaceEnvironmentPath(name);
fullname = MakeAbsPath(tmpname,path);
// search first without extension, then with it. if (IsFileReadable(fullname)) return fullname; elseif(ext.empty()) return LString(); else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
fullname += '.';
fullname += ext; if (IsFileReadable(fullname)) return fullname; else return LString();
}
}
// Search the file name.ext in the subdirectory dir of // 1) user_lyxdir // 2) build_lyxdir (if not empty) // 3) system_lyxdir
LString LibFileSearch(LString const & dir, LString const & name,
LString const & ext)
{
LString fullname = FileSearch(AddPath(user_lyxdir,dir), name,
ext); if (!fullname.empty()) return fullname;
if (!build_lyxdir.empty())
fullname = FileSearch(AddPath(build_lyxdir,dir),
name, ext); if (!fullname.empty()) return fullname;
// Should supposedly help with some automountd-stuff... // Fortunately no need to patch for OS/2 support
LString safer_getcwd ()
{
LString temp = getenv ("PWD"); if (temp.empty()) {
temp = getenv ("CWD"); if (temp.empty())
temp = GetCWD();
} return temp;
}
// Returns current working directory
LString GetCWD ()
{ int n = 256; // Assume path is less than 256 chars char * err; char * tbuf = newchar [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 = newchar [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; for (; j > 0 && Filename[j] != '/'; j--);
if (Filename[j] != '/') return"./"; else { // Strip to pathname
LString temp = Filename; 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]!='/') #endif return RelPath;
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 = 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 for (j=Filename.length()-1; Filename[j] != '/' && j>0; j--);
// 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;
} elseif (Temp=="~") { return getEnvPath("HOME") + '/' + RTemp;
} elseif (Temp=="..") { return MakeAbsPath(copy);
} else // Don't know how to handle this return copy;
}
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 = "./";
} elseif (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;
}
// // 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 // constchar CompareChar = '$'; constchar FirstChar = '{'; constchar EndChar = '}'; constchar 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 constchar *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" constchar *cp1 = res1_contents+1; bool result = isalpha((unsignedchar) *cp1) || (*cp1 == UnderscoreChar);
++cp1; while (*cp1 && result) {
result = isalnum((unsignedchar) *cp1) ||
(*cp1 == UnderscoreChar);
++cp1;
}
if (!result) { // no correct variable name
result1 += CompareString + res1 + EndString;
result0 = res0.split(res1, CompareChar);
result1 += res1; continue;
}
// Make relative path out of two absolute paths
LString MakeRelPath(LString const & abspath, LString const & basepath) // 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. if (abspath.empty()) return"";
// 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<abslen && abspath[i] != '/' && i==baselen)
|| (i<baselen && basepath[i] != '/' && i==abslen))
{ if (i) --i; // here was the last match while (i && abspath[i] != '/') --i;
}
if (i == 0) { // actually no match - cannot make it relative return abspath;
}
// Count how many dirs there are in basepath above match // and append as many '..''s into relpath
LString buf; int j = i; while (j < baselen) { if (basepath[j] == '/') { if (j+1 == baselen) break;
buf += "../";
}
++j;
}
// Append relative stuff from common directory to abspath if (abspath[i] == '/') ++i; for (; i<abslen; ++i)
buf += abspath[i]; // Remove trailing / if (buf.suffixIs('/'))
buf.substring(0,buf.length()-2); // Substitute empty with . if (buf.empty())
buf = '.'; return buf;
}
// Append sub-directory(ies) to a path in an intelligent way
LString AddPath(LString const & path, LString const & path2)
{
LString buf;
if (!path.empty() && path != "." && path != "./") {
buf = path; if (!path.suffixIs('/'))
buf += '/';
}
if (!path2.empty()){ int p2start = 0; while (path2[p2start] == '/') p2start++;
int p2end = path2.length()-1; while (path2[p2end] == '/') p2end--;
/* Change extension of oldname to extension. Strips path off if no_path == true. If no extension on oldname, just appends.
*/
LString ChangeExtension(LString const & oldname, LString const & extension, bool no_path)
{ int n = oldname.length()-1; int dot;
// Make sure the extension includes the dot, if not empty
LString ext; if (!extension.empty() && extension[0] != '.')
ext = "." + extension; else
ext = extension;
// Go back to the first dot not crossing any / for (dot=n; dot>=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)
{ constint 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);