2 filetools.C (former paths.C) - part of LyX project
3 General path-mangling functions
4 Copyright 1996 Ivan Schreter
5 Parts Copyright 1996 Dirk Niggemann
6 Parts Copyright 1985, 1990, 1993 Free Software Foundation, Inc.
7 Parts Copyright 1996 Asger Alstrup
11 lyx-filetool.C : tools functions for file/path handling
12 this file is part of LyX, the High Level Word Processor
13 Copyright 1995-1996, Matthias Ettrich and the LyX Team
27 #pragma implementation "filetools.h"
35 #include "support/lstrings.h"
36 #include "support/systemcall.h"
38 #include "filetools.h"
39 #include "LSubstring.h"
40 #include "frontends/Alert.h"
42 #include "support/path.h" // I know it's OS/2 specific (SMiyata)
47 // Which part of this is still necessary? (JMarc).
50 # define NAMLEN(dirent) strlen((dirent)->d_name)
52 # define dirent direct
53 # define NAMLEN(dirent) (dirent)->d_namlen
55 # include <sys/ndir.h>
71 extern string system_lyxdir;
72 extern string build_lyxdir;
73 extern string user_lyxdir;
74 extern string system_tempdir;
75 extern string system_packageList;
78 bool IsLyXFilename(string const & filename)
80 return suffixIs(lowercase(filename), ".lyx");
84 bool IsSGMLFilename(string const & filename)
86 return suffixIs(lowercase(filename), ".sgml");
90 // Substitutes spaces with underscores in filename (and path)
91 string const MakeLatexName(string const & file)
93 string name = OnlyFilename(file);
94 string const path = OnlyPath(file);
96 for (string::size_type i = 0; i < name.length(); ++i) {
97 name[i] &= 0x7f; // set 8th bit to 0
100 // ok so we scan through the string twice, but who cares.
101 string const keep("abcdefghijklmnopqrstuvwxyz"
102 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
103 "@!\"'()*+,-./0123456789:;<=>?[]`|");
105 string::size_type pos = 0;
106 while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
109 return AddName(path, name);
113 // Substitutes spaces with underscores in filename (and path)
114 string const QuoteName(string const & name)
116 return (os::shell() == os::UNIX) ?
122 // Is a file readable ?
123 bool IsFileReadable (string const & path)
126 if (file.isOK() && file.isRegular() && file.readable())
133 // Is a file read_only?
134 // return 1 read-write
136 // -1 error (doesn't exist, no access, anything else)
137 int IsFileWriteable (string const & path)
141 if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
143 if (fi.readable()) // read-only
145 return -1; // everything else.
149 //returns true: dir writeable
150 // false: not writeable
151 bool IsDirWriteable (string const & path)
153 lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
155 string const tmpfl(lyx::tempName(path, "lyxwritetest"));
165 // Uses a string of paths separated by ";"s to find a file to open.
166 // Can't cope with pathnames with a ';' in them. Returns full path to file.
167 // If path entry begins with $$LyX/, use system_lyxdir
168 // If path entry begins with $$User/, use user_lyxdir
169 // Example: "$$User/doc;$$LyX/doc"
170 string const FileOpenSearch (string const & path, string const & name,
173 string real_file, path_element;
174 bool notfound = true;
175 string tmppath = split(path, path_element, ';');
177 while (notfound && !path_element.empty()) {
178 path_element = os::slashify_path(path_element);
179 if (!suffixIs(path_element, '/'))
181 path_element = subst(path_element, "$$LyX", system_lyxdir);
182 path_element = subst(path_element, "$$User", user_lyxdir);
184 real_file = FileSearch(path_element, name, ext);
186 if (real_file.empty()) {
188 tmppath = split(tmppath, path_element, ';');
189 } while (!tmppath.empty() && path_element.empty());
195 if (ext.empty() && notfound) {
196 real_file = FileOpenSearch(path, name, "exe");
197 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
204 /// Returns a vector of all files in directory dir having extension ext.
205 vector<string> const DirList(string const & dir, string const & ext)
207 // This is a non-error checking C/system implementation
208 string extension(ext);
209 if (!extension.empty() && extension[0] != '.')
210 extension.insert(0, ".");
211 vector<string> dirlist;
212 DIR * dirp = ::opendir(dir.c_str());
215 << "Directory \"" << dir
216 << "\" does not exist to DirList." << endl;
221 while ((dire = ::readdir(dirp))) {
222 string const fil = dire->d_name;
223 if (suffixIs(fil, extension)) {
224 dirlist.push_back(fil);
229 /* I would have prefered to take a vector<string>& as parameter so
230 that we could avoid the copy of the vector when returning.
232 dirlist.swap(argvec);
233 to avoid the copy. (Lgb)
235 /* A C++ implementaion will look like this:
236 string extension(ext);
237 if (extension[0] != '.') extension.insert(0, ".");
238 vector<string> dirlist;
239 directory_iterator dit("dir");
240 while (dit != directory_iterator()) {
241 string fil = dit->filename;
242 if (prefixIs(fil, extension)) {
243 dirlist.push_back(fil);
247 dirlist.swap(argvec);
253 // Returns the real name of file name in directory path, with optional
255 string const FileSearch(string const & path, string const & name,
258 // if `name' is an absolute path, we ignore the setting of `path'
259 // Expand Environmentvariables in 'name'
260 string const tmpname = ReplaceEnvironmentPath(name);
261 string fullname = MakeAbsPath(tmpname, path);
262 // search first without extension, then with it.
263 if (IsFileReadable(fullname))
265 else if (ext.empty())
267 else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
270 if (IsFileReadable(fullname))
278 // Search the file name.ext in the subdirectory dir of
280 // 2) build_lyxdir (if not empty)
282 string const LibFileSearch(string const & dir, string const & name,
285 string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext);
286 if (!fullname.empty())
289 if (!build_lyxdir.empty())
290 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
291 if (!fullname.empty())
294 return FileSearch(AddPath(system_lyxdir, dir), name, ext);
299 i18nLibFileSearch(string const & dir, string const & name,
302 // this comment is from intl/dcigettext.c. We try to mimick this
304 /* The highest priority value is the `LANGUAGE' environment
305 variable. But we don't use the value if the currently
306 selected locale is the C locale. This is a GNU extension. */
308 string const lc_all = GetEnv("LC_ALL");
309 string lang = GetEnv("LANGUAGE");
310 if (lang.empty() || lc_all == "C") {
313 lang = GetEnv("LANG");
317 lang = token(lang, '_', 0);
319 if (lang.empty() || lang == "C")
320 return LibFileSearch(dir, name, ext);
322 string const tmp = LibFileSearch(dir, lang + '_' + name,
327 return LibFileSearch(dir, name, ext);
332 string const GetEnv(string const & envname)
334 // f.ex. what about error checking?
335 char const * const ch = getenv(envname.c_str());
336 string const envstr = !ch ? "" : ch;
341 string const GetEnvPath(string const & name)
344 string const pathlist = subst(GetEnv(name), ':', ';');
346 string const pathlist = os::slashify_path(GetEnv(name));
348 return strip(pathlist, ';');
352 bool PutEnv(string const & envstr)
354 // CHECK Look at and fix this.
355 // f.ex. what about error checking?
358 // this leaks, but what can we do about it?
359 // Is doing a getenv() and a free() of the older value
360 // a good idea? (JMarc)
361 // Actually we don't have to leak...calling putenv like this
362 // should be enough: ... and this is obviously not enough if putenv
363 // does not make a copy of the string. It is also not very wise to
364 // put a string on the free store. If we have to leak we should do it
366 char * leaker = new char[envstr.length() + 1];
367 envstr.copy(leaker, envstr.length());
368 leaker[envstr.length()] = '\0';
369 int const retval = lyx::putenv(leaker);
371 // If putenv does not make a copy of the char const * this
372 // is very dangerous. OTOH if it does take a copy this is the
374 // The only implementation of putenv that I have seen does not
375 // allocate memory. _And_ after testing the putenv in glibc it
376 // seems that we need to make a copy of the string contents.
377 // I will enable the above.
378 //int retval = lyx::putenv(envstr.c_str());
382 string const str = envstr.split(varname,'=');
383 int const retval = ::setenv(varname.c_str(), str.c_str(), true);
385 // No environment setting function. Can this happen?
386 int const retval = 1; //return an error condition.
393 bool PutEnvPath(string const & envstr)
395 return PutEnv(envstr);
401 int DeleteAllFilesInDir (string const & path)
403 // I have decided that we will be using parts from the boost
404 // library. Check out http://www.boost.org/
405 // For directory access we will then use the directory_iterator.
406 // Then the code will be something like:
407 // directory_iterator dit(path);
408 // directory_iterator dend;
409 // if (dit == dend) {
410 // Alert::err_alert(_("Error! Cannot open directory:"), path);
413 // for (; dit != dend; ++dit) {
414 // string filename(*dit);
415 // if (filename == "." || filename == "..")
417 // string unlinkpath(AddName(path, filename));
418 // if (lyx::unlink(unlinkpath))
419 // Alert::err_alert(_("Error! Could not remove file:"),
423 DIR * dir = ::opendir(path.c_str());
425 Alert::err_alert (_("Error! Cannot open directory:"), path);
429 int return_value = 0;
430 while ((de = readdir(dir))) {
431 string const temp = de->d_name;
432 if (temp == "." || temp == "..")
434 string const unlinkpath = AddName (path, temp);
436 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath
440 FileInfo fi(unlinkpath);
441 if (fi.isOK() && fi.isDir())
442 deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
443 deleted &= (lyx::unlink(unlinkpath) == 0);
445 Alert::err_alert(_("Error! Could not remove file:"),
455 string const CreateTmpDir(string const & tempdir, string const & mask)
458 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
459 << "CreateTmpDir: mask=`" << mask << "'" << endl;
461 string const tmpfl(lyx::tempName(tempdir, mask));
462 // lyx::tempName actually creates a file to make sure that it
463 // stays unique. So we have to delete it before we can create
464 // a dir with the same name. Note also that we are not thread
465 // safe because of the gap between unlink and mkdir. (Lgb)
466 lyx::unlink(tmpfl.c_str());
468 if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
469 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
473 return MakeAbsPath(tmpfl);
477 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
482 if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
485 if (lyx::rmdir(tmpdir)) {
486 Alert::err_alert(_("Error! Couldn't delete temporary directory:"),
496 string const CreateBufferTmpDir(string const & pathfor)
499 static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
500 // We are in our own directory. Why bother to mangle name?
501 // In fact I wrote this code to circumvent a problematic behaviour (bug?)
503 string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
504 if (lyx::mkdir(tmpfl, 0777)) {
505 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
513 int DestroyBufferTmpDir(string const & tmpdir)
515 return DestroyTmpDir(tmpdir, true);
519 string const CreateLyXTmpDir(string const & deflt)
521 if ((!deflt.empty()) && (deflt != "/tmp")) {
522 if (lyx::mkdir(deflt, 0777)) {
526 return CreateTmpDir(deflt, "lyx_tmpdir");
533 return CreateTmpDir("/tmp", "lyx_tmpdir");
538 int DestroyLyXTmpDir(string const & tmpdir)
540 return DestroyTmpDir (tmpdir, false); // Why false?
544 // Creates directory. Returns true if succesfull
545 bool createDirectory(string const & path, int permission)
547 string temp(strip(os::slashify_path(path), '/'));
550 Alert::alert(_("Internal error!"),
551 _("Call to createDirectory with invalid name"));
555 if (lyx::mkdir(temp, permission)) {
556 Alert::err_alert (_("Error! Couldn't create directory:"), temp);
563 // Strip filename from path name
564 string const OnlyPath(string const & Filename)
566 // If empty filename, return empty
567 if (Filename.empty()) return Filename;
569 // Find last / or start of filename
570 string::size_type j = Filename.rfind('/');
571 if (j == string::npos)
573 return Filename.substr(0, j + 1);
577 // Convert relative path into absolute path based on a basepath.
578 // If relpath is absolute, just use that.
579 // If basepath is empty, use CWD as base.
580 string const MakeAbsPath(string const & RelPath, string const & BasePath)
582 // checks for already absolute path
583 if (os::is_absolute_path(RelPath))
586 // Copies given paths
587 string TempRel(os::slashify_path(RelPath));
588 // Since TempRel is NOT absolute, we can safely replace "//" with "/"
589 TempRel = subst(TempRel, "//", "/");
593 if (os::is_absolute_path(BasePath))
596 TempBase = AddPath(lyx::getcwd(), BasePath);
598 // Handle /./ at the end of the path
599 while (suffixIs(TempBase, "/./"))
600 TempBase.erase(TempBase.length() - 2);
602 // processes relative path
603 string RTemp(TempRel);
606 while (!RTemp.empty()) {
608 RTemp = split(RTemp, Temp, '/');
610 if (Temp == ".") continue;
612 // Remove one level of TempBase
613 string::difference_type i = TempBase.length() - 2;
616 while (i > 0 && TempBase[i] != '/') --i;
620 while (i > 2 && TempBase[i] != '/') --i;
623 TempBase.erase(i, string::npos);
626 } else if (Temp.empty() && !RTemp.empty()) {
627 TempBase = os::current_root() + RTemp;
630 // Add this piece to TempBase
631 if (!suffixIs(TempBase, '/'))
637 // returns absolute path
638 return os::slashify_path(TempBase);
642 // Correctly append filename to the pathname.
643 // If pathname is '.', then don't use pathname.
644 // Chops any path of filename.
645 string const AddName(string const & path, string const & fname)
648 string const basename(OnlyFilename(fname));
652 if (path != "." && path != "./" && !path.empty()) {
653 buf = os::slashify_path(path);
654 if (!suffixIs(path, '/'))
658 return buf + basename;
662 // Strips path from filename
663 string const OnlyFilename(string const & fname)
668 string::size_type j = fname.rfind('/');
669 if (j == string::npos) // no '/' in fname
673 return fname.substr(j + 1);
677 /// Returns true is path is absolute
678 bool AbsolutePath(string const & path)
680 return os::is_absolute_path(path);
685 // Create absolute path. If impossible, don't do anything
686 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
687 string const ExpandPath(string const & path)
689 // checks for already absolute path
690 string RTemp(ReplaceEnvironmentPath(path));
691 if (os::is_absolute_path(RTemp))
695 string const copy(RTemp);
698 RTemp = split(RTemp, Temp, '/');
701 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
704 return GetEnvPath("HOME") + '/' + RTemp;
707 return MakeAbsPath(copy);
709 // Don't know how to handle this
715 // Constracts path/../path
716 // Can't handle "../../" or "/../" (Asger)
717 string const NormalizePath(string const & path)
723 if (os::is_absolute_path(path))
726 // Make implicit current directory explicit
729 while (!RTemp.empty()) {
731 RTemp = split(RTemp, Temp, '/');
735 } else if (Temp == "..") {
736 // Remove one level of TempBase
737 string::difference_type i = TempBase.length() - 2;
738 while (i > 0 && TempBase[i] != '/')
740 if (i >= 0 && TempBase[i] == '/')
741 TempBase.erase(i + 1, string::npos);
745 TempBase += Temp + '/';
749 // returns absolute path
754 string const GetFileContents(string const & fname)
756 FileInfo finfo(fname);
758 ifstream ifs(fname.c_str());
763 return ofs.str().c_str();
766 lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
772 // Search ${...} as Variable-Name inside the string and replace it with
773 // the denoted environmentvariable
774 // Allow Variables according to
775 // variable := '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
778 string const ReplaceEnvironmentPath(string const & path)
781 // CompareChar: Environmentvariables starts with this character
782 // PathChar: Next path component start with this character
783 // while CompareChar found do:
784 // Split String with PathChar
785 // Search Environmentvariable
786 // if found: Replace Strings
788 char const CompareChar = '$';
789 char const FirstChar = '{';
790 char const EndChar = '}';
791 char const UnderscoreChar = '_';
792 string EndString; EndString += EndChar;
793 string FirstString; FirstString += FirstChar;
794 string CompareString; CompareString += CompareChar;
795 string const RegExp("*}*"); // Exist EndChar inside a String?
797 // first: Search for a '$' - Sign.
799 string result1; //(copy); // for split-calls
800 string result0 = split(path, result1, CompareChar);
801 while (!result0.empty()) {
802 string copy1(result0); // contains String after $
804 // Check, if there is an EndChar inside original String.
806 if (!regexMatch(copy1, RegExp)) {
807 // No EndChar inside. So we are finished
808 result1 += CompareString + result0;
814 string res0 = split(copy1, res1, EndChar);
815 // Now res1 holds the environmentvariable
816 // First, check, if Contents is ok.
817 if (res1.empty()) { // No environmentvariable. Continue Loop.
818 result1 += CompareString + FirstString;
822 // check contents of res1
823 char const * res1_contents = res1.c_str();
824 if (*res1_contents != FirstChar) {
825 // Again No Environmentvariable
826 result1 += CompareString;
830 // Check for variable names
831 // Situation ${} is detected as "No Environmentvariable"
832 char const * cp1 = res1_contents + 1;
833 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
835 while (*cp1 && result) {
836 result = isalnum(*cp1) ||
837 (*cp1 == UnderscoreChar);
842 // no correct variable name
843 result1 += CompareString + res1 + EndString;
844 result0 = split(res0, res1, CompareChar);
849 string env(GetEnv(res1_contents + 1));
851 // Congratulations. Environmentvariable found
854 result1 += CompareString + res1 + EndString;
857 result0 = split(res0, res1, CompareChar);
861 } // ReplaceEnvironmentPath
864 // Make relative path out of two absolute paths
865 string const MakeRelPath(string const & abspath, string const & basepath)
866 // Makes relative path out of absolute path. If it is deeper than basepath,
867 // it's easy. If basepath and abspath share something (they are all deeper
868 // than some directory), it'll be rendered using ..'s. If they are completely
869 // different, then the absolute path will be used as relative path.
871 string::size_type const abslen = abspath.length();
872 string::size_type const baselen = basepath.length();
874 string::size_type i = os::common_path(abspath, basepath);
877 // actually no match - cannot make it relative
881 // Count how many dirs there are in basepath above match
882 // and append as many '..''s into relpath
884 string::size_type j = i;
885 while (j < baselen) {
886 if (basepath[j] == '/') {
887 if (j + 1 == baselen)
894 // Append relative stuff from common directory to abspath
895 if (abspath[i] == '/')
897 for (; i < abslen; ++i)
900 if (suffixIs(buf, '/'))
901 buf.erase(buf.length() - 1);
902 // Substitute empty with .
909 // Append sub-directory(ies) to a path in an intelligent way
910 string const AddPath(string const & path, string const & path_2)
913 string const path2 = os::slashify_path(path_2);
915 if (!path.empty() && path != "." && path != "./") {
916 buf = os::slashify_path(path);
917 if (path[path.length() - 1] != '/')
921 if (!path2.empty()) {
922 string::size_type const p2start = path2.find_first_not_of('/');
923 string::size_type const p2end = path2.find_last_not_of('/');
924 string const tmp = path2.substr(p2start, p2end - p2start + 1);
932 Change extension of oldname to extension.
933 Strips path off if no_path == true.
934 If no extension on oldname, just appends.
936 string const ChangeExtension(string const & oldname, string const & extension)
938 string::size_type const last_slash = oldname.rfind('/');
939 string::size_type last_dot = oldname.rfind('.');
940 if (last_dot < last_slash && last_slash != string::npos)
941 last_dot = string::npos;
944 // Make sure the extension starts with a dot
945 if (!extension.empty() && extension[0] != '.')
946 ext= "." + extension;
950 return os::slashify_path(oldname.substr(0, last_dot) + ext);
954 /// Return the extension of the file (not including the .)
955 string const GetExtension(string const & name)
957 string::size_type const last_slash = name.rfind('/');
958 string::size_type const last_dot = name.rfind('.');
959 if (last_dot != string::npos &&
960 (last_slash == string::npos || last_dot > last_slash))
961 return name.substr(last_dot + 1,
962 name.length() - (last_dot + 1));
967 // the different filetypes and what they contain in one of the first lines
968 // (dots are any characters). (Herbert 20020131)
971 // EPS %!PS-Adobe-3.0 EPSF...
977 // PBM P1... or P4 (B/W)
978 // PGM P2... or P5 (Grayscale)
979 // PPM P3... or P6 (color)
980 // PS %!PS-Adobe-2.0 or 1.0, no "EPSF"!
981 // SGI \001\332... (decimal 474)
983 // TIFF II... or MM...
984 // XBM ... static char ...
986 // XWD \000\000\000\151 (0x00006900)
988 // GZIP \037\213\010\010... http://www.ietf.org/rfc/rfc1952.txt
989 // ZIP PK... http://www.halyava.ru/document/ind_arch.htm
990 // Z \037\177 UNIX compress
992 /// return the "extension" which belongs to the contents
993 /// for no knowing contents return the extension. Without
994 /// an extension and unknown contents we return "user"
995 string const getExtFromContents(string const & filename) {
996 if (filename.empty() || !IsFileReadable(filename))
997 return string(); // paranoia check
998 ifstream ifs(filename.c_str());
1000 return string(); // Couldn't open file...
1001 string const gzipStamp = "\037\213\010\010"; // gnuzip
1002 string const zipStamp = "PK"; // PKZIP
1003 string const compressStamp = "\037\177"; // compress
1004 int const max_count = 50; // Maximum strings to read
1005 int count = 0; // Counter of attempts.
1007 bool firstLine = true;
1008 for (; count < max_count; ++count) {
1010 lyxerr[Debug::GRAPHICS] << "filetools(getExtFromContents)\n"
1011 "\tEnd of file reached and it wasn't found a known Type!" << endl;
1016 // at first we check for a zipped file, because this information
1017 // is saved in the first bytes of the file!
1018 // also some graphic formats which save the information
1019 // in the first line, too.
1020 if (str.substr(0,4) == gzipStamp)
1022 string const stamp = str.substr(0,2);
1023 if (stamp == zipStamp)
1025 else if (stamp == compressStamp)
1027 // the graphics part
1028 else if (stamp == "BM")
1030 else if (str.at(0) == 'P') { // PBM family
1031 switch (str.at(1)) {
1045 if (stamp == "\001\332")
1047 else if ((stamp == "II") || (stamp == "MM"))
1049 else if (str.substr(0,3) == "GIF")
1051 // else if ((str.at(3) == 'i') && (str.at(0) == '\000') &&
1052 // (str.at(1) == '\000') && (str.at(2) == '\000'))
1056 if (contains(str,"EPSF")) // dummy, if we have wrong file
1057 return "eps"; // description like "%!PS-Adobe-2.0EPSF"
1058 else if (contains(str,"TGIF"))
1060 else if (contains(str,"Grace"))
1062 else if (contains(str,"JFIF"))
1064 else if (contains(str,"%PDF"))
1066 else if (contains(str,"PNG"))
1068 else if (contains(str,"%!PS-Adobe")) { // eps or ps
1070 if (contains(str,"EPSF"))
1074 } else if (contains(str,"static char"))
1076 else if (contains(str,"XPM"))
1078 else if (contains(str,"BITPIX"))
1081 lyxerr[Debug::GRAPHICS] << "filetools(getExtFromContents)\n"
1082 "\tCouldn't find a known Type!"
1083 "\twill use ext or a \"user\" defined format" << endl;
1084 string const ext(GetExtension(filename));
1091 /// check for zipped file
1092 bool zippedFile(string const & name)
1094 string const type = getExtFromContents(name);
1095 if (contains("gzip zip compress", type) && !type.empty())
1101 string const unzipFile(string const & zipped_file)
1103 string const file = ChangeExtension(zipped_file, string());
1104 string const tempfile = lyx::tempName(string(), file);
1106 string const command = "gunzip -c " + zipped_file + " > " + tempfile;
1108 one.startscript(Systemcall::Wait, command);
1109 // test that command was executed successfully (anon)
1110 // yes, please do. (Lgb)
1115 // Creates a nice compact path for displaying
1117 MakeDisplayPath (string const & path, unsigned int threshold)
1119 string::size_type const l1 = path.length();
1121 // First, we try a relative path compared to home
1122 string const home(GetEnvPath("HOME"));
1123 string relhome = MakeRelPath(path, home);
1125 string::size_type l2 = relhome.length();
1129 // If we backup from home or don't have a relative path,
1130 // this try is no good
1131 if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1132 // relative path was no good, just use the original path
1139 // Is the path too long?
1140 if (l2 > threshold) {
1146 while (relhome.length() > threshold)
1147 relhome = split(relhome, temp, '/');
1149 // Did we shortend everything away?
1150 if (relhome.empty()) {
1151 // Yes, filename in itself is too long.
1152 // Pick the start and the end of the filename.
1153 relhome = OnlyFilename(path);
1154 string const head = relhome.substr(0, threshold/2 - 3);
1156 l2 = relhome.length();
1158 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1159 relhome = head + "..." + tail;
1162 return prefix + relhome;
1166 bool LyXReadLink(string const & File, string & Link)
1168 char LinkBuffer[512];
1169 // Should be PATH_MAX but that needs autconf support
1170 int const nRead = ::readlink(File.c_str(),
1171 LinkBuffer, sizeof(LinkBuffer) - 1);
1174 LinkBuffer[nRead] = '\0'; // terminator
1182 typedef pair<int, string> cmdret;
1184 cmdret const do_popen(string const & cmd)
1186 // One question is if we should use popen or
1187 // create our own popen based on fork, exec, pipe
1188 // of course the best would be to have a
1189 // pstream (process stream), with the
1190 // variants ipstream, opstream
1192 FILE * inf = ::popen(cmd.c_str(), os::read_mode());
1194 // (Claus Hentschel) Check if popen was succesful ;-)
1196 return make_pair(-1, string());
1201 ret += static_cast<char>(c);
1204 int const pret = pclose(inf);
1205 return make_pair(pret, ret);
1211 string const findtexfile(string const & fil, string const & /*format*/)
1213 /* There is no problem to extend this function too use other
1214 methods to look for files. It could be setup to look
1215 in environment paths and also if wanted as a last resort
1216 to a recursive find. One of the easier extensions would
1217 perhaps be to use the LyX file lookup methods. But! I am
1218 going to implement this until I see some demand for it.
1222 // If the file can be found directly, we just return a
1223 // absolute path version of it.
1224 if (FileInfo(fil).exist())
1225 return MakeAbsPath(fil);
1227 // No we try to find it using kpsewhich.
1228 // It seems from the kpsewhich manual page that it is safe to use
1229 // kpsewhich without --format: "When the --format option is not
1230 // given, the search path used when looking for a file is inferred
1231 // from the name given, by looking for a known extension. If no
1232 // known extension is found, the search path for TeX source files
1234 // However, we want to take advantage of the format sine almost all
1235 // the different formats has environment variables that can be used
1236 // to controll which paths to search. f.ex. bib looks in
1237 // BIBINPUTS and TEXBIB. Small list follows:
1238 // bib - BIBINPUTS, TEXBIB
1240 // graphic/figure - TEXPICTS, TEXINPUTS
1241 // ist - TEXINDEXSTYLE, INDEXSTYLE
1242 // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1244 // tfm - TFMFONTS, TEXFONTS
1245 // This means that to use kpsewhich in the best possible way we
1246 // should help it by setting additional path in the approp. envir.var.
1247 string const kpsecmd = "kpsewhich " + fil;
1249 cmdret const c = do_popen(kpsecmd);
1251 lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1252 << "kpse result = `" << strip(c.second, '\n')
1255 return os::internal_path(strip(strip(c.second, '\n'), '\r'));
1261 void removeAutosaveFile(string const & filename)
1263 string a = OnlyPath(filename);
1265 a += OnlyFilename(filename);
1267 FileInfo const fileinfo(a);
1268 if (fileinfo.exist()) {
1269 if (lyx::unlink(a) != 0) {
1270 Alert::err_alert(_("Could not delete auto-save file!"), a);