home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / lyx-0.13.2.tar.gz / lyx-0.13.2.tar / lyx-0.13.2 / src / filetools.C < prev    next >
C/C++ Source or Header  |  1998-04-23  |  21KB  |  933 lines

  1. /*
  2.     filetools.C (former paths.C) - part of LyX project
  3.     General path-mangling functions 
  4.     Copyright (C) 1996 Ivan Schreter
  5.     Parts Copyright (C) 1996 Dirk Niggemann
  6.         Parts Copyright (C) 1985, 1990, 1993 Free Software Foundation, Inc.
  7.     Parts Copyright (C) 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 (C) 1995-1996, Matthias Ettrich and the LyX Team
  14.  
  15. */
  16.  
  17. #include <config.h>
  18.  
  19. #include <stdlib.h>
  20. #include <ctype.h>
  21. #include <errno.h>        // I know it's OS/2 specific (SMiyata)
  22.  
  23. #ifdef __GNUG__
  24. #pragma implementation "filetools.h"
  25. #endif
  26.  
  27. #include "filetools.h"
  28. #include "lyx_gui_misc.h"
  29. #include "FileInfo.h"
  30. #include "path.h"
  31. #include "gettext.h"
  32.  
  33. // Which part of this is still necessary? (JMarc).
  34. #if HAVE_DIRENT_H
  35. # include <dirent.h>
  36. # define NAMLEN(dirent) strlen((dirent)->d_name)
  37. #else
  38. # define dirent direct
  39. # define NAMLEN(dirent) (dirent)->d_namlen
  40. # if HAVE_SYS_NDIR_H
  41. #  include <sys/ndir.h>
  42. # endif
  43. # if HAVE_SYS_DIR_H
  44. #  include <sys/dir.h>
  45. # endif
  46. # if HAVE_NDIR_H
  47. #  include <ndir.h>
  48. # endif
  49. #endif
  50.  
  51. //     $Id: filetools.C,v 1.1.1.1 1998/04/23 16:02:49 larsbj Exp $    
  52.  
  53. #if !defined(lint) && !defined(WITH_WARNINGS)
  54. static char vcid[] = "$Id: filetools.C,v 1.1.1.1 1998/04/23 16:02:49 larsbj Exp $";
  55. #endif /* lint */
  56.  
  57.  
  58. extern LString system_lyxdir;
  59. extern LString build_lyxdir;
  60. extern LString user_lyxdir;
  61. extern LString system_tempdir;
  62.  
  63.  
  64. bool IsLyXFilename(LString const & filename)
  65. {
  66.     return filename.contains(".lyx");
  67. }
  68.  
  69.  
  70. bool IsSGMLFilename(LString const & filename)
  71. {
  72.     return filename.contains(".sgml");
  73. }
  74.  
  75. // Substitutes spaces with underscores in filename (and path)
  76. LString SpaceLess(LString const & file)
  77. {
  78.     LString name = OnlyFilename(file);
  79.     LString path = OnlyPath(file);
  80.     // Substitute chars that LaTeX can't handle with safe ones
  81.     name.subst('~','-');
  82.     name.subst('$','S');
  83.     name.subst('%','_');
  84.  
  85.     LString temp = AddName(path, name);
  86.     // Replace spaces with underscores, also in directory
  87.     temp.subst(' ','_');
  88.  
  89.     return temp;
  90. }
  91.  
  92.  
  93. /// Returns an unique name to be used as a temporary file. 
  94. LString TmpFileName(LString const & dir, LString const & mask)
  95. {// With all these temporary variables, it should be safe enough :-) (JMarc)
  96.     LString tmpdir;    
  97.     if (dir.empty())
  98.         tmpdir = system_tempdir;
  99.     else
  100.         tmpdir = dir;
  101.     LString tmpfl = AddName(tmpdir, mask);
  102.  
  103.     // find a uniq postfix for the filename...
  104.     // using the pid, and...
  105.     tmpfl += int(getpid());
  106.     // a short string...
  107.     LString ret;
  108.     FileInfo fnfo;
  109.     for (int a='a'; a<= 'z'; a++)
  110.         for (int b='a'; b<= 'z'; b++)
  111.             for (int c='a'; c<= 'z'; c++) {
  112.                 // if this is not enough I have no idea what
  113.                 // to do.
  114.                 ret = tmpfl + char(a) + char(b) + char(c);
  115.                 // check if the file exist
  116.                 if (!fnfo.newFile(ret).exist())
  117.                     return ret;
  118.             }
  119.     lyxerr.print("Not able to find a uniq tmpfile name.");
  120.     return LString();
  121. }
  122.  
  123.  
  124. // Is a file readable ?
  125. bool IsFileReadable (LString const & path)
  126. {
  127.     FileInfo file(path);
  128.     if (file.isOK() && file.readable())
  129.         return true;
  130.     else
  131.         return false;
  132. }
  133.  
  134.  
  135. // Is a file read_only?
  136. // return 1 read-write
  137. //      0 read_only
  138. //     -1 error (doesn't exist, no access, anything else) 
  139. int IsFileWriteable (LString const & path)
  140. {
  141.     FilePtr fp(path, FilePtr::update);
  142.     if (!fp()) {
  143.         if ((errno == EACCES) || (errno == EROFS)) {
  144.             //fp = FilePtr(path, FilePtr::read);
  145.             fp.reopen(path, FilePtr::read);
  146.             if (fp()) {
  147.                 return 0;
  148.             }
  149.         }
  150.         return -1;
  151.     }
  152.     return 1;
  153. }
  154.  
  155.  
  156. //returns 1: dir writeable
  157. //      0: not writeable
  158. //     -1: error- couldn't find out
  159. int IsDirWriteable (LString const & path)
  160. {
  161.         LString tmpfl = TmpFileName(path);
  162.  
  163.     if (tmpfl.empty()) {
  164.         WriteFSAlert(_("LyX Internal Error!"), 
  165.                  _("Could not test if directory is writeable"));
  166.         return -1;
  167.     } else {
  168.     FilePtr fp(tmpfl, FilePtr::truncate);
  169.     if (!fp()) {
  170.         if (errno == EACCES) {
  171.             return 0;
  172.         } else { 
  173.                 WriteFSAlert(_("LyX Internal Error!"), 
  174.                      _("Cannot open directory test file"));
  175.             return -1;
  176.         }
  177.         }
  178.     }
  179.         if (remove (tmpfl.c_str())) {
  180.             WriteFSAlert(_("LyX Internal Error!"), 
  181.                     _("Created test file but cannot remove it?"));
  182.              return -1;
  183.     }
  184.     return 1;
  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. LString FileOpenSearch (LString const & path, LString const & name, 
  194.             LString const & ext)
  195. {
  196.     LString real_file, path_element;
  197.     LString tmppath = path;
  198.     bool notfound = true;
  199.  
  200.     tmppath.split(path_element, ';');
  201.     
  202.     while (notfound && !path_element.empty()) {
  203.  
  204.         if (!path_element.suffixIs('/'))
  205.             path_element+='/';
  206.         path_element.subst("$$LyX",system_lyxdir);
  207.         path_element.subst("$$User",user_lyxdir);
  208.         
  209.         real_file = FileSearch(path_element, name, ext);
  210.  
  211.         if (real_file.empty()) {
  212.           tmppath.split(path_element, ';');
  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. LString FileSearch(LString const & path, LString const & name, 
  230.            LString const & ext)
  231. {
  232.         LString fullname;
  233.     LString tmp;
  234.  
  235.     // if `name' is an absolute path, we ignore the setting of `path'
  236.     // Expand Environmentvariables in 'name'
  237.     LString tmpname = ReplaceEnvironmentPath(name);
  238.     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 LString();
  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 LString();
  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. LString LibFileSearch(LString const & dir, LString const & name, 
  261.               LString const & ext)
  262. {
  263.         LString fullname = FileSearch(AddPath(user_lyxdir,dir), name,
  264.                       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. static
  279. int DeleteAllFilesInDir (LString const & path)
  280. {
  281.     DIR * dir;
  282.     struct dirent *de;
  283.     dir = opendir(path.c_str());
  284.     if (!dir) {
  285.         WriteFSAlert (_("Error! Cannot open directory:"), path);
  286.         return -1;
  287.     }
  288.     while ((de = readdir(dir))) {
  289.         LString temp = de->d_name;
  290.         if (temp=="." || temp=="..") 
  291.             continue;
  292.         LString unlinkpath = AddName (path, temp);
  293.  
  294.         lyxerr.debug("Deleting file: " + unlinkpath);
  295.  
  296.          if (remove (unlinkpath.c_str()))
  297.             WriteFSAlert (_("Error! Could not remove file:"), 
  298.                       unlinkpath);
  299.         }
  300.     closedir (dir);
  301.     return 0;
  302. }
  303.  
  304.  
  305. static
  306. LString CreateTmpDir (LString const & tempdir, LString const & mask)
  307. {
  308.     LString tmpfl = TmpFileName(tempdir, mask);
  309.     
  310.     if ((tmpfl.empty()) || mkdir (tmpfl.c_str(), 0777)) {
  311.         WriteFSAlert(_("Error! Couldn't create temporary directory:"),
  312.                  tempdir);
  313.         return LString();
  314.     }
  315.     return MakeAbsPath(tmpfl);
  316. }
  317.  
  318.  
  319. static
  320. int DestroyTmpDir (LString const & tmpdir, bool Allfiles)
  321. {
  322.     if ((Allfiles) && (DeleteAllFilesInDir (tmpdir))) return -1;
  323.     if (rmdir(tmpdir.c_str())) { 
  324. #ifdef __EMX__
  325.         if (errno == EBUSY) {
  326.             chdir(user_lyxdir.c_str()); // They are in the same drive.
  327.             if (!rmdir(tmpdir.c_str())) return 0;
  328.         }
  329. #endif
  330.         WriteFSAlert(_("Error! Couldn't delete temporary directory:"), 
  331.                  tmpdir);
  332.         return -1;
  333.     }
  334.     return 0; 
  335.  
  336.  
  337. LString CreateBufferTmpDir (LString const & pathfor)
  338. {
  339.     return CreateTmpDir (pathfor, "lyx_bufrtmp");
  340. }
  341.  
  342.  
  343. int DestroyBufferTmpDir (LString const & tmpdir)
  344. {
  345.        return DestroyTmpDir (tmpdir, true);
  346. }
  347.  
  348.  
  349. LString CreateLyXTmpDir (LString const & deflt)
  350. {
  351.         LString t;
  352.         
  353.     if ((!deflt.empty()) && (deflt!="/tmp")) {
  354.         if (mkdir (deflt.c_str(), 0777)) {
  355. #ifdef __EMX__
  356.                         //PathPush(user_lyxdir);
  357.             Path p(user_lyxdir);
  358. #endif
  359.             t = CreateTmpDir (deflt.c_str(), "lyx_tmp");
  360. //#ifdef __EMX__
  361. //                        PathPop();
  362. //#endif
  363.                         return t;
  364.         } else
  365.                         return deflt;
  366.     } else {
  367. #ifdef __EMX__
  368.         //PathPush(user_lyxdir);
  369.         Path p(user_lyxdir);
  370. #endif
  371.         t = CreateTmpDir ("/tmp", "lyx_tmp");
  372. //#ifdef __EMX__
  373. //        PathPop();
  374. //#endif
  375.         return t;
  376.     }
  377. }
  378.  
  379.  
  380. int DestroyLyXTmpDir (LString const & tmpdir)
  381. {
  382.        return DestroyTmpDir (tmpdir, false); // Why false?
  383. }
  384.  
  385.  
  386. // Creates directory. Returns true if succesfull
  387. bool createDirectory(LString const & path, int permission)
  388. {
  389.     LString temp = path;
  390.     
  391.     // Cut off trailing /s
  392.     temp.strip('/');
  393.  
  394.     if (temp.empty()) {
  395.         WriteAlert(_("Internal error!"),
  396.                _("Call to createDirectory with invalid name"));
  397.         return false;
  398.     }
  399.  
  400.     if (mkdir(temp.c_str(), permission)) {
  401.         WriteFSAlert (_("Error! Couldn't create directory:"), temp);
  402.         return false;
  403.     }
  404.     return true;
  405. }
  406.  
  407.  
  408. // Should supposedly help with some automountd-stuff...
  409. // Fortunately no need to patch for OS/2 support
  410. LString safer_getcwd ()
  411. {
  412.     LString temp = getenv ("PWD");
  413.     if (temp.empty()) {
  414.         temp = getenv ("CWD");
  415.         if (temp.empty())
  416.             temp = GetCWD();
  417.     }
  418.     return temp;
  419. }
  420.  
  421.  
  422. // Returns current working directory
  423. LString GetCWD ()
  424. {
  425.       int n = 256;    // Assume path is less than 256 chars
  426.     char * err;
  427.       char * tbuf = new char [n];
  428.     LString result;
  429.       
  430.       // Safe. Hopefully all getcwds behave this way!
  431.       while (((err = getcwd (tbuf, n)) == NULL) && (errno == ERANGE)) {
  432.         // Buffer too small, double the buffersize and try again
  433.             delete[] tbuf;
  434.             n = 2*n;
  435.             tbuf = new char [n];
  436.       }
  437.  
  438.     if (err) result = tbuf;
  439.     delete[] tbuf;
  440.     return result;
  441. }
  442.  
  443.  
  444. // Strip filename from path name
  445. LString OnlyPath(LString const &Filename)
  446. {
  447.     // If empty filename, return empty
  448.     if (Filename.empty()) return Filename;
  449.  
  450.     // Find last / or start of filename
  451.     int j = Filename.length() - 1;
  452.     for (; j > 0 && Filename[j] != '/'; j--);
  453.  
  454.     if (Filename[j] != '/')
  455.         return "./";
  456.     else {
  457.         // Strip to pathname
  458.         LString temp = Filename;
  459.         return temp.substring(0, j);
  460.     }
  461. }
  462.  
  463.  
  464. // Convert relative path into absolute path based on a basepath.
  465. // If relpath is absolute, just use that.
  466. // If basepath is empty, use CWD as base.
  467. LString MakeAbsPath(LString const &RelPath, LString const &BasePath)
  468. {
  469.     // checks for already absolute path
  470.     if (AbsolutePath(RelPath))
  471. #ifdef __EMX__
  472.         if(RelPath[0]!='/')
  473. #endif
  474.         return RelPath;
  475.  
  476.     // Copies given paths
  477.     LString TempRel = RelPath;
  478.  
  479.     LString TempBase;
  480.  
  481.     if (!BasePath.empty()) {
  482. #ifndef __EMX__
  483.         TempBase = BasePath;
  484. #else
  485.         char* with_drive = new char[_MAX_PATH];
  486.         _abspath(with_drive, BasePath.c_str(), _MAX_PATH);
  487.         TempBase = with_drive;
  488.         delete[] with_drive;
  489. #endif
  490.     } else
  491.         TempBase = safer_getcwd();
  492. #ifdef __EMX__
  493.     if (AbsolutePath(TempRel))
  494.         return TempBase.substring(0,1) + TempRel;
  495. #endif
  496.  
  497.     // Handle /./ at the end of the path
  498.     while(TempBase.suffixIs("/./"))
  499.         TempBase.substring(0,TempBase.length()-3);
  500.  
  501.     // processes relative path
  502.     LString RTemp = TempRel;
  503.     LString Temp;
  504.  
  505.     while (!RTemp.empty()) {
  506.         // Split by next /
  507.         RTemp.split(Temp, '/');
  508.         
  509.         if (Temp==".") continue;
  510.         if (Temp=="..") {
  511.             // Remove one level of TempBase
  512.             int i = TempBase.length()-2;
  513. #ifndef __EMX__
  514.             if (i<0) i=0;
  515.             while (i>0 && TempBase[i] != '/') i--;
  516.             if (i>0)
  517. #else
  518.                 if (i<2) i=2;
  519.             while (i>2 && TempBase[i] != '/') i--;
  520.             if (i>2)
  521. #endif
  522.                 TempBase.substring(0, i);
  523.             else
  524.                 TempBase += '/';
  525.         } else {
  526.             // Add this piece to TempBase
  527.             if (!TempBase.suffixIs('/'))
  528.                 TempBase += '/';
  529.             TempBase += Temp;
  530.         }
  531.     }
  532.  
  533.     // returns absolute path
  534.     return TempBase;    
  535. }
  536.  
  537.  
  538. // Correctly append filename to the pathname.
  539. // If pathname is '.', then don't use pathname.
  540. // Chops any path of filename.
  541. LString AddName(LString const &Path, LString const &FileName)
  542. {
  543.     // Get basename
  544.     LString Basename = OnlyFilename(FileName);
  545.  
  546.     LString buf;
  547.  
  548.     if (Path != "." && Path != "./" && !Path.empty()) {
  549.         buf = Path;
  550.         if (!Path.suffixIs('/'))
  551.             buf += '/';
  552.     }
  553.  
  554.     return buf + Basename;
  555. }
  556.  
  557.  
  558. // Strips path from filename
  559. LString OnlyFilename(LString const &Filename)
  560. {
  561.     // If empty filename, return empty
  562.     if (Filename.empty()) return Filename;
  563.  
  564.     int j;
  565.     // Find last / or start of filename
  566.     for (j=Filename.length()-1; Filename[j] != '/' && j>0; j--);
  567.  
  568.     // Skip potential /
  569.     if (j!=0) j++;
  570.  
  571.     // Strip to basename
  572.     LString temp = Filename;
  573.     return temp.substring(j, temp.length()-1);
  574. }
  575.  
  576.  
  577. // Is a filename/path absolute?
  578. bool AbsolutePath(LString const &path)
  579. {
  580. #ifndef __EMX__
  581.     return (!path.empty() && path[0]=='/');
  582. #else
  583.     return (!path.empty() && (path[0]=='/' || (isalpha((unsigned char) path[0]) && path[1]==':')));
  584. #endif
  585. }
  586.  
  587.  
  588. // Create absolute path. If impossible, don't do anything
  589. // Supports ./ and ~/. Later we can add support for ~logname/. (Asger)
  590. LString ExpandPath(LString const &path)
  591. {
  592.     // checks for already absolute path
  593.     LString RTemp = ReplaceEnvironmentPath(path);
  594.     if (AbsolutePath(RTemp))
  595.         return RTemp;
  596.  
  597.     LString Temp;
  598.     LString copy(RTemp);
  599.  
  600.     // Split by next /
  601.     RTemp.split(Temp, '/');
  602.  
  603.     if (Temp==".") {
  604.         return GetCWD() + '/' + RTemp;
  605.     } else if (Temp=="~") {
  606.         return getEnvPath("HOME") + '/' + RTemp;
  607.     } else if (Temp=="..") {
  608.         return MakeAbsPath(copy);
  609.     } else
  610.         // Don't know how to handle this
  611.         return copy;
  612. }
  613.  
  614.  
  615. // Normalize a path
  616. // Constracts path/../path
  617. // Can't handle "../../" or "/../" (Asger)
  618. LString NormalizePath(LString const &path)
  619. {
  620.     LString TempBase;
  621.     LString RTemp;
  622.     LString Temp;
  623.  
  624.     if (AbsolutePath(path))
  625.         RTemp = path;
  626.     else
  627.         // Make implicit current directory explicit
  628.         RTemp = "./" +path;
  629.  
  630.     while (!RTemp.empty()) {
  631.         // Split by next /
  632.         RTemp.split(Temp, '/');
  633.         
  634.         if (Temp==".") {
  635.             TempBase = "./";
  636.         } else if (Temp=="..") {
  637.             // Remove one level of TempBase
  638.             int i = TempBase.length()-2;
  639.             while (i>0 && TempBase[i] != '/')
  640.                 i--;
  641.             if (i>=0 && TempBase[i] == '/')
  642.                 TempBase.substring(0, i);
  643.             else
  644.                 TempBase = "../";
  645.         } else {
  646.             TempBase += Temp + '/';
  647.         }
  648.     }
  649.  
  650.     // returns absolute path
  651.     return TempBase;    
  652. }
  653.  
  654.  
  655. //
  656. // Search ${...} as Variable-Name inside the string and replace it with
  657. // the denoted environmentvariable
  658. // Allow Variables according to 
  659. //  variable :=  '$' '{' [A-Za-z_]{[A-Za-z_0-9]*} '}'
  660. //
  661.  
  662. LString ReplaceEnvironmentPath(LString const &path)
  663. {
  664. // 
  665. // CompareChar: Environmentvariables starts with this character
  666. // PathChar:    Next path component start with this character
  667. // while CompareChar found do:
  668. //       Split String with PathChar
  669. //      Search Environmentvariable
  670. //     if found: Replace Strings
  671. //
  672.     const char CompareChar = '$';
  673.     const char FirstChar = '{'; 
  674.     const char EndChar = '}'; 
  675.     const char UnderscoreChar = '_'; 
  676.     LString EndString;
  677.     EndString += EndChar;
  678.     LString FirstString;
  679.     FirstString += FirstChar;
  680.     LString CompareString;
  681.     CompareString += CompareChar;
  682.     const LString RegExp("*}*"); // Exist EndChar inside a String?
  683.  
  684.     if (path.empty()) return path; // nothing to do.
  685.  
  686. // first: Search for a '$' - Sign.
  687.     LString copy(path);
  688.     LString result1(copy);    // for split-calls
  689.     LString result0 = copy.split(result1, CompareChar);
  690.     while (!result0.empty()) {
  691.         LString copy1(result0); // contains String after $
  692.         
  693.         // Check, if there is an EndChar inside original String.
  694.         
  695.         if (!copy1.regexMatch(RegExp)) {
  696.             // No EndChar inside. So we are finished
  697.             result1 += CompareString + result0;
  698.             result0 = LString();
  699.             continue;
  700.         }
  701.  
  702.         LString res1;
  703.         LString res0 = copy1.split(res1, EndChar);
  704.         // Now res1 holds the environmentvariable
  705.         // First, check, if Contents is ok.
  706.         if (res1.empty()) { // No environmentvariable. Continue Loop.
  707.             result1 += CompareString + FirstString;
  708.             result0  = res0;
  709.             continue;
  710.         }
  711.         // check contents of res1
  712.         const char *res1_contents = res1.c_str();
  713.         if (*res1_contents != FirstChar) {
  714.             // Again No Environmentvariable
  715.             result1 += CompareString;
  716.             result0  = res0;
  717.         }
  718.  
  719.         // Check for variable names
  720.         // Situation ${} is detected as "No Environmentvariable"
  721.         const char *cp1 = res1_contents+1;
  722.         bool result = isalpha((unsigned char) *cp1) || (*cp1 == UnderscoreChar);
  723.         ++cp1;
  724.         while (*cp1 && result) {
  725.             result = isalnum((unsigned char) *cp1) || 
  726.                 (*cp1 == UnderscoreChar); 
  727.             ++cp1;
  728.         }
  729.  
  730.         if (!result) {
  731.             // no correct variable name
  732.             result1 += CompareString + res1 + EndString;
  733.             result0  = res0.split(res1, CompareChar);
  734.             result1 += res1;
  735.             continue;
  736.         }
  737.             
  738.         char *env = getenv(res1_contents+1);
  739.         if (env) {
  740.             // Congratulations. Environmentvariable found
  741.             result1 += env;
  742.         } else {
  743.             result1 += CompareString + res1 + EndString;
  744.         }
  745.         // Next $-Sign?
  746.         result0  = res0.split(res1, CompareChar);
  747.         result1 += res1;
  748.     } 
  749.     return result1;
  750. }  // ReplaceEnvironmentPath
  751.  
  752.  
  753. // Make relative path out of two absolute paths
  754. LString MakeRelPath(LString const & abspath, LString const & basepath)
  755. // Makes relative path out of absolute path. If it is deeper than basepath,
  756. // it's easy. If basepath and abspath share something (they are all deeper
  757. // than some directory), it'll be rendered using ..'s. If they are completely
  758. // different, then the absolute path will be used as relative path.
  759. {
  760.     // This is a hack. It should probaly be done in another way. Lgb.
  761.     if (abspath.empty())
  762.         return "<unknown_path>";
  763.  
  764.     const int abslen = abspath.length();
  765.     const int baselen = basepath.length();
  766.     
  767.     // Find first different character
  768.     int i = 0;
  769.     while (i < abslen && i < baselen && abspath[i] == basepath[i]) ++i;
  770.  
  771.     // Go back to last /
  772.     if (i < abslen && i < baselen
  773.         || (i<abslen && abspath[i] != '/' && i==baselen)
  774.         || (i<baselen && basepath[i] != '/' && i==abslen))
  775.     {
  776.         if (i) --i;    // here was the last match
  777.         while (i && abspath[i] != '/') --i;
  778.     }
  779.  
  780.     if (i == 0) {
  781.         // actually no match - cannot make it relative
  782.         return abspath;
  783.     }
  784.  
  785.     // Count how many dirs there are in basepath above match
  786.     // and append as many '..''s into relpath
  787.     LString buf;
  788.     int j = i;
  789.     while (j < baselen) {
  790.         if (basepath[j] == '/') {
  791.             if (j+1 == baselen) break;
  792.             buf += "../";
  793.         }
  794.         ++j;
  795.     }
  796.  
  797.     // Append relative stuff from common directory to abspath
  798.     if (abspath[i] == '/') ++i;
  799.     for (; i<abslen; ++i)
  800.         buf += abspath[i];
  801.     // Remove trailing /
  802.     if (buf.suffixIs('/'))
  803.         buf.substring(0,buf.length()-2);
  804.     // Substitute empty with .
  805.     if (buf.empty())
  806.         buf = '.';
  807.     return buf;
  808. }
  809.  
  810.  
  811. // Append sub-directory(ies) to a path in an intelligent way
  812. LString AddPath(LString const & path, LString const & path2)
  813. {
  814.     LString buf;
  815.  
  816.     if (!path.empty() && path != "." && path != "./") {
  817.         buf = path;
  818.         if (!path.suffixIs('/'))
  819.             buf += '/';
  820.     }
  821.  
  822.     if (!path2.empty()){
  823.             int p2start = 0;
  824.         while (path2[p2start] == '/') p2start++;
  825.  
  826.         int p2end = path2.length()-1;
  827.         while (path2[p2end] == '/') p2end--;
  828.  
  829.         LString tmp = path2;
  830.         tmp.substring(p2start,p2end);
  831.         buf += tmp + '/';
  832.     }
  833.     return buf;
  834. }
  835.  
  836.  
  837. /* 
  838.  Change extension of oldname to extension.
  839.  Strips path off if no_path == true.
  840.  If no extension on oldname, just appends.
  841.  */
  842. LString ChangeExtension(LString const & oldname, LString const & extension, 
  843.             bool no_path) 
  844.     int n = oldname.length()-1;
  845.     int dot;
  846.  
  847.     // Make sure the extension includes the dot, if not empty
  848.     LString ext;
  849.     if (!extension.empty() && extension[0] != '.')
  850.         ext = "." + extension;
  851.     else
  852.         ext = extension;
  853.  
  854.     // Go back to the first dot not crossing any /
  855.     for (dot=n; dot>=0 && oldname[dot]!='.' && oldname[dot]!='/'; dot--);
  856.    
  857.     if (dot==-1 || oldname[dot]!='.')
  858.         // If no extension was found, we use the end of the string
  859.         dot = n;
  860.     else
  861.         // Remove the dot, because the new extension includes it
  862.         dot--;
  863.  
  864.     // "path" points to last / or 0 if path is wanted
  865.     int path = 0;
  866.     if (no_path) {
  867.         for (path=dot; path && oldname[path]!='/';path--);
  868.         if (oldname[path]=='/')
  869.             path++;
  870.     } else 
  871.         path = 0;
  872.  
  873.     LString result = oldname;
  874.     result.substring(path,dot);
  875.     if (!ext.empty())
  876.         result += ext;
  877.     return result;
  878. }
  879.  
  880.  
  881. // Creates a nice compact path for displaying
  882. LString MakeDisplayPath (LString const & path, int threshold)
  883. {
  884.     const int l1 = path.length();
  885.  
  886.     // First, we try a relative path compared to home
  887.     LString home = getEnvPath("HOME");
  888.     LString relhome = MakeRelPath(path, home);
  889.  
  890.     int l2 = relhome.length();
  891.  
  892.     LString prefix;
  893.  
  894.     // If we backup from home or don't have a relative path,
  895.     // this try is no good
  896.     if (relhome.prefixIs("../") || AbsolutePath(relhome)) {
  897.         // relative path was no good, just use the original path
  898.         relhome = path;
  899.         l2 = l1;
  900.     } else {
  901.         prefix = "~/";
  902.     }
  903.  
  904.     // Is the path too long?
  905.     if (l2 > threshold) {
  906.         // Yes, shortend it
  907.         prefix += ".../";
  908.         
  909.         LString temp;
  910.         
  911.         while (relhome.length()>threshold)
  912.             relhome.split(temp, '/');
  913.  
  914.         // Did we shortend everything away?
  915.         if (relhome.empty()) {
  916.             // Yes, filename in itself is too long.
  917.             // Pick the start and the end of the filename.
  918.             relhome = OnlyFilename(path);
  919.             LString head = relhome;
  920.             head.substring(0, threshold/2 - 3);
  921.  
  922.             LString tail = relhome;
  923.             l2 = tail.length();
  924.             tail.substring(l2 - threshold/2 -2, l2 - 1);
  925.  
  926.             relhome = head + "..." + tail;
  927.         }
  928.     }
  929.     return prefix + relhome;
  930. }
  931.