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