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