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