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