]> Creatis software - bbtk.git/blob - kernel/src/bbtkUtilities.cxx
Refixed a f*%£!!""# bug for at least the third time !
[bbtk.git] / kernel / src / bbtkUtilities.cxx
1 /*=========================================================================                                                                               
2   Program:   bbtk
3   Module:    $RCSfile: bbtkUtilities.cxx,v $
4   Language:  C++
5   Date:      $Date: 2009/01/29 08:45:29 $
6   Version:   $Revision: 1.13 $
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 += "\\bb";
284        else
285           libname += "bb";
286        libname += pkgname;
287        libname += ".dll";
288 #endif
289     return libname;    
290   }
291
292 // ===================================================================================
293
294   std::string Utilities::MakePkgnameFromPath(std::string path, std::string pkgname, bool addExt)
295   {
296     std::string libname = path;
297     char c = path[path.size()-1];
298     if (c != '/' && c != '\\')
299     {
300        libname +=  ConfigurationFile::GetInstance().Get_file_separator ();
301     }
302     libname += pkgname;
303     if (addExt)
304     {
305        int l = libname.size();
306        if (l>4)
307        {
308           if (libname.substr(l-4, 4) != ".bbs")
309           {
310                libname = libname + ".bbs";
311           }
312        }
313     }
314     return libname;
315   }
316   // =======================================================================
317
318   // =======================================================================
319   /// Returns the user settings dir, e.g. /home/username/.bbtk
320   std::string Utilities::GetUserSettingsDir()
321   {
322 #if defined(__GNUC__)
323     std::string str_home(getenv("HOME"));
324 #elif defined(_WIN32)
325     std::string str_home(getenv("USERPROFILE"));
326 #endif
327     std::string fullname = str_home + "/.bbtk";
328     MakeValidFileName(fullname);
329     return fullname;
330   }
331   
332
333   // =======================================================================
334   /// Builds the complete path to the file 'name' located 
335   /// in user settings dir, e.g. /home/username/.bbtk/
336   std::string Utilities::MakeUserSettingsFullFileName(const std::string& name)
337   {
338 #if defined(__GNUC__)
339     std::string str_home(getenv("HOME"));
340 #elif defined(_WIN32)
341     std::string str_home(getenv("USERPROFILE"));
342 #endif
343     std::string fullname = str_home + "/.bbtk/" + name;
344     MakeValidFileName(fullname);
345     return fullname;
346   }
347   // =======================================================================
348   
349
350   // =======================================================================
351   void Utilities::CreateDirectoryIfNeeded( std::string const &dirName)
352   {
353     if (FileExists(dirName)) return;
354     std::string cmd("mkdir \"");
355     cmd += dirName;
356     cmd += "\"";
357     system(cmd.c_str());
358   }  
359   // =======================================================================
360   
361   
362   //========================================================================
363   bool Utilities::IsAtRoot(std::string cwd)
364   {
365     if ( cwd == "/"              // hope it gets /     (for Linux)
366          || (cwd.size() <= 3 && cwd[1] == ':') ) // hope it gets C: D: (for Windows)
367       return (true);
368     else
369       return(false);
370   }
371   // ======================================================================
372
373   // ======================================================================
374   bool Utilities::IsDirectory(std::string const &dirName)
375   {
376     struct stat fs;
377     
378     if ( stat(dirName.c_str(), &fs) == 0 )
379       {
380 #if _WIN32
381         return ((fs.st_mode & _S_IFDIR) != 0);
382 #else
383         return S_ISDIR(fs.st_mode);
384 #endif
385       }
386     else
387       {
388         return false;
389       }
390   }
391   // =======================================================================
392     
393   // =======================================================================
394   void Utilities::SplitAroundFirstDot( const std::string& in,
395                                        std::string& left,
396                                        std::string& right)
397   {
398     std::string delimiter = ".";
399     std::string::size_type pos = in.find_first_of(delimiter);
400     if (std::string::npos != pos) 
401       {
402         left = in.substr(0,pos);
403         right = in.substr(pos+1,in.size());
404         
405       }
406     else
407       {
408         left ="";
409         right = "";
410         bbtkGlobalError("Token '"<<in<<"' : expected 'a.b' format but no dot found");
411       }
412   }
413   //=======================================================================
414
415   //=======================================================================
416   void Utilities::SplitString ( const std::string& str, 
417                                 const std::string& delimiters, 
418                                 std::vector<std::string>& tokens)
419   {
420     // Skip delimiters at beginning.
421     std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
422     // Find first delimiter.
423     std::string::size_type pos     = str.find_first_of(delimiters, lastPos);
424     
425     while (std::string::npos != pos || std::string::npos != lastPos)
426       {
427         // Found a token, add it to the vector.
428         tokens.push_back(str.substr(lastPos, pos - lastPos));
429         // Skip delimiters.  Note the "not_of"
430         lastPos = str.find_first_not_of(delimiters, pos);
431         // Find next delimiter
432         pos = str.find_first_of(delimiters, lastPos);
433       }
434     
435   }
436   //=======================================================================
437   
438   
439   // ======================================================================
440   std::string Utilities::get_file_name(const std::string& s) 
441   { 
442     std::string::size_type slash_position = s.find_last_of("/\\");
443     if (slash_position != std::string::npos) 
444       {
445         return  s.substr(slash_position+1,std::string::npos);   
446       }
447     else 
448       {
449         return s;
450       }
451   }
452   //=======================================================================
453  
454
455   // ========================================================================
456   /**
457    * \brief   Explore a directory with possibility of recursion
458    *          return number of files read
459    * @param  dirpath   directory to explore
460    * @param  recursive whether we want recursion or not
461    */
462   int Utilities::Explore(std::string const &dirpath, bool recursive, std::vector<std::string> &Filenames)
463   {
464     int numberOfFiles = 0;
465     std::string fileName;
466       
467       std::string dirName = dirpath;
468       
469 #ifdef _MSC_VER
470       WIN32_FIND_DATA fileData;
471    HANDLE hFile = FindFirstFile((dirName+"\\*").c_str(), &fileData);
472
473    for(BOOL b = (hFile != INVALID_HANDLE_VALUE); b;
474        b = FindNextFile(hFile, &fileData))
475    {
476       fileName = fileData.cFileName;
477       if ( fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
478       {
479          // Need to check for . and .. to avoid infinite loop
480          if ( fileName != "." && fileName != ".." && recursive )
481          {
482             numberOfFiles += Explore(dirName+ "\\"+fileName,recursive,Filenames);
483          }
484       }
485       else
486       {
487          Filenames.push_back(dirName+"\\"+fileName);
488          numberOfFiles++;
489       }
490    }
491    DWORD dwError = GetLastError();
492    if (hFile != INVALID_HANDLE_VALUE) 
493       FindClose(hFile);
494    if (dwError != ERROR_NO_MORE_FILES) 
495    {
496       LPVOID lpMsgBuf;
497       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
498                     FORMAT_MESSAGE_FROM_SYSTEM|
499                     FORMAT_MESSAGE_IGNORE_INSERTS,
500                     NULL,GetLastError(),
501                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
502                     (LPTSTR) &lpMsgBuf,0,NULL);
503
504      // ErrorMacro("FindNextFile error. Error is " << (char *)lpMsgBuf
505      //             <<" for the directory : "<<dirName);
506       
507       return 0;
508    }
509
510 #else
511   // Real POSIX implementation: scandir is a BSD extension only, and doesn't 
512   // work on debian for example
513 //std::cout <<"in Explor dirname[" << dirName << "]" << std::endl; 
514    DIR* dir = opendir(dirName.c_str());
515    if (!dir)
516    {
517       return 0;
518    }
519 //std::cout <<"Open OK" << std::endl; 
520    // According to POSIX, the dirent structure contains a field char d_name[]
521    // of unspecified size, with at most NAME_MAX characters preceeding the
522    // terminating null character. Use of other fields will harm the  porta-
523    // bility of your programs.
524
525    struct stat buf;
526    dirent *d;
527    for (d = readdir(dir); d; d = readdir(dir))
528    {
529       fileName = dirName + "/" + d->d_name;
530 //std::cout <<"in Explor filename[" << fileName << "]" << std::endl;      
531       if( stat(fileName.c_str(), &buf) != 0 )
532       {
533          //ErrorMacro( strerror(errno) );
534       }
535       if ( S_ISREG(buf.st_mode) )    //is it a regular file?
536       {
537          Filenames.push_back( fileName );
538          numberOfFiles++;
539       }
540       else if ( S_ISDIR(buf.st_mode) ) //directory?
541       {
542          if ( d->d_name[0] != '.' && recursive ) //we also skip hidden files
543          {
544             numberOfFiles += Explore( fileName, recursive, Filenames);
545          }
546       }
547       else
548       {
549          //ErrorMacro( "Unexpected error" );
550          return -1;
551       }
552    }
553    if( closedir(dir) != 0 )
554    {
555      // ErrorMacro( strerror(errno) );
556    }
557 #endif
558
559   return numberOfFiles;
560
561 }
562   //=======================================================================
563  
564
565     //=======================================================================
566     // Replaces substrings "\\n" by a real carriage return "\n"
567     void Utilities::SubsBackslashN ( std::string& s )
568     {
569       std::string ss("\\n");
570       std::string::size_type pos = 0;
571       pos = s.find(ss,0);
572       const char* cr = "\n";
573       while ( pos != std::string::npos )
574         {
575           s.replace(pos,2,cr,1);
576           pos = s.find(ss, pos-1);
577         }
578     }
579     //=======================================================================
580
581
582    //=======================================================================
583
584   bool Utilities::loosematch(std::string stdLine,std::string stdOptions) 
585   {
586     bool result=false;
587     std::vector<std::string> tokens;
588     SplitString ( stdOptions,"|", tokens);
589     int i,size=tokens.size();  
590     for (i=0; i<size; i++)
591       {           
592 #ifdef WIN32
593         if ( strcmpi(stdLine.c_str(),tokens[i].c_str())==0) 
594           { 
595             result=true; 
596           }               
597 #else
598         if ( strcasecmp(stdLine.c_str(),tokens[i].c_str())==0) 
599           { 
600             result=true; 
601           }               
602 #endif
603         
604       }
605     return result;
606   }
607   //=========================================================================  
608   
609   //=========================================================================  
610   // From http://www.fltk.org/newsgroups.php?gfltk.general+v:22083
611   //
612   int get_app_path (char *pname, size_t pathsize)
613   {
614 #ifdef LINUX
615     /* Oddly, the readlink(2) man page says no NULL is appended. */
616     /* So you have to do it yourself, based on the return value: */
617     pathsize --; /* Preserve a space to add the trailing NULL */
618     long result = readlink("/proc/self/exe", pname, pathsize);
619     if (result > 0)
620       {
621         pname[result] = 0; /* add the #@!%ing NULL */
622         
623         if ((access(pname, 0) == 0))
624           return 0; /* file exists, return OK */
625         /*else name doesn't seem to exist, return FAIL (falls
626           through) */
627       }
628 #endif /* LINUX */
629     
630 #ifdef WIN32
631     long result = GetModuleFileName(NULL, pname, pathsize);
632     if (result > 0)
633       {
634         /* fix up the dir slashes... */
635         int len = strlen(pname);
636         int idx;
637         for (idx = 0; idx < len; idx++)
638           {
639             if (pname[idx] == '\\') pname[idx] = '/';
640           }
641         
642         for (idx = len-1; idx >=0 ; idx--)
643           {
644             if (pname[idx] == '/')
645               { 
646                 pname[idx+1] = '\0';
647                 idx = -1;
648               }
649           }
650         
651         if ((access(pname, 0) == 0))
652           return 0; /* file exists, return OK */
653         /*else name doesn't seem to exist, return FAIL (falls
654           through) */
655       }
656 #endif /* WIN32 */
657     
658 #ifdef SOLARIS
659     char *p = getexecname();
660     if (p)
661       {
662         /* According to the Sun manpages, getexecname will
663            "normally" return an */
664         /* absolute path - BUT might not... AND that IF it is not,
665            pre-pending */
666         /* getcwd() will "usually" be the correct thing... Urgh!
667          */
668         
669         /* check pathname is absolute (begins with a / ???) */
670         if (p[0] == '/') /* assume this means we have an
671                             absolute path */
672           {
673             strncpy(pname, p, pathsize);
674             if ((access(pname, 0) == 0))
675               return 0; /* file exists, return OK */
676           }
677         else /* if not, prepend getcwd() then check if file
678                 exists */
679           {
680             getcwd(pname, pathsize);
681             long result = strlen(pname);
682             strncat(pname, "/", (pathsize - result));
683             result ++;
684             strncat(pname, p, (pathsize - result));
685             
686             if ((access(pname, 0) == 0))
687               return 0; /* file exists, return OK */
688             /*else name doesn't seem to exist, return FAIL
689               (falls through) */
690           }
691       }
692 #endif /* SOLARIS */
693     
694 #ifdef MACOSX /* assume this is OSX */
695     /*
696       from http://www.hmug.org/man/3/NSModule.html
697       
698       extern int _NSGetExecutablePath(char *buf, unsigned long
699       *bufsize);
700       
701       _NSGetExecutablePath  copies  the  path  of the executable
702       into the buffer and returns 0 if the path was successfully
703       copied  in the provided buffer. If the buffer is not large
704       enough, -1 is returned and the  expected  buffer  size  is
705       copied  in  *bufsize.  Note that _NSGetExecutablePath will
706       return "a path" to the executable not a "real path" to the
707       executable.  That  is  the path may be a symbolic link and
708       not the real file. And with  deep  directories  the  total
709       bufsize needed could be more than MAXPATHLEN.
710     */
711     int status = -1;
712     char *given_path = (char*)malloc(MAXPATHLEN * 2);
713     if (!given_path) return status;
714     
715     uint32_t npathsize = MAXPATHLEN * 2;
716     long result = _NSGetExecutablePath(given_path, &npathsize);
717     if (result == 0)
718       { /* OK, we got something - now try and resolve the real path...
719          */
720         if (realpath(given_path, pname) != NULL)
721           {
722             if ((access(pname, 0) == 0))
723               status = 0; /* file exists, return OK */
724           }
725       }
726     free (given_path);
727     return status;
728 #endif /* MACOSX */
729     
730     return -1; /* Path Lookup Failed */
731   } 
732   //=========================================================================
733         
734
735         
736   //=========================================================================
737   std::string Utilities::GetExecutablePath()
738   {
739     char name[PATH_MAX];
740     int err = get_app_path(name, PATH_MAX);
741     if (err) 
742       {
743         bbtkGlobalError("Could not determine current executable path ?");  
744       }
745     
746     // remove the exe name
747     char *slash;                
748     slash = strrchr(name, VALID_FILE_SEPARATOR_CHAR);
749     if (slash)
750       {
751         *slash = 0;
752       }
753     return name;
754   }
755   //=========================================================================
756
757
758
759 }