]> Creatis software - bbtk.git/blob - kernel/src/bbtkUtilities.cxx
Bug fix on windows
[bbtk.git] / kernel / src / bbtkUtilities.cxx
1 /*=========================================================================                                                                               
2   Program:   bbtk
3   Module:    $RCSfile: bbtkUtilities.cxx,v $
4   Language:  C++
5   Date:      $Date: 2009/01/28 11:54:04 $
6   Version:   $Revision: 1.12 $
7 =========================================================================*/
8
9 /* ---------------------------------------------------------------------
10
11 * Copyright (c) CREATIS-LRMN (Centre de Recherche en Imagerie Medicale)
12 * Authors : Eduardo Davila, Laurent Guigues, Jean-Pierre Roux
13 *
14 *  This software is governed by the CeCILL-B license under French law and 
15 *  abiding by the rules of distribution of free software. You can  use, 
16 *  modify and/ or redistribute the software under the terms of the CeCILL-B 
17 *  license as circulated by CEA, CNRS and INRIA at the following URL 
18 *  http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html 
19 *  or in the file LICENSE.txt.
20 *
21 *  As a counterpart to the access to the source code and  rights to copy,
22 *  modify and redistribute granted by the license, users are provided only
23 *  with a limited warranty  and the software's author,  the holder of the
24 *  economic rights,  and the successive licensors  have only  limited
25 *  liability. 
26 *
27 *  The fact that you are presently reading this means that you have had
28 *  knowledge of the CeCILL-B license and that you accept its terms.
29 * ------------------------------------------------------------------------ */                                                                         
30
31
32 #include "bbtkUtilities.h"
33 #include "bbtkMessageManager.h"
34
35 #if defined(MACOSX) // assume this is OSX 
36 # include <sys/param.h>
37 # include <mach-o/dyld.h> // _NSGetExecutablePath : must add -framework CoreFoundation to link line 
38 # include <string.h>
39 # ifndef PATH_MAX
40 #  define PATH_MAX MAXPATHLEN
41 # endif
42 #endif // MACOSX
43
44 #ifndef PATH_MAX // If not defined yet : do it 
45 #  define PATH_MAX 2048
46 #endif 
47
48 namespace bbtk
49 {
50
51
52
53             // ======================================================================
54     // See : http://www.techbytes.ca/techbyte103.html for more O.S.
55     bool Utilities::FileExists(std::string strFilename) 
56     {
57       struct stat stFileInfo;
58      bool blnReturn;
59      int intStat;
60      
61      // Attempt to get the file attributes
62      intStat = stat(strFilename.c_str(),&stFileInfo);
63      if(intStat == 0) 
64        {
65          // We were able to get the file attributes
66          // so the file obviously exists.
67          blnReturn = true;
68        } 
69      else 
70        {
71          // We were not able to get the file attributes.
72          // This may mean that we don't have permission to
73          // access the folder which contains this file. If you
74          // need to do that level of checking, lookup the
75          // return values of stat which will give you
76          // more details on why stat failed.
77          blnReturn = false;
78        }
79      
80      return(blnReturn);
81     }
82     
83     
84     // =====================================================================
85     
86     std::string Utilities::ExtractPackageName(const std::string  &name, 
87                                           std::string& path)
88     {
89       std::string pkgname;
90       path = "";
91       
92       std::string::size_type slash_position = name.find_last_of("/\\");
93       if (slash_position != std::string::npos) 
94         {
95           pkgname = name.substr(slash_position+1,std::string::npos);
96           path = name.substr(0,slash_position);
97           //    std::cout << "F:P='"<<path<<"'"<<std::endl;//+1,std::string::npos);
98         }
99       else 
100         {
101           pkgname = name;
102         }
103       
104       // remove {.so | dll} if any
105       std::string::size_type dot_position = pkgname.find_last_of('.');      
106       if (dot_position != std::string::npos){
107         pkgname = pkgname.substr(0,dot_position);
108       }      
109 #if defined(__GNUC__)
110       
111       // GCC mechanism
112       // shared lib name = libbb<name>.so
113       
114       // remove {libbb} if any
115       if (memcmp ( pkgname.c_str(), "libbb", 5) == 0) {
116         pkgname =  pkgname.substr(5, pkgname.length());
117       }
118       /*
119      /// \ \todo     what would happen if (stupid) user names his package 'libbb' ?!?
120       /// \ --> Should be forbidden!
121       */
122 #elif defined(_WIN32)
123       
124       // WIN 32 mechanism
125       // shared lib name = <name>.dll
126       
127       // EED Problem loading package call bbtkTools
128       //     // remove {bb} if any
129       if (memcmp (pkgname.c_str(), "bb", 2) == 0) {
130         pkgname =  pkgname.substr(2, pkgname.length());  
131       }
132       
133       /*
134      /// \ \todo     what would happen if (stupid) user names his package 'bb' ?!?
135      /// \ --> Should be forbidden!
136      */
137 #else
138       bbtkError("neither __GNUC__ nor _WIN32 ?!? How did you compile ?");
139 #endif      
140       return pkgname;
141     }
142     
143     //=====================================================================
144     std::string Utilities::ExtractScriptName(const std::string &name,
145                                          std::string& path)
146     {
147       std::string pkgname;
148       
149       std::string::size_type slash_position = name.find_last_of("/\\");
150       if (slash_position != std::string::npos) {
151         pkgname =name.substr(slash_position+1,std::string::npos);
152         path = name.substr(0,slash_position);      
153       } else {
154         pkgname = name;
155       }
156       // remove {.bbs } if any
157       std::string::size_type dot_position = pkgname.find_last_of('.');
158       if (dot_position != std::string::npos){
159         pkgname = pkgname.substr(0,dot_position);
160       }
161       return pkgname;
162     }
163     
164     // ========================================================================
165
166     std::string Utilities::ExpandLibName(const std::string &name, bool verbose)
167     {
168       // -----   Think of expanding path name ( ./ ../ ../../ )
169       
170       char buf[2048]; // for getcwd
171       char * currentDir = getcwd(buf, 2048);
172       std::string cwd(currentDir);
173       std::string libname(name);
174       std::string fileSeparator;
175       fileSeparator = ConfigurationFile::GetInstance().Get_file_separator();
176       // tooHigh : true is user supplies a library pathname with too many "../"
177       bool tooHigh = false;
178       
179       //std::cout << "------------------cwd ["  << cwd << "] name [" << name << "]" << std::endl;
180       
181       if ( name[0] == '/' ||  name[1] == ':' ) // Linux or Windows absolute name
182         {
183           return(libname);
184         }
185       else if  ( name =="." )
186         {
187           libname = cwd  + fileSeparator;
188           return(libname);
189         }
190       else if  (name[0] == '.' && (name[1] == '/' || name[1] == '\\') )
191         {
192           libname = cwd  + fileSeparator + name.substr(2, name.length());
193           return(libname);
194         }
195       else if ( name[0] == '.' &&  name[1] == '.' /*  && (name[2] == '/' || name[2] == '\\') */ ) 
196         {
197           if ( IsAtRoot(cwd) )  // hope it gets / (for Linux),  C: D: (for Windows)
198       {  
199      // if we are already at / or c: --> hopeless  
200          if (verbose)
201            std::cout << "   File path [" <<  name << "] doesn't exist" << std::endl;
202          tooHigh = true;
203       }
204       else
205       {
206          // iterate on ../ and go up from the current working dir!
207          std::string a(name); 
208          bool alreadyProcessRoot = false;
209
210           //if (a[a.size()-1] != fileSeparator[0])
211           //   a.append(fileSeparator);
212 //std::cout << "------------------a ["  << a << "]" << std::endl;
213
214          for(;;)  // wild loop !
215          {
216             std::string::size_type slash_position = cwd.find_last_of(fileSeparator);
217             if (slash_position != std::string::npos) {
218              if (slash_position == 0)
219                 slash_position = 1;
220               cwd = cwd.substr(0,slash_position/*+1*/);
221 //std::cout << "------------------cwd ["  << cwd << "]" << std::endl;
222             //  if (a == "..") {
223             //    a = "";
224             //    break;
225             //   }
226             //   else
227                  a = a.substr(3, /*name.length()*/ a.length());  // remove ../
228 //std::cout << "------------------a ["  << a << "]" << std::endl;  
229               if (a == "" || alreadyProcessRoot)
230               {
231                 if (verbose)
232                   std::cout << "   File path : [" <<  name << "] doesn't exist" << std::endl;
233                 tooHigh = true;
234                 break;
235               }
236              // std::string b = cwd + a;
237               libname =  cwd;
238               char c = cwd[cwd.size()-1];
239               if (c != '/' && c != '\\' )
240                 libname += fileSeparator;
241               libname += a;
242
243               if ( a[0] != '.' ) // if . (probabely ../), loop again
244                 break;
245
246               if (IsAtRoot(cwd))
247                 alreadyProcessRoot = true;
248             }
249          } // end iterating on ../
250       }
251 //std::cout << "------------------out of loop]" << std::endl;        
252       if (tooHigh)
253          libname="";
254       return (libname);
255
256     }  // -----   End of expanding path name   ( ./ ../ ../../ )
257
258     std::cout <<"* ERROR in ExpandLibName : should never get here!" << std::endl;
259     // To avoid warning
260     return(""); // Will never get here!
261   }
262
263 // ===================================================================================
264
265   std::string Utilities::MakeLibnameFromPath(std::string path, std::string pkgname)
266   {
267     std::string libname = path;
268     char c = path[path.size()-1];    
269 #if defined(__GNUC__)
270        if (c != '/')
271           libname += "/libbb";
272        else
273           libname += "libbb";
274        libname += pkgname;
275 #if defined(MACOSX)
276           libname += ".dylib";
277 #else
278           libname += ".so";
279 #endif  
280           
281 #elif defined(_WIN32)
282        if (c != '\\')
283           libname = path+"\\bb";
284        libname += pkgname;
285        libname += ".dll";
286 #endif
287     return libname;    
288   }
289
290 // ===================================================================================
291
292   std::string Utilities::MakePkgnameFromPath(std::string path, std::string pkgname, bool addExt)
293   {
294     std::string libname = path;
295     char c = path[path.size()-1];
296     if (c != '/' && c != '\\')
297     {
298        libname +=  ConfigurationFile::GetInstance().Get_file_separator ();
299     }
300     libname += pkgname;
301     if (addExt)
302     {
303        int l = libname.size();
304        if (l>4)
305        {
306           if (libname.substr(l-4, 4) != ".bbs")
307           {
308                libname = libname + ".bbs";
309           }
310        }
311     }
312     return libname;
313   }
314   // =======================================================================
315
316   // =======================================================================
317   /// Returns the user settings dir, e.g. /home/username/.bbtk
318   std::string Utilities::GetUserSettingsDir()
319   {
320 #if defined(__GNUC__)
321     std::string str_home(getenv("HOME"));
322 #elif defined(_WIN32)
323     std::string str_home(getenv("USERPROFILE"));
324 #endif
325     std::string fullname = str_home + "/.bbtk";
326     MakeValidFileName(fullname);
327     return fullname;
328   }
329   
330
331   // =======================================================================
332   /// Builds the complete path to the file 'name' located 
333   /// in user settings dir, e.g. /home/username/.bbtk/
334   std::string Utilities::MakeUserSettingsFullFileName(const std::string& name)
335   {
336 #if defined(__GNUC__)
337     std::string str_home(getenv("HOME"));
338 #elif defined(_WIN32)
339     std::string str_home(getenv("USERPROFILE"));
340 #endif
341     std::string fullname = str_home + "/.bbtk/" + name;
342     MakeValidFileName(fullname);
343     return fullname;
344   }
345   // =======================================================================
346   
347
348   // =======================================================================
349   void Utilities::CreateDirectoryIfNeeded( std::string const &dirName)
350   {
351     if (FileExists(dirName)) return;
352     std::string cmd("mkdir \"");
353     cmd += dirName;
354     cmd += "\"";
355     system(cmd.c_str());
356   }  
357   // =======================================================================
358   
359   
360   //========================================================================
361   bool Utilities::IsAtRoot(std::string cwd)
362   {
363     if ( cwd == "/"              // hope it gets /     (for Linux)
364          || (cwd.size() <= 3 && cwd[1] == ':') ) // hope it gets C: D: (for Windows)
365       return (true);
366     else
367       return(false);
368   }
369   // ======================================================================
370
371   // ======================================================================
372   bool Utilities::IsDirectory(std::string const &dirName)
373   {
374     struct stat fs;
375     
376     if ( stat(dirName.c_str(), &fs) == 0 )
377       {
378 #if _WIN32
379         return ((fs.st_mode & _S_IFDIR) != 0);
380 #else
381         return S_ISDIR(fs.st_mode);
382 #endif
383       }
384     else
385       {
386         return false;
387       }
388   }
389   // =======================================================================
390     
391   // =======================================================================
392   void Utilities::SplitAroundFirstDot( const std::string& in,
393                                        std::string& left,
394                                        std::string& right)
395   {
396     std::string delimiter = ".";
397     std::string::size_type pos = in.find_first_of(delimiter);
398     if (std::string::npos != pos) 
399       {
400         left = in.substr(0,pos);
401         right = in.substr(pos+1,in.size());
402         
403       }
404     else
405       {
406         left ="";
407         right = "";
408         bbtkGlobalError("Token '"<<in<<"' : expected 'a.b' format but no dot found");
409       }
410   }
411   //=======================================================================
412
413   //=======================================================================
414   void Utilities::SplitString ( const std::string& str, 
415                                 const std::string& delimiters, 
416                                 std::vector<std::string>& tokens)
417   {
418     // Skip delimiters at beginning.
419     std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
420     // Find first delimiter.
421     std::string::size_type pos     = str.find_first_of(delimiters, lastPos);
422     
423     while (std::string::npos != pos || std::string::npos != lastPos)
424       {
425         // Found a token, add it to the vector.
426         tokens.push_back(str.substr(lastPos, pos - lastPos));
427         // Skip delimiters.  Note the "not_of"
428         lastPos = str.find_first_not_of(delimiters, pos);
429         // Find next delimiter
430         pos = str.find_first_of(delimiters, lastPos);
431       }
432     
433   }
434   //=======================================================================
435   
436   
437   // ======================================================================
438   std::string Utilities::get_file_name(const std::string& s) 
439   { 
440     std::string::size_type slash_position = s.find_last_of("/\\");
441     if (slash_position != std::string::npos) 
442       {
443         return  s.substr(slash_position+1,std::string::npos);   
444       }
445     else 
446       {
447         return s;
448       }
449   }
450   //=======================================================================
451  
452
453   // ========================================================================
454   /**
455    * \brief   Explore a directory with possibility of recursion
456    *          return number of files read
457    * @param  dirpath   directory to explore
458    * @param  recursive whether we want recursion or not
459    */
460   int Utilities::Explore(std::string const &dirpath, bool recursive, std::vector<std::string> &Filenames)
461   {
462     int numberOfFiles = 0;
463     std::string fileName;
464       
465       std::string dirName = dirpath;
466       
467 #ifdef _MSC_VER
468       WIN32_FIND_DATA fileData;
469    HANDLE hFile = FindFirstFile((dirName+"\\*").c_str(), &fileData);
470
471    for(BOOL b = (hFile != INVALID_HANDLE_VALUE); b;
472        b = FindNextFile(hFile, &fileData))
473    {
474       fileName = fileData.cFileName;
475       if ( fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
476       {
477          // Need to check for . and .. to avoid infinite loop
478          if ( fileName != "." && fileName != ".." && recursive )
479          {
480             numberOfFiles += Explore(dirName+ "\\"+fileName,recursive,Filenames);
481          }
482       }
483       else
484       {
485          Filenames.push_back(dirName+"\\"+fileName);
486          numberOfFiles++;
487       }
488    }
489    DWORD dwError = GetLastError();
490    if (hFile != INVALID_HANDLE_VALUE) 
491       FindClose(hFile);
492    if (dwError != ERROR_NO_MORE_FILES) 
493    {
494       LPVOID lpMsgBuf;
495       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
496                     FORMAT_MESSAGE_FROM_SYSTEM|
497                     FORMAT_MESSAGE_IGNORE_INSERTS,
498                     NULL,GetLastError(),
499                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
500                     (LPTSTR) &lpMsgBuf,0,NULL);
501
502      // ErrorMacro("FindNextFile error. Error is " << (char *)lpMsgBuf
503      //             <<" for the directory : "<<dirName);
504       
505       return 0;
506    }
507
508 #else
509   // Real POSIX implementation: scandir is a BSD extension only, and doesn't 
510   // work on debian for example
511 //std::cout <<"in Explor dirname[" << dirName << "]" << std::endl; 
512    DIR* dir = opendir(dirName.c_str());
513    if (!dir)
514    {
515       return 0;
516    }
517 //std::cout <<"Open OK" << std::endl; 
518    // According to POSIX, the dirent structure contains a field char d_name[]
519    // of unspecified size, with at most NAME_MAX characters preceeding the
520    // terminating null character. Use of other fields will harm the  porta-
521    // bility of your programs.
522
523    struct stat buf;
524    dirent *d;
525    for (d = readdir(dir); d; d = readdir(dir))
526    {
527       fileName = dirName + "/" + d->d_name;
528 //std::cout <<"in Explor filename[" << fileName << "]" << std::endl;      
529       if( stat(fileName.c_str(), &buf) != 0 )
530       {
531          //ErrorMacro( strerror(errno) );
532       }
533       if ( S_ISREG(buf.st_mode) )    //is it a regular file?
534       {
535          Filenames.push_back( fileName );
536          numberOfFiles++;
537       }
538       else if ( S_ISDIR(buf.st_mode) ) //directory?
539       {
540          if ( d->d_name[0] != '.' && recursive ) //we also skip hidden files
541          {
542             numberOfFiles += Explore( fileName, recursive, Filenames);
543          }
544       }
545       else
546       {
547          //ErrorMacro( "Unexpected error" );
548          return -1;
549       }
550    }
551    if( closedir(dir) != 0 )
552    {
553      // ErrorMacro( strerror(errno) );
554    }
555 #endif
556
557   return numberOfFiles;
558
559 }
560   //=======================================================================
561  
562
563     //=======================================================================
564     // Replaces substrings "\\n" by a real carriage return "\n"
565     void Utilities::SubsBackslashN ( std::string& s )
566     {
567       std::string ss("\\n");
568       std::string::size_type pos = 0;
569       pos = s.find(ss,0);
570       const char* cr = "\n";
571       while ( pos != std::string::npos )
572         {
573           s.replace(pos,2,cr,1);
574           pos = s.find(ss, pos-1);
575         }
576     }
577     //=======================================================================
578
579
580    //=======================================================================
581
582   bool Utilities::loosematch(std::string stdLine,std::string stdOptions) 
583   {
584     bool result=false;
585     std::vector<std::string> tokens;
586     SplitString ( stdOptions,"|", tokens);
587     int i,size=tokens.size();  
588     for (i=0; i<size; i++)
589       {           
590 #ifdef WIN32
591         if ( strcmpi(stdLine.c_str(),tokens[i].c_str())==0) 
592           { 
593             result=true; 
594           }               
595 #else
596         if ( strcasecmp(stdLine.c_str(),tokens[i].c_str())==0) 
597           { 
598             result=true; 
599           }               
600 #endif
601         
602       }
603     return result;
604   }
605   //=========================================================================  
606   
607   //=========================================================================  
608   // From http://www.fltk.org/newsgroups.php?gfltk.general+v:22083
609   //
610   int get_app_path (char *pname, size_t pathsize)
611   {
612 #ifdef LINUX
613     /* Oddly, the readlink(2) man page says no NULL is appended. */
614     /* So you have to do it yourself, based on the return value: */
615     pathsize --; /* Preserve a space to add the trailing NULL */
616     long result = readlink("/proc/self/exe", pname, pathsize);
617     if (result > 0)
618       {
619         pname[result] = 0; /* add the #@!%ing NULL */
620         
621         if ((access(pname, 0) == 0))
622           return 0; /* file exists, return OK */
623         /*else name doesn't seem to exist, return FAIL (falls
624           through) */
625       }
626 #endif /* LINUX */
627     
628 #ifdef WIN32
629     long result = GetModuleFileName(NULL, pname, pathsize);
630     if (result > 0)
631       {
632         /* fix up the dir slashes... */
633         int len = strlen(pname);
634         int idx;
635         for (idx = 0; idx < len; idx++)
636           {
637             if (pname[idx] == '\\') pname[idx] = '/';
638           }
639         
640         for (idx = len-1; idx >=0 ; idx--)
641           {
642             if (pname[idx] == '/')
643               { 
644                 pname[idx+1] = '\0';
645                 idx = -1;
646               }
647           }
648         
649         if ((access(pname, 0) == 0))
650           return 0; /* file exists, return OK */
651         /*else name doesn't seem to exist, return FAIL (falls
652           through) */
653       }
654 #endif /* WIN32 */
655     
656 #ifdef SOLARIS
657     char *p = getexecname();
658     if (p)
659       {
660         /* According to the Sun manpages, getexecname will
661            "normally" return an */
662         /* absolute path - BUT might not... AND that IF it is not,
663            pre-pending */
664         /* getcwd() will "usually" be the correct thing... Urgh!
665          */
666         
667         /* check pathname is absolute (begins with a / ???) */
668         if (p[0] == '/') /* assume this means we have an
669                             absolute path */
670           {
671             strncpy(pname, p, pathsize);
672             if ((access(pname, 0) == 0))
673               return 0; /* file exists, return OK */
674           }
675         else /* if not, prepend getcwd() then check if file
676                 exists */
677           {
678             getcwd(pname, pathsize);
679             long result = strlen(pname);
680             strncat(pname, "/", (pathsize - result));
681             result ++;
682             strncat(pname, p, (pathsize - result));
683             
684             if ((access(pname, 0) == 0))
685               return 0; /* file exists, return OK */
686             /*else name doesn't seem to exist, return FAIL
687               (falls through) */
688           }
689       }
690 #endif /* SOLARIS */
691     
692 #ifdef MACOSX /* assume this is OSX */
693     /*
694       from http://www.hmug.org/man/3/NSModule.html
695       
696       extern int _NSGetExecutablePath(char *buf, unsigned long
697       *bufsize);
698       
699       _NSGetExecutablePath  copies  the  path  of the executable
700       into the buffer and returns 0 if the path was successfully
701       copied  in the provided buffer. If the buffer is not large
702       enough, -1 is returned and the  expected  buffer  size  is
703       copied  in  *bufsize.  Note that _NSGetExecutablePath will
704       return "a path" to the executable not a "real path" to the
705       executable.  That  is  the path may be a symbolic link and
706       not the real file. And with  deep  directories  the  total
707       bufsize needed could be more than MAXPATHLEN.
708     */
709     int status = -1;
710     char *given_path = (char*)malloc(MAXPATHLEN * 2);
711     if (!given_path) return status;
712     
713     uint32_t npathsize = MAXPATHLEN * 2;
714     long result = _NSGetExecutablePath(given_path, &npathsize);
715     if (result == 0)
716       { /* OK, we got something - now try and resolve the real path...
717          */
718         if (realpath(given_path, pname) != NULL)
719           {
720             if ((access(pname, 0) == 0))
721               status = 0; /* file exists, return OK */
722           }
723       }
724     free (given_path);
725     return status;
726 #endif /* MACOSX */
727     
728     return -1; /* Path Lookup Failed */
729   } 
730   //=========================================================================
731         
732
733         
734   //=========================================================================
735   std::string Utilities::GetExecutablePath()
736   {
737     char name[PATH_MAX];
738     int err = get_app_path(name, PATH_MAX);
739     if (err) 
740       {
741         bbtkGlobalError("Could not determine current executable path ?");  
742       }
743     
744     // remove the exe name
745     char *slash;                
746     slash = strrchr(name, VALID_FILE_SEPARATOR_CHAR);
747     if (slash)
748       {
749         *slash = 0;
750       }
751     return name;
752   }
753   //=========================================================================
754
755
756
757 }