]> git.lyx.org Git - features.git/blob - src/support/filetools.C
fix startup failure
[features.git] / src / support / filetools.C
1 /*
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
8         
9         See also filetools.h.
10
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
14
15 */
16
17 #include <config.h>
18
19 #include <cctype>
20
21 #include <utility>
22 #include <fstream>
23
24 #include "Lsstream.h"
25
26 #ifdef __GNUG__
27 #pragma implementation "filetools.h"
28 #endif
29
30 #include <cstdlib>
31 #include <cstdio> 
32 #include <fcntl.h>
33 #include <cerrno>
34 #include "debug.h"
35 #include "support/lstrings.h"
36
37 #include "filetools.h"
38 #include "LSubstring.h"
39 #include "frontends/Alert.h"
40 #include "FileInfo.h"
41 #include "support/path.h"        // I know it's OS/2 specific (SMiyata)
42 #include "gettext.h"
43 #include "lyxlib.h"
44 #include "os.h"
45
46 // Which part of this is still necessary? (JMarc).
47 #if HAVE_DIRENT_H
48 # include <dirent.h>
49 # define NAMLEN(dirent) strlen((dirent)->d_name)
50 #else
51 # define dirent direct
52 # define NAMLEN(dirent) (dirent)->d_namlen
53 # if HAVE_SYS_NDIR_H
54 #  include <sys/ndir.h>
55 # endif
56 # if HAVE_SYS_DIR_H
57 #  include <sys/dir.h>
58 # endif
59 # if HAVE_NDIR_H
60 #  include <ndir.h>
61 # endif
62 #endif
63
64 using std::make_pair;
65 using std::pair;
66 using std::endl;
67 using std::ifstream;
68 using std::vector;
69
70 extern string system_lyxdir;
71 extern string build_lyxdir;
72 extern string user_lyxdir;
73 extern string system_tempdir;
74 extern string system_packageList;
75
76
77 bool IsLyXFilename(string const & filename)
78 {
79         return suffixIs(lowercase(filename), ".lyx");
80 }
81
82
83 bool IsSGMLFilename(string const & filename)
84 {
85         return suffixIs(lowercase(filename), ".sgml");
86 }
87
88
89 // Substitutes spaces with underscores in filename (and path)
90 string const MakeLatexName(string const & file)
91 {
92         string name = OnlyFilename(file);
93         string const path = OnlyPath(file);
94         
95         for (string::size_type i = 0; i < name.length(); ++i) {
96                 name[i] &= 0x7f; // set 8th bit to 0
97         };
98
99         // ok so we scan through the string twice, but who cares.
100         string const keep("abcdefghijklmnopqrstuvwxyz"
101                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
102                 "@!\"'()*+,-./0123456789:;<=>?[]`|");
103         
104         string::size_type pos = 0;
105         while ((pos = name.find_first_not_of(keep, pos)) != string::npos) {
106                 name[pos++] = '_';
107         }
108         return AddName(path, name);
109 }
110
111
112 // Substitutes spaces with underscores in filename (and path)
113 string const QuoteName(string const & name)
114 {
115         return (os::shell() == os::UNIX) ?
116                 "\'" + name + "\'":
117                 "\"" + name + "\"";
118 }
119
120
121 // Is a file readable ?
122 bool IsFileReadable (string const & path)
123 {
124         FileInfo file(path);
125         if (file.isOK() && file.isRegular() && file.readable())
126                 return true;
127         else
128                 return false;
129 }
130
131
132 // Is a file read_only?
133 // return 1 read-write
134 //        0 read_only
135 //       -1 error (doesn't exist, no access, anything else) 
136 int IsFileWriteable (string const & path)
137 {
138         FileInfo fi(path);
139  
140         if (fi.access(FileInfo::wperm|FileInfo::rperm)) // read-write
141                 return 1;
142         if (fi.readable()) // read-only
143                 return 0;
144         return -1; // everything else.
145 }
146
147
148 //returns true: dir writeable
149 //        false: not writeable
150 bool IsDirWriteable (string const & path)
151 {
152         lyxerr[Debug::FILES] << "IsDirWriteable: " << path << endl;
153  
154         string const tmpfl(lyx::tempName(path, "lyxwritetest"));
155
156         if (tmpfl.empty())
157                 return false;
158  
159         lyx::unlink(tmpfl);
160         return true;
161 }
162
163
164 // Uses a string of paths separated by ";"s to find a file to open.
165 // Can't cope with pathnames with a ';' in them. Returns full path to file.
166 // If path entry begins with $$LyX/, use system_lyxdir
167 // If path entry begins with $$User/, use user_lyxdir
168 // Example: "$$User/doc;$$LyX/doc"
169 string const FileOpenSearch (string const & path, string const & name, 
170                              string const & ext)
171 {
172         string real_file, path_element;
173         bool notfound = true;
174         string tmppath = split(path, path_element, ';');
175         
176         while (notfound && !path_element.empty()) {
177                 path_element = os::slashify_path(path_element);
178                 if (!suffixIs(path_element, '/'))
179                         path_element+= '/';
180                 path_element = subst(path_element, "$$LyX", system_lyxdir);
181                 path_element = subst(path_element, "$$User", user_lyxdir);
182                 
183                 real_file = FileSearch(path_element, name, ext);
184                 
185                 if (real_file.empty()) {
186                         do {
187                                 tmppath = split(tmppath, path_element, ';');
188                         } while (!tmppath.empty() && path_element.empty());
189                 } else {
190                         notfound = false;
191                 }
192         }
193 #ifdef __EMX__
194         if (ext.empty() && notfound) {
195                 real_file = FileOpenSearch(path, name, "exe");
196                 if (notfound) real_file = FileOpenSearch(path, name, "cmd");
197         }
198 #endif
199         return real_file;
200 }
201
202
203 /// Returns a vector of all files in directory dir having extension ext.
204 vector<string> const DirList(string const & dir, string const & ext)
205 {
206         // This is a non-error checking C/system implementation
207         string extension(ext);
208         if (!extension.empty() && extension[0] != '.')
209                 extension.insert(0, ".");
210         vector<string> dirlist;
211         DIR * dirp = ::opendir(dir.c_str());
212         if (!dirp) {
213                 lyxerr[Debug::FILES] 
214                         << "Directory \"" << dir
215                         << "\" does not exist to DirList." << endl;
216                 return dirlist;
217         }
218  
219         dirent * dire;
220         while ((dire = ::readdir(dirp))) {
221                 string const fil = dire->d_name;
222                 if (suffixIs(fil, extension)) {
223                         dirlist.push_back(fil);
224                 }
225         }
226         ::closedir(dirp);
227         return dirlist;
228         /* I would have prefered to take a vector<string>& as parameter so
229            that we could avoid the copy of the vector when returning.
230            Then we would use:
231            dirlist.swap(argvec);
232            to avoid the copy. (Lgb)
233         */
234         /* A C++ implementaion will look like this:
235            string extension(ext);
236            if (extension[0] != '.') extension.insert(0, ".");
237            vector<string> dirlist;
238            directory_iterator dit("dir");
239            while (dit != directory_iterator()) {
240                    string fil = dit->filename;
241                    if (prefixIs(fil, extension)) {
242                            dirlist.push_back(fil);
243                    }
244                    ++dit;
245            }
246            dirlist.swap(argvec);
247            return;
248         */
249 }
250
251
252 // Returns the real name of file name in directory path, with optional
253 // extension ext.  
254 string const FileSearch(string const & path, string const & name, 
255                         string const & ext)
256 {
257         // if `name' is an absolute path, we ignore the setting of `path'
258         // Expand Environmentvariables in 'name'
259         string const tmpname = ReplaceEnvironmentPath(name);
260         string fullname = MakeAbsPath(tmpname, path);
261         // search first without extension, then with it.
262         if (IsFileReadable(fullname))
263                 return fullname;
264         else if (ext.empty()) 
265                 return string();
266         else { // Is it not more reasonable to use ChangeExtension()? (SMiyata)
267                 fullname += '.';
268                 fullname += ext;
269                 if (IsFileReadable(fullname))
270                         return fullname;
271                 else 
272                         return string();
273         }
274 }
275
276
277 // Search the file name.ext in the subdirectory dir of
278 //   1) user_lyxdir
279 //   2) build_lyxdir (if not empty)
280 //   3) system_lyxdir
281 string const LibFileSearch(string const & dir, string const & name, 
282                            string const & ext)
283 {
284         string fullname = FileSearch(AddPath(user_lyxdir, dir), name, ext); 
285         if (!fullname.empty())
286                 return fullname;
287         
288         if (!build_lyxdir.empty()) 
289                 fullname = FileSearch(AddPath(build_lyxdir, dir), name, ext);
290         if (!fullname.empty())
291                 return fullname;
292         
293         return FileSearch(AddPath(system_lyxdir, dir), name, ext);
294 }
295
296
297 string const
298 i18nLibFileSearch(string const & dir, string const & name, 
299                   string const & ext)
300 {
301         // this comment is from intl/dcigettext.c. We try to mimick this
302         // behaviour here.  
303         /* The highest priority value is the `LANGUAGE' environment
304            variable. But we don't use the value if the currently
305            selected locale is the C locale. This is a GNU extension. */
306
307         string const lc_all = GetEnv("LC_ALL");
308         string lang = GetEnv("LANGUAGE");
309         if (lang.empty() || lc_all == "C") {
310                 lang = lc_all;
311                 if (lang.empty()) {
312                         lang = GetEnv("LANG");
313                 }
314         }
315         
316         lang = token(lang, '_', 0);
317         
318         if (lang.empty() || lang == "C")
319                 return LibFileSearch(dir, name, ext);
320         else {
321                 string const tmp = LibFileSearch(dir, lang + '_' + name,
322                                                  ext);
323                 if (!tmp.empty())
324                         return tmp;
325                 else
326                         return LibFileSearch(dir, name, ext);
327         }
328 }
329
330
331 string const GetEnv(string const & envname)
332 {
333         // f.ex. what about error checking?
334         char const * const ch = getenv(envname.c_str());
335         string const envstr = !ch ? "" : ch;
336         return envstr;
337 }
338
339
340 string const GetEnvPath(string const & name)
341 {
342 #ifndef __EMX__
343         string const pathlist = subst(GetEnv(name), ':', ';');
344 #else
345         string const pathlist = os::slashify_path(GetEnv(name));
346 #endif
347         return strip(pathlist, ';');
348 }
349
350
351 bool PutEnv(string const & envstr)
352 {
353         // CHECK Look at and fix this.
354         // f.ex. what about error checking?
355
356 #if HAVE_PUTENV
357         // this leaks, but what can we do about it?
358         //   Is doing a getenv() and a free() of the older value 
359         //   a good idea? (JMarc)
360         // Actually we don't have to leak...calling putenv like this
361         // should be enough: ... and this is obviously not enough if putenv
362         // does not make a copy of the string. It is also not very wise to
363         // put a string on the free store. If we have to leak we should do it
364         // like this:
365         char * leaker = new char[envstr.length() + 1];
366         envstr.copy(leaker, envstr.length());
367         leaker[envstr.length()] = '\0';
368         int const retval = lyx::putenv(leaker);
369
370         // If putenv does not make a copy of the char const * this
371         // is very dangerous. OTOH if it does take a copy this is the
372         // best solution.
373         // The  only implementation of putenv that I have seen does not
374         // allocate memory. _And_ after testing the putenv in glibc it
375         // seems that we need to make a copy of the string contents.
376         // I will enable the above.
377         //int retval = lyx::putenv(envstr.c_str());
378 #else
379 #ifdef HAVE_SETENV 
380         string varname;
381         string const str = envstr.split(varname,'=');
382         int const retval = ::setenv(varname.c_str(), str.c_str(), true);
383 #else
384         // No environment setting function. Can this happen?
385         int const retval = 1; //return an error condition.
386 #endif
387 #endif
388         return retval == 0;
389 }
390
391
392 bool PutEnvPath(string const & envstr)
393 {
394         return PutEnv(envstr);
395 }
396
397
398 namespace {
399
400 int DeleteAllFilesInDir (string const & path)
401 {
402         // I have decided that we will be using parts from the boost
403         // library. Check out http://www.boost.org/
404         // For directory access we will then use the directory_iterator.
405         // Then the code will be something like:
406         // directory_iterator dit(path);
407         // directory_iterator dend;
408         // if (dit == dend) {
409         //         Alert::err_alert(_("Error! Cannot open directory:"), path);
410         //         return -1;
411         // }
412         // for (; dit != dend; ++dit) {
413         //         string filename(*dit);
414         //         if (filename == "." || filename == "..")
415         //                 continue;
416         //         string unlinkpath(AddName(path, filename));
417         //         if (lyx::unlink(unlinkpath))
418         //                 Alert::err_alert(_("Error! Could not remove file:"),
419         //                              unlinkpath);
420         // }
421         // return 0;
422         DIR * dir = ::opendir(path.c_str());
423         if (!dir) {
424                 Alert::err_alert (_("Error! Cannot open directory:"), path);
425                 return -1;
426         }
427         struct dirent * de;
428         int return_value = 0;
429         while ((de = readdir(dir))) {
430                 string const temp = de->d_name;
431                 if (temp == "." || temp == "..") 
432                         continue;
433                 string const unlinkpath = AddName (path, temp);
434
435                 lyxerr[Debug::FILES] << "Deleting file: " << unlinkpath 
436                                      << endl;
437
438                 bool deleted = true;
439                 FileInfo fi(unlinkpath);
440                 if (fi.isOK() && fi.isDir())
441                         deleted = (DeleteAllFilesInDir(unlinkpath) == 0);
442                 deleted &= (lyx::unlink(unlinkpath) == 0);
443                 if (!deleted) {
444                         Alert::err_alert(_("Error! Could not remove file:"), 
445                                 unlinkpath);
446                         return_value = -1;
447                 }
448         }
449         closedir(dir);
450         return return_value;
451 }
452
453
454 string const CreateTmpDir(string const & tempdir, string const & mask)
455 {
456         lyxerr[Debug::FILES]
457                 << "CreateTmpDir: tempdir=`" << tempdir << "'\n"
458                 << "CreateTmpDir:    mask=`" << mask << "'" << endl;
459         
460         string const tmpfl(lyx::tempName(tempdir, mask));
461         // lyx::tempName actually creates a file to make sure that it
462         // stays unique. So we have to delete it before we can create
463         // a dir with the same name. Note also that we are not thread
464         // safe because of the gap between unlink and mkdir. (Lgb)
465         lyx::unlink(tmpfl.c_str());
466         
467         if (tmpfl.empty() || lyx::mkdir(tmpfl, 0700)) {
468                 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
469                              tempdir);
470                 return string();
471         }
472         return MakeAbsPath(tmpfl);
473 }
474
475
476 int DestroyTmpDir(string const & tmpdir, bool Allfiles)
477 {
478 #ifdef __EMX__
479         Path p(user_lyxdir);
480 #endif
481         if (Allfiles && DeleteAllFilesInDir(tmpdir)) {
482                 return -1;
483         }
484         if (lyx::rmdir(tmpdir)) { 
485                 Alert::err_alert(_("Error! Couldn't delete temporary directory:"), 
486                              tmpdir);
487                 return -1;
488         }
489         return 0; 
490 }
491
492 } // namespace anon
493
494
495 string const CreateBufferTmpDir(string const & pathfor)
496 {
497         static int count;
498         static string const tmpdir(pathfor.empty() ? os::getTmpDir() : pathfor);
499         // We are in our own directory.  Why bother to mangle name?
500         // In fact I wrote this code to circumvent a problematic behaviour (bug?)
501         // of EMX mkstemp().
502         string const tmpfl = tmpdir + "/lyx_tmpbuf" + tostr(count++);
503         if (lyx::mkdir(tmpfl, 0777)) {
504                 Alert::err_alert(_("Error! Couldn't create temporary directory:"),
505                              tmpdir);
506                 return string();
507         }
508         return tmpfl;
509 }
510
511
512 int DestroyBufferTmpDir(string const & tmpdir)
513 {
514         return DestroyTmpDir(tmpdir, true);
515 }
516
517
518 string const CreateLyXTmpDir(string const & deflt)
519 {
520         if ((!deflt.empty()) && (deflt  != "/tmp")) {
521                 if (lyx::mkdir(deflt, 0777)) {
522 #ifdef __EMX__
523                 Path p(user_lyxdir);
524 #endif
525                         return CreateTmpDir(deflt, "lyx_tmpdir");
526                 } else
527                         return deflt;
528         } else {
529 #ifdef __EMX__
530                 Path p(user_lyxdir);
531 #endif
532                 return CreateTmpDir("/tmp", "lyx_tmpdir");
533         }
534 }
535
536
537 int DestroyLyXTmpDir(string const & tmpdir)
538 {
539         return DestroyTmpDir (tmpdir, false); // Why false?
540 }
541
542
543 // Creates directory. Returns true if succesfull
544 bool createDirectory(string const & path, int permission)
545 {
546         string temp(strip(os::slashify_path(path), '/'));
547
548         if (temp.empty()) {
549                 Alert::alert(_("Internal error!"),
550                            _("Call to createDirectory with invalid name"));
551                 return false;
552         }
553
554         if (lyx::mkdir(temp, permission)) {
555                 Alert::err_alert (_("Error! Couldn't create directory:"), temp);
556                 return false;
557         }
558         return true;
559 }
560
561
562 // Strip filename from path name
563 string const OnlyPath(string const & Filename)
564 {
565         // If empty filename, return empty
566         if (Filename.empty()) return Filename;
567
568         // Find last / or start of filename
569         string::size_type j = Filename.rfind('/');
570         if (j == string::npos)
571                 return "./";
572         return Filename.substr(0, j + 1);
573 }
574
575
576 // Convert relative path into absolute path based on a basepath.
577 // If relpath is absolute, just use that.
578 // If basepath is empty, use CWD as base.
579 string const MakeAbsPath(string const & RelPath, string const & BasePath)
580 {
581         // checks for already absolute path
582         if (os::is_absolute_path(RelPath))
583                 return RelPath;
584
585         // Copies given paths
586         string TempRel(os::slashify_path(RelPath));
587         // Since TempRel is NOT absolute, we can safely replace "//" with "/"
588         TempRel = subst(TempRel, "//", "/");
589
590         string TempBase;
591
592         if (os::is_absolute_path(BasePath))
593                 TempBase = BasePath;
594         else
595                 TempBase = AddPath(lyx::getcwd(), BasePath);
596         
597         // Handle /./ at the end of the path
598         while (suffixIs(TempBase, "/./"))
599                 TempBase.erase(TempBase.length() - 2);
600
601         // processes relative path
602         string RTemp(TempRel);
603         string Temp;
604
605         while (!RTemp.empty()) {
606                 // Split by next /
607                 RTemp = split(RTemp, Temp, '/');
608                 
609                 if (Temp == ".") continue;
610                 if (Temp == "..") {
611                         // Remove one level of TempBase
612                         string::difference_type i = TempBase.length() - 2;
613 #ifndef __EMX__
614                         if (i < 0) i = 0;
615                         while (i > 0 && TempBase[i] != '/') --i;
616                         if (i > 0)
617 #else
618                         if (i < 2) i = 2;
619                         while (i > 2 && TempBase[i] != '/') --i;
620                         if (i > 2)
621 #endif
622                                 TempBase.erase(i, string::npos);
623                         else
624                                 TempBase += '/';
625                 } else if (Temp.empty() && !RTemp.empty()) {
626                                 TempBase = os::current_root() + RTemp;
627                                 RTemp.erase();
628                 } else {
629                         // Add this piece to TempBase
630                         if (!suffixIs(TempBase, '/'))
631                                 TempBase += '/';
632                         TempBase += Temp;
633                 }
634         }
635
636         // returns absolute path
637         return os::slashify_path(TempBase);
638 }
639
640
641 // Correctly append filename to the pathname.
642 // If pathname is '.', then don't use pathname.
643 // Chops any path of filename.
644 string const AddName(string const & path, string const & fname)
645 {
646         // Get basename
647         string const basename(OnlyFilename(fname));
648
649         string buf;
650
651         if (path != "." && path != "./" && !path.empty()) {
652                 buf = os::slashify_path(path);
653                 if (!suffixIs(path, '/'))
654                         buf += '/';
655         }
656
657         return buf + basename;
658 }
659
660
661 // Strips path from filename
662 string const OnlyFilename(string const & fname)
663 {
664         if (fname.empty())
665                 return fname;
666
667         string::size_type j = fname.rfind('/');
668         if (j == string::npos) // no '/' in fname
669                 return fname;
670
671         // Strip to basename
672         return fname.substr(j + 1);
673 }
674
675
676 /// Returns true is path is absolute
677 bool AbsolutePath(string const & path)
678 {
679         return os::is_absolute_path(path);
680 }
681
682
683
684 // Create absolute path. If impossible, don't do anything
685 // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
686 string const ExpandPath(string const & path)
687 {
688         // checks for already absolute path
689         string RTemp(ReplaceEnvironmentPath(path));
690         if (os::is_absolute_path(RTemp))
691                 return RTemp;
692
693         string Temp;
694         string const copy(RTemp);
695
696         // Split by next /
697         RTemp = split(RTemp, Temp, '/');
698
699         if (Temp == ".") {
700                 return lyx::getcwd() /*GetCWD()*/ + '/' + RTemp;
701         }
702         if (Temp == "~") {
703                 return GetEnvPath("HOME") + '/' + RTemp;
704         }
705         if (Temp == "..") {
706                 return MakeAbsPath(copy);
707         }
708         // Don't know how to handle this
709         return copy;
710 }
711
712
713 // Normalize a path
714 // Constracts path/../path
715 // Can't handle "../../" or "/../" (Asger)
716 string const NormalizePath(string const & path)
717 {
718         string TempBase;
719         string RTemp;
720         string Temp;
721
722         if (os::is_absolute_path(path))
723                 RTemp = path;
724         else
725                 // Make implicit current directory explicit
726                 RTemp = "./" +path;
727
728         while (!RTemp.empty()) {
729                 // Split by next /
730                 RTemp = split(RTemp, Temp, '/');
731                 
732                 if (Temp == ".") {
733                         TempBase = "./";
734                 } else if (Temp == "..") {
735                         // Remove one level of TempBase
736                         string::difference_type i = TempBase.length() - 2;
737                         while (i > 0 && TempBase[i] != '/')
738                                 --i;
739                         if (i >= 0 && TempBase[i] == '/')
740                                 TempBase.erase(i + 1, string::npos);
741                         else
742                                 TempBase = "../";
743                 } else {
744                         TempBase += Temp + '/';
745                 }
746         }
747
748         // returns absolute path
749         return TempBase;        
750 }
751
752
753 string const GetFileContents(string const & fname)
754 {
755         FileInfo finfo(fname);
756         if (finfo.exist()) {
757                 ifstream ifs(fname.c_str());
758                 ostringstream ofs;
759                 if (ifs && ofs) {
760                         ofs << ifs.rdbuf();
761                         ifs.close();
762                         return ofs.str().c_str();
763                 }
764         }
765         lyxerr << "LyX was not able to read file '" << fname << "'" << endl;
766         return string();
767 }
768
769
770 //
771 // Search ${...} as Variable-Name inside the string and replace it with
772 // the denoted environmentvariable
773 // Allow Variables according to 
774 //  variable :=  '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
775 //
776
777 string const ReplaceEnvironmentPath(string const & path)
778 {
779 // 
780 // CompareChar: Environmentvariables starts with this character
781 // PathChar:    Next path component start with this character
782 // while CompareChar found do:
783 //       Split String with PathChar
784 //       Search Environmentvariable
785 //       if found: Replace Strings
786 //
787         char const CompareChar = '$';
788         char const FirstChar = '{'; 
789         char const EndChar = '}'; 
790         char const UnderscoreChar = '_'; 
791         string EndString; EndString += EndChar;
792         string FirstString; FirstString += FirstChar;
793         string CompareString; CompareString += CompareChar;
794         string const RegExp("*}*"); // Exist EndChar inside a String?
795
796 // first: Search for a '$' - Sign.
797         //string copy(path);
798         string result1; //(copy);    // for split-calls
799         string result0 = split(path, result1, CompareChar);
800         while (!result0.empty()) {
801                 string copy1(result0); // contains String after $
802                 
803                 // Check, if there is an EndChar inside original String.
804                 
805                 if (!regexMatch(copy1, RegExp)) {
806                         // No EndChar inside. So we are finished
807                         result1 += CompareString + result0;
808                         result0.erase();
809                         continue;
810                 }
811
812                 string res1;
813                 string res0 = split(copy1, res1, EndChar);
814                 // Now res1 holds the environmentvariable
815                 // First, check, if Contents is ok.
816                 if (res1.empty()) { // No environmentvariable. Continue Loop.
817                         result1 += CompareString + FirstString;
818                         result0  = res0;
819                         continue;
820                 }
821                 // check contents of res1
822                 char const * res1_contents = res1.c_str();
823                 if (*res1_contents != FirstChar) {
824                         // Again No Environmentvariable
825                         result1 += CompareString;
826                         result0 = res0;
827                 }
828
829                 // Check for variable names
830                 // Situation ${} is detected as "No Environmentvariable"
831                 char const * cp1 = res1_contents + 1;
832                 bool result = isalpha(*cp1) || (*cp1 == UnderscoreChar);
833                 ++cp1;
834                 while (*cp1 && result) {
835                         result = isalnum(*cp1) || 
836                                 (*cp1 == UnderscoreChar); 
837                         ++cp1;
838                 }
839
840                 if (!result) {
841                         // no correct variable name
842                         result1 += CompareString + res1 + EndString;
843                         result0  = split(res0, res1, CompareChar);
844                         result1 += res1;
845                         continue;
846                 }
847             
848                 string env(GetEnv(res1_contents + 1));
849                 if (!env.empty()) {
850                         // Congratulations. Environmentvariable found
851                         result1 += env;
852                 } else {
853                         result1 += CompareString + res1 + EndString;
854                 }
855                 // Next $-Sign?
856                 result0  = split(res0, res1, CompareChar);
857                 result1 += res1;
858         } 
859         return result1;
860 }  // ReplaceEnvironmentPath
861
862
863 // Make relative path out of two absolute paths
864 string const MakeRelPath(string const & abspath, string const & basepath)
865 // Makes relative path out of absolute path. If it is deeper than basepath,
866 // it's easy. If basepath and abspath share something (they are all deeper
867 // than some directory), it'll be rendered using ..'s. If they are completely
868 // different, then the absolute path will be used as relative path.
869 {
870         string::size_type const abslen = abspath.length();
871         string::size_type const baselen = basepath.length();
872
873         string::size_type i = os::common_path(abspath, basepath);
874
875         if (i == 0) {
876                 // actually no match - cannot make it relative
877                 return abspath;
878         }
879
880         // Count how many dirs there are in basepath above match
881         // and append as many '..''s into relpath
882         string buf;
883         string::size_type j = i;
884         while (j < baselen) {
885                 if (basepath[j] == '/') {
886                         if (j + 1 == baselen)
887                                 break;
888                         buf += "../";
889                 }
890                 ++j;
891         }
892
893         // Append relative stuff from common directory to abspath
894         if (abspath[i] == '/')
895                 ++i;
896         for (; i < abslen; ++i)
897                 buf += abspath[i];
898         // Remove trailing /
899         if (suffixIs(buf, '/'))
900                 buf.erase(buf.length() - 1);
901         // Substitute empty with .
902         if (buf.empty())
903                 buf = '.';
904         return buf;
905 }
906
907
908 // Append sub-directory(ies) to a path in an intelligent way
909 string const AddPath(string const & path, string const & path_2)
910 {
911         string buf;
912         string const path2 = os::slashify_path(path_2);
913
914         if (!path.empty() && path != "." && path != "./") {
915                 buf = os::slashify_path(path);
916                 if (path[path.length() - 1] != '/')
917                         buf += '/';
918         }
919
920         if (!path2.empty()) {
921                 string::size_type const p2start = path2.find_first_not_of('/');
922                 string::size_type const p2end = path2.find_last_not_of('/');
923                 string const tmp = path2.substr(p2start, p2end - p2start + 1);
924                 buf += tmp + '/';
925         }
926         return buf;
927 }
928
929
930 /* 
931  Change extension of oldname to extension.
932  Strips path off if no_path == true.
933  If no extension on oldname, just appends.
934  */
935 string const ChangeExtension(string const & oldname, string const & extension)
936 {
937         string::size_type const last_slash = oldname.rfind('/');
938         string::size_type last_dot = oldname.rfind('.');
939         if (last_dot < last_slash && last_slash != string::npos)
940                 last_dot = string::npos;
941         
942         string ext;
943         // Make sure the extension starts with a dot
944         if (!extension.empty() && extension[0] != '.')
945                 ext= "." + extension;
946         else
947                 ext = extension;
948
949         return os::slashify_path(oldname.substr(0, last_dot) + ext);
950 }
951
952
953 /// Return the extension of the file (not including the .)
954 string const GetExtension(string const & name)
955 {
956         string::size_type const last_slash = name.rfind('/');
957         string::size_type const last_dot = name.rfind('.');
958         if (last_dot != string::npos &&
959             (last_slash == string::npos || last_dot > last_slash))
960                 return name.substr(last_dot + 1,
961                                    name.length() - (last_dot + 1));
962         else
963                 return string();
964 }
965
966 // the different filetypes and what they contain in one of the first lines
967 // (dots are any characters).           (Herbert 20020131)
968 // EPS  %!PS-Adobe-3.0 EPSF...
969 // GIF  GIF...
970 // JPG  JFIF
971 // PDF  %PDF-...
972 // PNG  .PNG...
973 // PS   %!PS-Adobe-2.0
974 // XBM  static char ...
975 // XPM  /* XPM */
976 /// return the "extension" which belongs to the contents
977 string const getExtFromContents(string const & filename) {
978         if (filename.empty() || !IsFileReadable(filename)) 
979             return string();    // paranoia check
980         ifstream ifs(filename.c_str());
981         if (!ifs) 
982             return string();    // Couldn't open file...
983         int const max_count = 50; // Maximum strings to read to attempt recognition
984         int count = 0; // Counter of attempts.
985         string str;
986         for (; count < max_count; ++count) {
987                 if (ifs.eof()) {
988                         lyxerr[Debug::INFO] << "InsetGraphics (classifyFiletype)"
989                                 " End of file reached and it wasn't found to be a known Type!" << endl;
990                         break;
991                 }
992                 ifs >> str;
993                 if (contains(str,"EPSF"))
994                     return "eps";
995                 else if (contains(str,"GIF"))
996                     return "gif";
997                 else if (contains(str,"JFIF"))
998                     return "jpg";
999                 else if (contains(str,"%PDF"))
1000                     return "pdf";
1001                 else if (contains(str,"PNG"))
1002                     return "png";
1003                 else if (contains(str,"%!PS-Adobe-"))
1004                     return "ps";                // eps here no more possible
1005                 else if (contains(str,"static char"))
1006                     return "xbm";
1007                 else if (contains(str,"XPM"))
1008                     return "xpm";
1009         }
1010         lyxerr[Debug::INFO] << "InsetGraphics (classifyFiletype)"
1011                 " Couldn't find a known Type!" << endl;
1012         return string();
1013 }
1014
1015
1016 // Creates a nice compact path for displaying
1017 string const
1018 MakeDisplayPath (string const & path, unsigned int threshold)
1019 {
1020         string::size_type const l1 = path.length();
1021
1022         // First, we try a relative path compared to home
1023         string const home(GetEnvPath("HOME"));
1024         string relhome = MakeRelPath(path, home);
1025
1026         string::size_type l2 = relhome.length();
1027
1028         string prefix;
1029
1030         // If we backup from home or don't have a relative path,
1031         // this try is no good
1032         if (prefixIs(relhome, "../") || os::is_absolute_path(relhome)) {
1033                 // relative path was no good, just use the original path
1034                 relhome = path;
1035                 l2 = l1;
1036         } else {
1037                 prefix = "~/";
1038         }
1039
1040         // Is the path too long?
1041         if (l2 > threshold) {
1042                 // Yes, shortend it
1043                 prefix += ".../";
1044                 
1045                 string temp;
1046                 
1047                 while (relhome.length() > threshold)
1048                         relhome = split(relhome, temp, '/');
1049
1050                 // Did we shortend everything away?
1051                 if (relhome.empty()) {
1052                         // Yes, filename in itself is too long.
1053                         // Pick the start and the end of the filename.
1054                         relhome = OnlyFilename(path);
1055                         string const head = relhome.substr(0, threshold/2 - 3);
1056
1057                         l2 = relhome.length();
1058                         string const tail =
1059                                 relhome.substr(l2 - threshold/2 - 2, l2 - 1);
1060                         relhome = head + "..." + tail;
1061                 }
1062         }
1063         return prefix + relhome;
1064 }
1065
1066
1067 bool LyXReadLink(string const & File, string & Link)
1068 {
1069         char LinkBuffer[512];
1070         // Should be PATH_MAX but that needs autconf support
1071         int const nRead = ::readlink(File.c_str(),
1072                                      LinkBuffer, sizeof(LinkBuffer) - 1);
1073         if (nRead <= 0)
1074                 return false;
1075         LinkBuffer[nRead] = '\0'; // terminator
1076         Link = LinkBuffer;
1077         return true;
1078 }
1079
1080
1081 namespace {
1082
1083 typedef pair<int, string> cmdret;
1084
1085 cmdret const do_popen(string const & cmd)
1086 {
1087         // One question is if we should use popen or
1088         // create our own popen based on fork, exec, pipe
1089         // of course the best would be to have a
1090         // pstream (process stream), with the
1091         // variants ipstream, opstream
1092         FILE * inf = ::popen(cmd.c_str(), "r");
1093         string ret;
1094         int c = fgetc(inf);
1095         while (c != EOF) {
1096                 ret += static_cast<char>(c);
1097                 c = fgetc(inf);
1098         }
1099         int const pret = pclose(inf);
1100         return make_pair(pret, ret);
1101 }
1102
1103 } // namespace anon
1104
1105
1106 string const findtexfile(string const & fil, string const & /*format*/)
1107 {
1108         /* There is no problem to extend this function too use other
1109            methods to look for files. It could be setup to look
1110            in environment paths and also if wanted as a last resort
1111            to a recursive find. One of the easier extensions would
1112            perhaps be to use the LyX file lookup methods. But! I am
1113            going to implement this until I see some demand for it.
1114            Lgb
1115         */
1116         
1117         // If the file can be found directly, we just return a
1118         // absolute path version of it. 
1119         if (FileInfo(fil).exist())
1120                 return MakeAbsPath(fil);
1121
1122         // No we try to find it using kpsewhich.
1123         // It seems from the kpsewhich manual page that it is safe to use
1124         // kpsewhich without --format: "When the --format option is not
1125         // given, the search path used when looking for a file is inferred
1126         // from the name given, by looking for a known extension. If no
1127         // known extension is found, the search path for TeX source files
1128         // is used."
1129         // However, we want to take advantage of the format sine almost all
1130         // the different formats has environment variables that can be used
1131         // to controll which paths to search. f.ex. bib looks in
1132         // BIBINPUTS and TEXBIB. Small list follows:
1133         // bib - BIBINPUTS, TEXBIB
1134         // bst - BSTINPUTS
1135         // graphic/figure - TEXPICTS, TEXINPUTS
1136         // ist - TEXINDEXSTYLE, INDEXSTYLE
1137         // pk - PROGRAMFONTS, PKFONTS, TEXPKS, GLYPHFONTS, TEXFONTS
1138         // tex - TEXINPUTS
1139         // tfm - TFMFONTS, TEXFONTS
1140         // This means that to use kpsewhich in the best possible way we
1141         // should help it by setting additional path in the approp. envir.var.
1142         string const kpsecmd = "kpsewhich " + fil;
1143
1144         cmdret const c = do_popen(kpsecmd);
1145
1146         lyxerr[Debug::LATEX] << "kpse status = " << c.first << "\n"
1147                  << "kpse result = `" << strip(c.second, '\n') 
1148                  << "'" << endl;
1149         if (c.first != -1) 
1150                 return os::internal_path(strip(strip(c.second, '\n'), '\r'));
1151         else
1152                 return string();
1153 }
1154
1155
1156 void removeAutosaveFile(string const & filename)
1157 {
1158         string a = OnlyPath(filename);
1159         a += '#';
1160         a += OnlyFilename(filename);
1161         a += '#';
1162         FileInfo const fileinfo(a);
1163         if (fileinfo.exist()) {
1164                 if (lyx::unlink(a) != 0) {
1165                         Alert::err_alert(_("Could not delete auto-save file!"), a);
1166                 }
1167         }
1168 }
1169