source: CMT/HEAD/source/cmt_system.cxx @ 569

Last change on this file since 569 was 569, checked in by rybkin, 13 years ago

See C.L. 452

  • Property svn:eol-style set to native
File size: 60.8 KB
Line 
1//-----------------------------------------------------------
2// Copyright Christian Arnault LAL-Orsay CNRS
3// arnault@lal.in2p3.fr
4// See the complete license in cmt_license.txt "http://www.cecill.info".
5//-----------------------------------------------------------
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <errno.h>
11
12#ifdef WIN32
13#include <direct.h>
14#define chdir _chdir
15#define rmdir _rmdir
16//#define mkdir _mkdir
17#define getcwd _getcwd
18#define popen _popen
19#define pclose _pclose
20#define S_IFDIR _S_IFDIR
21#define USE_GETCWD 1
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <time.h>
26#include <io.h>
27#include <windows.h>
28
29#define stat _stat
30#define WEXITSTATUS(w) (w)
31#include <stdlib.h>
32#define PATH_MAX _MAX_PATH
33
34#else
35#include <unistd.h>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <time.h>
39#include <dirent.h>
40#include <sys/utsname.h>
41#include <limits.h> // define PATH_MAX
42#endif
43
44#ifdef __hpux__
45#define USE_GETCWD 1
46#endif
47
48#ifdef __linux__
49#define USE_GETCWD 1
50#endif
51
52#ifdef USE_GETCWD
53char* getwd (const char* name)
54{
55  // defined in <limits.h>
56  char dir[PATH_MAX];
57  //  char dir[256];
58  getcwd (dir, sizeof (dir));
59  strcpy ((char*) name, dir);
60  return ((char*) name);
61}
62#endif
63
64#include "cmt_system.h"
65#include "cmt_error.h"
66#include "cmt_map.h"
67#include "cmt_log.h"
68
69//--------------------------------------------------
70cmt_string CmtSystem::pwd ()
71{
72  char buffer[PATH_MAX] = "";
73  //  char buffer[256] = "";
74  char* ptr = 0;
75  char* pwd_env = 0;
76
77  pwd_env = ::getenv ("PWD"); 
78
79  if (pwd_env != 0)
80    {
81      strncpy (buffer, pwd_env, PATH_MAX - 1);
82      buffer[PATH_MAX - 1] = '\0'; // ensure termination
83      //      strcpy (buffer, pwd_env);
84    }
85  else
86    {
87      ptr = getcwd (buffer, sizeof (buffer));
88    }
89
90  //cerr << "pwd> " << buffer << endl;
91
92  const char* t = &buffer[0];
93  return ((cmt_string) t);
94}
95
96//--------------------------------------------------
97bool CmtSystem::cd (const cmt_string& dir)
98{
99  if (!CmtSystem::absolute_path (dir))
100    {
101      cmt_string d = CmtSystem::pwd ();
102      d += file_separator ();
103      d += dir;
104      return (CmtSystem::cd (d));
105    }
106
107  static cmt_string s_dir;
108
109  s_dir = dir;
110
111  if ((s_dir.size () == 2) && (s_dir[1] == ':'))
112    {
113      s_dir += file_separator ();
114    }
115
116  compress_path (s_dir);
117
118  if (chdir (s_dir.c_str ()) == 0) 
119    {
120      putenv ("PWD", s_dir);
121      return (true);
122    }
123
124  return (false);
125}
126
127//--------------------------------------------------
128void CmtSystem::basename (const cmt_string& file_name, cmt_string& result)
129{
130  int pos = file_name.find_last_of ('/');
131  if (pos == cmt_string::npos)
132    {
133      pos = file_name.find_last_of ('\\');
134    }
135
136  if (pos == cmt_string::npos)
137    {
138      result = file_name;
139    }
140  else
141    {
142      file_name.substr (pos + 1, result);
143    }
144}
145
146//--------------------------------------------------
147void CmtSystem::basename (const cmt_string& file_name,
148                          const cmt_string& /*suffix*/,
149                          cmt_string& result)
150{
151  basename (file_name, result);
152
153  int pos;
154
155  pos = result.find_last_of ('.');
156
157  if (pos != cmt_string::npos)
158    {
159      result.erase (pos);
160    }
161}
162
163//--------------------------------------------------
164void CmtSystem::dirname (const cmt_string& file_name, cmt_string& result)
165{
166  int pos = file_name.find_last_of ('/');
167  if (pos == cmt_string::npos)
168    {
169      pos = file_name.find_last_of ('\\');
170    }
171
172  if (pos == cmt_string::npos)
173    {
174      result = "";
175    }
176  else
177    {
178      result = file_name;
179      result.erase (pos);
180    }
181}
182
183//--------------------------------------------------
184void CmtSystem::name (const cmt_string& file_name, cmt_string& result)
185{
186  int pos;
187
188  result = file_name;
189
190  // remove the suffix
191
192  pos = result.find_last_of ('.');
193
194  if (pos != cmt_string::npos)
195    {
196      result.erase (pos);
197    }
198
199  // remove the directory name
200
201  pos = result.find_last_of ('/');
202  if (pos == cmt_string::npos)
203    {
204      pos = result.find_last_of ('\\');
205    }
206
207  if (pos != cmt_string::npos)
208    {
209      result.erase (0, pos + 1);
210    }
211}
212
213//-------------------------------------------------
214void CmtSystem::get_suffix (const cmt_string& file, cmt_string& result)
215{
216  int pos = file.find_last_of ('.');
217  int sep = file.find_last_of (file_separator ());
218
219  if ((pos == cmt_string::npos) || (pos < sep))
220    {
221      result = "";
222    }
223  else
224    {
225      file.substr (pos + 1, result);
226    }
227}
228
229//-------------------------------------------------
230void CmtSystem::get_dot_suffix (const cmt_string& file, cmt_string& result)
231{
232  int pos = file.find_last_of ('.');
233  int sep = file.find_last_of (file_separator ());
234
235  if ((pos == cmt_string::npos) || (pos < sep))
236    {
237      result = "";
238    }
239  else
240    {
241      file.substr (pos, result);
242    }
243}
244
245//--------------------------------------------------
246bool CmtSystem::has_prefix (const cmt_string& name)
247{
248  if ((name.find ('/') == cmt_string::npos) &&
249      (name.find ('\\') == cmt_string::npos))
250    {
251      return (false);
252    }
253
254  return (true);
255}
256
257//--------------------------------------------------
258bool CmtSystem::realpath (const cmt_string& path, cmt_string& result)
259{
260  cmt_string here = CmtSystem::pwd ();
261  if  (test_directory(path))
262    {
263      CmtSystem::cd (path);
264      result = CmtSystem::pwd ();
265      CmtSystem::cd (here);
266      return true;
267    }
268  return false;
269}
270
271//--------------------------------------------------
272bool CmtSystem::realpath_ (const cmt_string& path, cmt_string& result)
273{
274  if  (test_directory(path))
275    {
276      cmt_string here = CmtSystem::pwd ();
277      CmtSystem::cd (path);
278      //
279      char buffer[PATH_MAX] = "";
280      //      char buffer[256] = "";
281      char* ptr = 0;
282      ptr = getcwd (buffer, sizeof (buffer));
283      const char* t = &buffer[0];
284      result = t;
285      //      cerr << "realpath_> path: " << path << " result: " << result << endl;
286      //
287      CmtSystem::cd (here);
288      return true;
289    }
290  return false;
291}
292
293//--------------------------------------------------
294bool CmtSystem::absolute_path (const cmt_string& name)
295{
296  if (name.size () == 0) return (false);
297
298  if ((name[0] == '/') ||
299      (name[0] == '\\')) return (true);
300
301  if (name.size () >= 2)
302    {
303      if (name[1] == ':')
304        {
305          return (true);
306        }
307    }
308  return (false);
309}
310
311//--------------------------------------------------
312bool CmtSystem::has_device (const cmt_string& name)
313{
314#ifdef WIN32
315  if (name.size () == 0) return (false);
316
317  if (name.size () >= 2)
318    {
319      if (name[1] == ':')
320        {
321          return (true);
322        }
323      else if ((name[0] == '\\') && (name[1] == '\\'))
324        {
325          return (true);
326        }
327    }
328#endif
329
330  return (false);
331}
332
333//--------------------------------------------------
334cmt_string CmtSystem::current_branch ()
335{
336  cmt_string result;
337
338  basename (pwd (), result);
339
340  return (result);
341}
342
343//--------------------------------------------------
344bool CmtSystem::test_directory (const cmt_string& name)
345{
346  struct stat file_stat;
347  int status;
348
349  status = stat (name.c_str (), &file_stat);
350
351  //cerr << "status(stat) for " << name << " : " << status << " st_mode=" << file_stat.st_mode << endl;
352
353  if (status == 0)
354    {
355      if ((file_stat.st_mode & S_IFDIR) == 0)
356        {
357          return (false);
358        }
359      else
360        {
361          return (true);
362        }
363    }
364  else
365    {
366      return (false);
367    }
368}
369
370//--------------------------------------------------
371bool CmtSystem::test_file (const cmt_string& name)
372{
373  struct stat file_stat;
374  int status;
375
376  status = stat (name.c_str (), &file_stat);
377
378  if (status == 0)
379    {
380      if ((file_stat.st_mode & S_IFDIR) == 0)
381        {
382          return (true);
383        }
384      else
385        {
386          return (false);
387        }
388    }
389  else
390    {
391      return (false);
392    }
393}
394
395//--------------------------------------------------
396bool CmtSystem::compare_files (const cmt_string& name1,
397                               const cmt_string& name2)
398{
399  struct stat file_stat1;
400  struct stat file_stat2;
401  int status;
402
403  status = stat (name1.c_str (), &file_stat1);
404
405  if (status == 0)
406    {
407      if ((file_stat1.st_mode & S_IFDIR) != 0)
408        {
409          return (false);
410        }
411    }
412  else
413    {
414      return (false);
415    }
416
417  status = stat (name2.c_str (), &file_stat2);
418
419  if (status == 0)
420    {
421      if ((file_stat2.st_mode & S_IFDIR) != 0)
422        {
423          return (false);
424        }
425    }
426  else
427    {
428      return (false);
429    }
430
431  if (((int) file_stat1.st_size) != ((int) file_stat2.st_size))
432    {
433      return (false);
434    }
435
436  static cmt_string s1;
437  static cmt_string s2;
438
439  s1.read (name1);
440  s2.read (name2);
441
442  return ((s1 == s2));
443}
444
445//--------------------------------------------------
446//
447// Function use to change file timestamps
448//
449//--------------------------------------------------
450bool CmtSystem::touch_file (const cmt_string& name)
451{
452  if (CmtSystem::test_file(name))
453    {
454      static cmt_string s;
455      s.read (name);
456      //unlink  (name);           
457      FILE* f = fopen (name, "wb");
458      if (f != NULL)
459        {
460          s.write (f);
461          fclose (f);
462          return (true);
463        }
464      chmod (name.c_str(), 0777);
465      /*            FILE* f = fopen (name, "a+");
466
467      if (f != NULL)
468      {
469      cmt_string empty = " ";
470      empty.write(f);
471                 
472      fclose (f);
473      return true;
474      }   
475      */             
476    }
477  return false;
478}
479   
480
481//--------------------------------------------------
482//
483// Check if the file "name1" is identical to "name2"
484// if they are identical, "name1" will be simply deleted
485// otherwise "name1" will be copied to "name2" and deleted afterwards
486//
487//--------------------------------------------------
488bool CmtSystem::compare_and_update_files (const cmt_string& name1,
489                                          const cmt_string& name2)
490{
491  struct stat file_stat1;
492  struct stat file_stat2;
493  static cmt_string s1;
494  static cmt_string s2;
495  int status;
496
497  status = stat (name1.c_str (), &file_stat1);
498
499  if (status == 0)
500    {
501      if ((file_stat1.st_mode & S_IFDIR) != 0)
502        {
503          // name1 is a directory.
504          return (false);
505        }
506    }
507  else
508    {
509      // name1 does not exist
510      return (false);
511    }
512
513  s1.read (name1);
514
515  status = stat (name2.c_str (), &file_stat2);
516
517  if (status == 0)
518    {
519      if ((file_stat2.st_mode & S_IFDIR) != 0)
520        {
521          // name2 is a directory
522          return (false);
523        }
524
525      if (((int) file_stat1.st_size) == ((int) file_stat2.st_size))
526        {
527          s2.read (name2);
528          if (s1 == s2)
529            {
530              unlink (name1);
531              return (true);
532            }
533        }
534    }
535
536  FILE* f = fopen (name2, "wb");
537  if (f != NULL)
538    {
539      s1.write (f);
540      if (ferror (f))
541        return (false);
542      if (fclose (f))
543        return (false);
544
545      unlink (name1);
546
547      return (true);
548    }
549  else
550    {
551      //
552      // keep the new file "name1" since it cannot be
553      // copied to "name2"
554      //
555      return (false);
556    }
557}
558
559//--------------------------------------------------
560int CmtSystem::file_size (const cmt_string& name)
561{
562  struct stat file_stat;
563  int status;
564
565  status = stat (name.c_str (), &file_stat);
566
567  if (status == 0)
568    {
569      return ((int) file_stat.st_size);
570    }
571  else
572    {
573      return (0);
574    }
575}
576
577//--------------------------------------------------
578char CmtSystem::file_separator ()
579{
580#ifdef WIN32
581  return ('\\');
582#else
583  return ('/');
584#endif
585}
586
587/**
588 *  Transform all / or \ characters in the text into the current file_separator
589 *  Reduce all multiple file_separator into single ones.
590 */
591void CmtSystem::reduce_file_separators (cmt_string& text)
592{
593  if (file_separator () == '/')
594    {
595      text.replace_all ("\\", "/");
596      while (text.find ("//") != cmt_string::npos)
597        {
598          text.replace_all ("//", "/");
599        }
600    }
601  else
602    {
603      text.replace_all ("/", "\\");
604      while (text.find ("\\\\") != cmt_string::npos)
605        {
606          text.replace_all ("\\\\", "\\");
607        }
608    }
609}
610
611//--------------------------------------------------
612char CmtSystem::path_separator ()
613{
614#ifdef WIN32
615  return (';');
616#else
617  return (':');
618#endif
619}
620
621//--------------------------------------------------
622char CmtSystem::command_separator ()
623{
624#ifdef WIN32
625  return ('&');
626#else
627  return (';');
628#endif
629}
630
631//--------------------------------------------------
632const cmt_string& CmtSystem::ev_open ()
633{
634#ifdef WIN32
635  static const cmt_string s = "%";
636#else
637  static const cmt_string s = "${";
638#endif
639
640  return (s);
641}
642
643//--------------------------------------------------
644const cmt_string& CmtSystem::ev_close ()
645{
646#ifdef WIN32
647  static const cmt_string s = "%";
648#else
649  static const cmt_string s = "}";
650#endif
651
652  return (s);
653}
654
655//-------------------------------------------------
656bool CmtSystem::create_symlink (const cmt_string& oldname,
657                                const cmt_string& newname)
658{
659  ::unlink (newname.c_str ());
660
661#ifdef WIN32
662  int status = 1;
663#else
664  int status = ::symlink (oldname.c_str (), newname.c_str ());
665#endif
666
667  if (status == 0) return (true);
668  return (false);
669}
670
671//-------------------------------------------------
672bool CmtSystem::remove_file (const cmt_string& name)
673{
674  if (::unlink (name) != 0)
675    {
676      CmtMessage::error ("Cannot remove file " + name);
677      //      cerr << "#CMT> Cannot remove file " << name << endl;
678      return (false);
679    }
680
681  return (true);
682}
683
684//-------------------------------------------------
685bool CmtSystem::remove_directory (const cmt_string& name)
686{
687  //cerr << "Try to remove directory " << name << endl;
688
689  cmt_string_vector files;
690
691  scan_dir (name, files);
692
693  for (int i = 0; i < files.size (); i++)
694    {
695      cmt_string& file = files[i];
696
697      if (test_directory (file))
698        {
699          //cerr << "D=" << file << endl;
700          if (!remove_directory (file)) return (false);
701        }
702      else
703        {
704          //cerr << "F=" << file << endl;
705          if (!remove_file (file)) return (false);
706        }
707    }
708
709  int status = ::rmdir (name);
710  if (status != 0)
711    {
712      char num[32]; sprintf (num, "%d", errno);
713      CmtMessage::error ("Cannot remove directory " + name + " errno=" + num);
714      //      cerr << "#CMT> Cannot remove directory " << name << " errno=" << errno << endl;
715      return (false);
716    }
717
718  return (true);
719}
720
721//-------------------------------------------------
722bool CmtSystem::mkdir (const cmt_string& name)
723{
724  static cmt_string_vector path_vector;
725  int i;
726  static cmt_string full_path;
727  char double_fs[] = "  ";
728
729  double_fs[0] = file_separator ();
730  double_fs[1] = file_separator ();
731
732  full_path = name;
733
734  if (file_separator () == '/')
735    {
736      full_path.replace_all ("\\", file_separator ());
737    }
738  else
739    {
740      full_path.replace_all ("/", file_separator ());
741    }
742
743  full_path.replace_all (double_fs, file_separator ());
744
745  split (full_path, file_separator (), path_vector);
746
747  full_path = "";
748
749  if (absolute_path (name))
750    {
751      if (!has_device (name))
752        {
753          full_path = file_separator ();
754        }
755    }
756
757  for (i = 0; i < path_vector.size (); i++)
758    {
759      const cmt_string& path = path_vector[i];
760
761      if (i > 0) full_path += file_separator ();
762      full_path += path;
763
764      if (has_device (path)) continue;
765
766      if (!test_directory (full_path))
767        {
768#ifdef WIN32
769          if (::_mkdir (full_path.c_str ()) != 0)
770            {
771              // cerr << "CMT> cannot create directory " << full_path << endl;
772              return (false);
773            }
774#else
775          if (::mkdir (full_path.c_str (), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) != 0)
776            {
777              // cerr << "CMT> cannot create directory " << full_path << endl;
778              return (false);
779            }
780#endif
781        }
782    }
783
784  return (true);
785}
786
787//----------------------------------------------------------
788void CmtSystem::scan_dir (const cmt_string& dir_name,
789                          cmt_string_vector& list)
790{
791  static cmt_string dir_prefix;
792  static cmt_string name_prefix;
793
794  dir_prefix = dir_name;
795  if (dir_name == "") dir_prefix = ".";
796
797  if (!test_directory (dir_prefix))
798    {
799      dirname (dir_prefix, dir_prefix);
800      basename (dir_name, name_prefix);
801    }
802  else
803    {
804      name_prefix = "";
805    }
806
807  bool need_filter = false;
808
809  int wild_card;
810
811  wild_card = name_prefix.find ('*');
812  if (wild_card != cmt_string::npos)
813    {
814      name_prefix.erase (wild_card);
815    }
816
817  if (name_prefix.size () > 0)
818    {
819      need_filter = true;
820    }
821
822  list.clear ();
823
824#ifdef WIN32
825
826  long dir;
827  struct _finddata_t entry;
828
829  static cmt_string search;
830
831  search = dir_prefix;
832  search += file_separator ();
833  search += "*";
834
835  dir = _findfirst (search.c_str (), &entry);
836  if (dir > 0)
837    {
838      for (;;)
839        {
840          if ((strcmp ((char*) entry.name, ".") != 0) &&
841              (strcmp ((char*) entry.name, "..") != 0) &&
842              (strncmp ((char*) entry.name, ".nfs", 4) != 0))
843            {
844              const char* name = entry.name;
845
846              if (!need_filter ||
847                  (strncmp (name, name_prefix.c_str (), name_prefix.size ()) == 0))
848                {
849                  cmt_string& name_entry = list.add ();
850
851                  name_entry = dir_prefix;
852                  name_entry += file_separator ();
853                  name_entry += name;
854                }
855            }
856
857          int status = _findnext (dir, &entry);
858          if (status != 0)
859            {
860              break;
861            }
862        }
863
864      _findclose (dir);
865    }
866#else
867
868  //cerr << "scan_dir> dir=" << dir_name << endl;
869
870  DIR* dir = opendir (dir_prefix.c_str ());
871
872  struct dirent* entry;
873
874  if (dir != 0)
875    {
876      while ((entry = readdir (dir)) != 0)
877        {
878          //if (entry->d_name[0] == '.') continue;
879          if (!strcmp ((char*) entry->d_name, ".")) continue;
880          if (!strcmp ((char*) entry->d_name, "..")) continue;
881          if (!strncmp ((char*) entry->d_name, ".nfs", 4)) continue;
882
883          const char* name = entry->d_name;
884
885          if (need_filter &&
886              (strncmp (name, name_prefix.c_str (), name_prefix.size ()) != 0)) continue;
887
888          //cerr << "scan_dir> name=" << name << endl;
889
890          cmt_string& name_entry = list.add ();
891
892          name_entry = dir_prefix;
893          name_entry += file_separator ();
894          name_entry += name;
895        }
896
897      closedir (dir);
898    }
899#endif
900
901}
902
903//----------------------------------------------------------
904void CmtSystem::scan_dir (const cmt_string& dir_name,
905                          const cmt_regexp& expression,
906                          cmt_string_vector& list)
907{
908  static cmt_string dir_prefix;
909
910  dir_prefix = dir_name;
911  if (dir_name == "") dir_prefix = ".";
912
913  if (!test_directory (dir_prefix))
914    {
915      dirname (dir_prefix, dir_prefix);
916    }
917
918  list.clear ();
919
920#ifdef WIN32
921
922  long dir;
923  struct _finddata_t entry;
924
925  static cmt_string search;
926
927  search = dir_prefix;
928  search += file_separator ();
929  search += "*";
930
931  dir = _findfirst (search.c_str (), &entry);
932  if (dir > 0)
933    {
934      for (;;)
935        {
936          if ((entry.name[0] != '.') &&
937              (strcmp ((char*) entry.name, ".") != 0) &&
938              (strcmp ((char*) entry.name, "..") != 0) &&
939              (strncmp ((char*) entry.name, ".nfs", 4) != 0))
940            {
941              const char* name = entry.name;
942             
943              if (expression.match (name))
944                {
945                  cmt_string& name_entry = list.add ();
946
947                  name_entry = dir_prefix;
948                  name_entry += file_separator ();
949                  name_entry += name;
950                }
951            }
952
953          int status = _findnext (dir, &entry);
954          if (status != 0)
955            {
956              break;
957            }
958        }
959      _findclose (dir);
960    }
961#else
962
963  //cerr << "scan_dir> dir=" << dir_name << endl;
964
965  DIR* dir = opendir (dir_prefix.c_str ());
966
967  struct dirent* entry;
968
969  if (dir != 0)
970    {
971      while ((entry = readdir (dir)) != 0)
972        {
973          //if (entry->d_name[0] == '.') continue;
974          if (!strcmp ((char*) entry->d_name, ".")) continue;
975          if (!strcmp ((char*) entry->d_name, "..")) continue;
976          if (!strncmp ((char*) entry->d_name, ".nfs", 4)) continue;
977
978          const char* name = entry->d_name;
979
980          if (!expression.match (name)) continue;
981
982          cmt_string& name_entry = list.add ();
983
984          name_entry = dir_prefix;
985          name_entry += file_separator ();
986          name_entry += name;
987        }
988
989      closedir (dir);
990    }
991#endif
992
993}
994
995//----------------------------------------------------------
996CmtSystem::cmt_string_vector& CmtSystem::scan_dir (const cmt_string& dir_name)
997{
998  static cmt_string_vector result;
999
1000  scan_dir (dir_name, result);
1001
1002  return (result);
1003}
1004
1005//----------------------------------------------------------
1006const cmt_string& CmtSystem::get_cmt_root ()
1007{
1008  static cmt_string root;
1009
1010  root = "";
1011
1012  const char* env = ::getenv ("CMTROOT");
1013  if (env != 0)
1014    {
1015      root = env;
1016
1017      dirname (root, root);
1018      dirname (root, root);
1019      root.replace_all ("\"", "");
1020      return (root);
1021    }
1022
1023#ifdef WIN32
1024  LONG status;
1025  HKEY key = 0;
1026
1027  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
1028                         0, KEY_READ, &key);
1029  if (status == ERROR_SUCCESS)
1030    {
1031      char temp[256];
1032      DWORD length = sizeof (temp) - 1;
1033      DWORD type;
1034
1035      status = RegQueryValueEx (key, "root", 0, &type, (LPBYTE) temp, &length);
1036      if (status == ERROR_SUCCESS)
1037        {
1038          root = temp;
1039          return (root);
1040        }
1041    }
1042#endif
1043
1044  return (root);
1045}
1046
1047//----------------------------------------------------------
1048void CmtSystem::get_cmt_version (cmt_string& version)
1049{
1050  version = "";
1051
1052  const char* env = ::getenv ("CMTROOT");
1053  if (env != 0)
1054    {
1055      cmt_string s = env;
1056      basename (s, version);
1057      version.replace_all ("\"", "");
1058    }
1059  else
1060    {
1061#ifdef WIN32
1062      LONG status;
1063      HKEY key = 0;
1064
1065      status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
1066                             0, KEY_READ, &key);
1067      if (status == ERROR_SUCCESS)
1068        {
1069          char temp[256];
1070          DWORD length = sizeof (temp) - 1;
1071          DWORD type;
1072         
1073          status = RegQueryValueEx (key, "version", 0, &type, 
1074                                    (LPBYTE) temp, &length);
1075          if (status == ERROR_SUCCESS)
1076            {
1077              version = temp;
1078            }
1079        }
1080#endif
1081    }
1082}
1083
1084//----------------------------------------------------------
1085cmt_string CmtSystem::get_cmt_config ()
1086{
1087  const char* env = ::getenv ("CMTCONFIG");
1088  if (env != 0)
1089    {
1090      return (cmt_string (env));
1091    }
1092
1093  env = ::getenv ("CMTBIN");
1094  if (env != 0)
1095    {
1096      return (cmt_string (env));
1097    }
1098
1099#ifdef WIN32
1100  LONG status;
1101  HKEY key = 0;
1102
1103  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
1104                         0, KEY_READ, &key);
1105  if (status == ERROR_SUCCESS)
1106    {
1107      char temp[256];
1108      DWORD length = sizeof (temp) - 1;
1109      DWORD type;
1110
1111      status = RegQueryValueEx (key, "config", 0, &type, 
1112                                (LPBYTE) temp, &length);
1113      if (status == ERROR_SUCCESS)
1114        {
1115          cmt_string config (temp);
1116          return (config);
1117        }
1118    }
1119
1120  return ("VisualC");
1121#endif
1122
1123  return ("");
1124
1125}
1126
1127//----------------------------------------------------------
1128cmt_string CmtSystem::get_cmt_site ()
1129{
1130  const char* env = ::getenv ("CMTSITE");
1131  if (env != 0)
1132    {
1133      return (cmt_string (env));
1134    }
1135
1136#ifdef WIN32
1137  LONG status;
1138  HKEY key = 0;
1139
1140  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
1141                         0, KEY_READ, &key);
1142  if (status == ERROR_SUCCESS)
1143    {
1144      char temp[256];
1145      DWORD length = sizeof (temp) - 1;
1146      DWORD type;
1147
1148      status = RegQueryValueEx (key, "site", 0, &type, (LPBYTE) temp, &length);
1149      if (status == ERROR_SUCCESS)
1150        {
1151          cmt_string site (temp);
1152          return (site);
1153        }
1154    }
1155#endif
1156
1157  return ("");
1158}
1159
1160//----------------------------------------------------------
1161void CmtSystem::get_uname (cmt_string& uname)
1162{
1163#ifdef WIN32
1164  uname = "WIN32";
1165#else
1166
1167  uname = "";
1168  struct utsname buf;
1169
1170  if (::uname (&buf) >= 0)
1171    {
1172      uname = buf.sysname;
1173    }
1174  else
1175    {
1176      perror ("uname");
1177    }
1178
1179  /*
1180  FILE* file;
1181
1182  file = popen ("uname", "r");
1183
1184  if (file != 0)
1185    {
1186      char line[1024];
1187      char* ptr;
1188      char* nl;
1189
1190      line[0] = 0;
1191      ptr = fgets (line, sizeof (line), file);
1192      if (ptr != 0)
1193        {
1194          nl = strrchr (ptr, '\n');
1195          if (nl != 0) *nl = 0;
1196
1197          uname = ptr;
1198        }
1199      pclose (file);
1200    }
1201  */
1202#endif
1203}
1204
1205//----------------------------------------------------------
1206void CmtSystem::get_hosttype (cmt_string& hosttype)
1207{
1208  hosttype = "";
1209
1210  char* ptr;
1211
1212  ptr = ::getenv ("HOSTTYPE");
1213  if (ptr != 0)
1214    {
1215      hosttype = ptr;
1216    }
1217}
1218
1219//----------------------------------------------------------
1220cmt_string CmtSystem::get_temporary_name ()
1221{
1222  cmt_string name;
1223
1224  name = ::tmpnam (NULL);
1225
1226  return (name);
1227}
1228
1229//----------------------------------------------------------
1230cmt_string CmtSystem::get_home_package ()
1231{
1232  cmt_string name = "CMTHOME";
1233
1234  return (name);
1235}
1236
1237//----------------------------------------------------------
1238bool CmtSystem::is_home_package (const cmt_string& name,
1239                                 const cmt_string& version)
1240{
1241  if (name == "CMTHOME") return (true);
1242
1243  return (false);
1244}
1245
1246//----------------------------------------------------------
1247cmt_string CmtSystem::get_user_context_package ()
1248{
1249  cmt_string name = "CMTUSERCONTEXT";
1250
1251  return (name);
1252}
1253
1254//----------------------------------------------------------
1255bool CmtSystem::is_user_context_package (const cmt_string& name,
1256                                         const cmt_string& version)
1257{
1258  if (name == "CMTUSERCONTEXT") return (true);
1259
1260  return (false);
1261}
1262
1263//----------------------------------------------------------
1264cmt_string CmtSystem::get_project_package ()
1265{
1266  cmt_string name = "PROJECT";
1267
1268  return (name);
1269}
1270
1271//----------------------------------------------------------
1272bool CmtSystem::is_project_package (const cmt_string& name,
1273                                    const cmt_string& version)
1274{
1275  if (name == "PROJECT") return (true);
1276
1277  return (false);
1278}
1279
1280//----------------------------------------------------------
1281bool CmtSystem::testenv (const cmt_string& name)
1282{
1283  const char* env = ::getenv (name);
1284  if (env == 0) return (false);
1285  return (true);
1286}
1287
1288//----------------------------------------------------------
1289cmt_string CmtSystem::getenv (const cmt_string& name)
1290{
1291  cmt_string result;
1292
1293  const char* env = ::getenv (name);
1294  if (env != 0)
1295    {
1296      result = env;
1297    }
1298
1299  if (name == "CMTCONFIG")
1300    {
1301      return (get_cmt_config ());
1302    }
1303
1304  /*
1305    if (name == "CMTROOT")
1306    {
1307    return (get_cmt_root ());
1308    }
1309
1310    if (name == "CMTSITE")
1311    {
1312    return (get_cmt_site ());
1313    }
1314  */
1315
1316  return (result);
1317}
1318
1319/**
1320   Implementation based on a local static map of used environment
1321   variables to ensure a stable character string. The OS requires
1322   that the character string sent via a putenv is kept untouched forever
1323*/
1324bool CmtSystem::putenv (const cmt_string& name, const cmt_string& value)
1325{
1326  static cmt_map <cmt_string, cmt_string> envs;
1327
1328  if (!envs.has (name))
1329    {
1330      cmt_string& v = *(new cmt_string);
1331      v = name;
1332      v += "=";
1333      v += value;
1334      envs.add (name, v);
1335    }
1336  else
1337    {
1338      cmt_string& v = *(envs.find (name));
1339      v = name;
1340      v += "=";
1341      v += value;
1342    }
1343
1344  {
1345    const cmt_string& v = *envs.find (name);
1346    //cerr << "#CmtSystem::putenv> name=" << name << " &v=" << &v << endl;
1347
1348    int status = ::putenv ((char*) v.c_str ());
1349
1350    if (status == 0) return (true);
1351    else return (false);
1352  }
1353
1354}
1355
1356//----------------------------------------------------------
1357//
1358// This singleton interacts with the ProjectFactory to consistently create
1359// the project graph.
1360//
1361// In particular a single-depth stack of the top project is maintained.
1362//
1363//----------------------------------------------------------
1364class CMTPathManager
1365{
1366public:
1367  static CMTPathManager& instance ();
1368  static void reset ();
1369  static void add_cmt_path (const cmt_string& path,
1370                            const cmt_string& path_source,
1371                            IProjectFactory& factory);
1372
1373private:
1374  CMTPathManager () : m_project (0)
1375  {
1376  }
1377
1378  void do_reset ()
1379  {
1380    m_project = 0;
1381  }
1382
1383  void do_add_cmt_path (const cmt_string& path,
1384                        const cmt_string& path_source,
1385                        IProjectFactory& factory)
1386  {
1387    cmt_string npath = path;
1388
1389    if (npath == "") return;
1390
1391#ifdef WIN32
1392    if (npath.size () == 2)
1393      {
1394        if (npath[1] == ':')
1395          {
1396            npath += CmtSystem::file_separator ();
1397          }
1398      }
1399#endif
1400
1401    npath.replace_all ("\\", CmtSystem::file_separator ());
1402    npath.replace_all ("/", CmtSystem::file_separator ());
1403
1404    if (!CmtSystem::absolute_path (npath))
1405      {
1406        cmt_string h = CmtSystem::pwd ();
1407        h += CmtSystem::file_separator ();
1408        h += npath;
1409        npath = h;
1410      }
1411   
1412    CmtSystem::compress_path (npath);
1413
1414    //cerr << "adding npath=" << npath << endl;
1415
1416    while (npath[npath.size ()-1] == CmtSystem::file_separator ())
1417      {
1418        npath.erase (npath.size ()-1);
1419      }
1420   
1421    //cerr << "adding npath=[" << npath << "]" << endl;
1422   
1423    if (npath != "")
1424      {
1425        cmt_string project_name;
1426
1427        if ((path_source == "CMTUSERCONTEXT") ||
1428            (path_source == "CMTHOME"))
1429          {
1430            project_name = path_source;
1431          }
1432
1433        m_project = factory.create_project (project_name, npath, path_source, 0);
1434        //      m_project = factory.create_project (project_name, npath, path_source, m_project);
1435        /*
1436        if ((path_source == "CMTUSERCONTEXT") ||
1437            (path_source == "CMTHOME"))
1438          {
1439            m_project = 0;
1440          }
1441        */
1442      }
1443  }
1444
1445  Project* m_project;
1446};
1447
1448CMTPathManager& CMTPathManager::instance ()
1449{
1450  static CMTPathManager me;
1451 
1452  return (me);
1453}
1454
1455void CMTPathManager::reset ()
1456{
1457  static CMTPathManager& me = instance ();
1458  me.do_reset ();
1459}
1460
1461void CMTPathManager::add_cmt_path (const cmt_string& path,
1462                                   const cmt_string& path_source,
1463                                   IProjectFactory& factory)
1464{
1465  static CMTPathManager& me = instance ();
1466  me.do_add_cmt_path (path, path_source, factory);
1467}
1468
1469
1470
1471//----------------------------------------------------------
1472static void add_cmt_paths_from_text (const cmt_string& text,
1473                                     const cmt_string& context,
1474                                     IProjectFactory& factory)
1475{
1476  static CmtSystem::cmt_string_vector path_vector;
1477  int i;
1478
1479  CmtSystem::split (text, CmtSystem::path_separator (), path_vector);
1480
1481  for (i = 0; i < path_vector.size (); i++)
1482    {
1483      const cmt_string& path = path_vector[i];
1484
1485      CMTPathManager::add_cmt_path (path, context, factory);
1486    }
1487}
1488
1489//----------------------------------------------------------
1490static void add_cmt_paths_from_file (const cmt_string& file_name, IProjectFactory& factory)
1491{
1492  if (!CmtSystem::test_file (file_name)) return;
1493
1494  static cmt_string text;
1495
1496  text.read (file_name);
1497
1498  int pos = text.find ("CMTPATH");
1499  if (pos == cmt_string::npos) return;
1500  pos += strlen ("CMTPATH");
1501  pos = text.find (pos, "=");
1502  if (pos == cmt_string::npos) return;
1503  pos++;
1504
1505  text.erase (0, pos);
1506
1507  int nl = text.find (pos, "\n");
1508  if (nl != cmt_string::npos) text.erase (nl);
1509
1510  add_cmt_paths_from_text (text, file_name, factory);
1511}
1512
1513//----------------------------------------------------------
1514//
1515// With this function we analyse all possible ways of
1516// externally entering CMTPATH items
1517//  + from the environment variable
1518//  + from .cmtrc files
1519//  + from registry on Windows
1520//  + from EV settings for CMTUSERCONTEXT and CMTHOME
1521//
1522// Then projects are created from these settings.
1523//
1524// (The other way to enter project graph is through project files)
1525//----------------------------------------------------------
1526void CmtSystem::get_cmt_paths (IProjectFactory& factory, 
1527                               const cmt_string& init_text,
1528                               const cmt_string& cmt_user_context,
1529                               const cmt_string& cmt_home)
1530{
1531  CMTPathManager::reset ();
1532
1533  CMTPathManager::add_cmt_path (cmt_user_context, "CMTUSERCONTEXT", factory);
1534  CMTPathManager::add_cmt_path (cmt_home, "CMTHOME", factory);
1535  /*
1536  if (init_text != "")
1537    {
1538      add_cmt_paths_from_text (init_text, "initialization", factory);
1539    }
1540  */
1541
1542#ifdef WIN32
1543  LONG status;
1544  HKEY key = 0;
1545
1546  status = RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\CMT\\path",
1547                         0, KEY_READ, &key);
1548  if (status == ERROR_SUCCESS)
1549    {
1550      DWORD index = 0;
1551      char name[256];
1552      char temp[256];
1553
1554      for (;;)
1555        {
1556          DWORD name_length = sizeof (name) - 1;
1557          DWORD length = sizeof (temp) - 1;
1558          DWORD type;
1559          status = RegEnumValue (key, index,
1560                                 name, &name_length, 0, &type,
1561                                 (LPBYTE) temp, &length);
1562          if ((status == ERROR_SUCCESS) ||
1563              (status == 234))
1564            {
1565              const cmt_string path = temp;
1566              CMTPathManager::add_cmt_path (path, "HKEY_CURRENT_USER", factory);
1567            }
1568
1569          if (status == 259)
1570            {
1571              break;
1572            }
1573
1574          index++;
1575        }
1576    }
1577
1578  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT\\path",
1579                         0, KEY_READ, &key);
1580  if (status == ERROR_SUCCESS)
1581    {
1582      DWORD index = 0;
1583      char name[256];
1584      char temp[256];
1585
1586      for (;;)
1587        {
1588          DWORD type;
1589          DWORD name_length = sizeof (name) - 1;
1590          DWORD length = sizeof (temp) - 1;
1591
1592
1593          status = RegEnumValue (key, index,
1594                                 name, &name_length, 0, &type,
1595                                 (LPBYTE) temp, &length);
1596          if (status != ERROR_NO_MORE_ITEMS)
1597            {
1598              const cmt_string path = temp;
1599              CMTPathManager::add_cmt_path (path, "HKEY_LOCAL_MACHINE", factory);
1600            }
1601          else
1602            {
1603              break;
1604            }
1605          index++;
1606        }
1607    }
1608
1609#endif
1610
1611  //-----------------------------------------
1612  // look for .cmtrc files :
1613  //  first look in ./
1614  //  then in "~/"
1615  //  then in ${CMTROOT}/mgr
1616  //-----------------------------------------
1617  cmt_string rc_name;
1618
1619  add_cmt_paths_from_file (".cmtrc", factory);
1620
1621  if (get_home_directory (rc_name))
1622    {
1623      rc_name += file_separator ();
1624      rc_name += ".cmtrc";
1625      add_cmt_paths_from_file (rc_name, factory);
1626    }
1627
1628  rc_name = get_cmt_root ();
1629  rc_name += file_separator ();
1630  rc_name += "CMT";
1631  rc_name += file_separator ();
1632  cmt_string version;
1633  get_cmt_version (version);
1634  rc_name += version;
1635  rc_name += file_separator ();
1636  rc_name += "mgr";
1637  rc_name += file_separator ();
1638  rc_name += ".cmtrc";
1639
1640  add_cmt_paths_from_file (rc_name, factory);
1641
1642  /*
1643  CMTPathManager::add_cmt_path (cmt_user_context, "CMTUSERCONTEXT", factory);
1644  CMTPathManager::add_cmt_path (cmt_home, "CMTHOME", factory);
1645  */
1646  if (init_text != "")
1647    {
1648      add_cmt_paths_from_text (init_text, "initialization", factory);
1649    }
1650}
1651
1652//----------------------------------------------------------
1653int CmtSystem::execute (const cmt_string& command)
1654{
1655  //cerr << "CmtSystem::execute1> [" << command << "]" << endl;
1656  int status = system (command.c_str ());
1657  if (status == -1) // failed
1658    return -1;
1659  else
1660    return WEXITSTATUS(status); // return status of the command
1661  //  return (system (command.c_str ()));
1662}
1663
1664//----------------------------------------------------------
1665int CmtSystem::execute (const cmt_string& command, cmt_string& output)
1666{
1667  output = "";
1668
1669  //  cerr << "CmtSystem::execute2> [" << command << "]" << endl;
1670
1671  FILE* f = popen (command.c_str (), "r"); 
1672 
1673  if (f != 0) 
1674    { 
1675      char line[PATH_MAX]; 
1676      //      char line[256];
1677      char* ptr;
1678
1679      while ((ptr = fgets (line, sizeof (line), f)) != NULL) 
1680        {
1681          output += ptr;
1682        } 
1683
1684      int status = pclose (f);
1685      if (status == -1) // error reported by pclose ()
1686        {
1687          perror ("pclose");
1688          if (errno == ECHILD)
1689            {
1690              return (0);
1691            }
1692          return (-1);
1693        }
1694      else
1695        {
1696          return WEXITSTATUS(status); // return status of the command
1697          //return (0);
1698        }
1699    }
1700
1701  return (-2);
1702}
1703
1704//----------------------------------------------------------
1705bool CmtSystem::is_package_directory (const cmt_string& name)
1706{
1707  cmt_string_vector dirs;
1708
1709  cmt_regexp exp ("^[a-zA-Z.][0-9]+([a-zA-Z.][0-9]+([a-zA-Z.][0-9]+)?)?");
1710
1711  scan_dir (name, exp, dirs);
1712
1713  cmt_string req;
1714
1715  req = name;
1716  req += file_separator ();
1717  req += "cmt";
1718  req += file_separator ();
1719  req += "requirements";
1720
1721  if (test_file (req)) return (true);
1722
1723  if (dirs.size () == 0) 
1724    {
1725      return (false);
1726    }
1727
1728  for (int i = 0; i < dirs.size (); i++)
1729    {
1730      const cmt_string& d = dirs[i];
1731
1732      req = d;
1733      req += file_separator ();
1734      req += "mgr";
1735      req += file_separator ();
1736      req += "requirements";
1737
1738      if (test_file (req)) return (true);
1739
1740      req = d;
1741      req += file_separator ();
1742      req += "cmt";
1743      req += file_separator ();
1744      req += "requirements";
1745     
1746      if (test_file (req)) return (true);
1747    }
1748
1749  return (false);
1750}
1751
1752//----------------------------------------------------------
1753bool CmtSystem::is_version_directory (const cmt_string& name)
1754{
1755  int v;
1756  int r;
1757  int p;
1758
1759  return (is_version_directory (name, v, r, p));
1760}
1761
1762//----------------------------------------------------------
1763bool CmtSystem::is_version_directory (const cmt_string& name,
1764                                      int& v,
1765                                      int& r,
1766                                      int& p)
1767{
1768  if ((name == "HEAD") || (name == "head"))
1769    {
1770      v = 0;
1771      r = 0;
1772      p = 0;
1773
1774      return (true);
1775    }
1776
1777  static const cmt_string numbers = "0123456789";
1778
1779  static const int id_version = 0;
1780  static const int id_release = 1;
1781  static const int id_patch   = 2;
1782
1783  cmt_string buffer;
1784
1785  enum 
1786    {
1787      starting,
1788      at_key,
1789      at_number
1790    } state;
1791
1792  int id;
1793  int pos;
1794  int value;
1795
1796  v = 0;
1797  r = 0;
1798  p = 0;
1799
1800  //
1801  // version : v-field
1802  //         | v-field r-field
1803  //         | v-field r-field p-field
1804  //
1805  // v-field : field
1806  // r-field : field
1807  // p-field : field
1808  //
1809  // field   : key '*'
1810  //         | key number
1811  //
1812  // key     : letters
1813  //
1814
1815  state = starting;
1816  id    = id_version;
1817
1818  for (pos = 0; pos < name.size (); pos++)
1819    {
1820      char c = name[pos];
1821
1822      if (c == '*')
1823        {
1824          // A wild card
1825          switch (state)
1826            {
1827            case starting:
1828              // cannot start with a wild card ??
1829              v = -1;
1830              r = -1;
1831              p = -1;
1832              return (false);
1833            case at_key:
1834              // the numeric field is valued with a wild card
1835              switch (id)
1836                {
1837                case id_version:
1838                  v = -1;
1839                case id_release:
1840                  r = -1;
1841                case id_patch:
1842                  p = -1;
1843                  break;
1844                }
1845              return (true);
1846            case at_number:
1847              // question:
1848              // a number followed by a wild-card is considered as:
1849              //   1) a wild card on the number itself (1* comp with 1, 10, 12, 120, etc)
1850              //   2) a wild card on the next fields (1* comp with 1r1, 1-12 etc)
1851              //
1852
1853              //  Here we select option 1)
1854
1855              sscanf (buffer.c_str (), "%d", &value);
1856              switch (id)
1857                {
1858                case id_version:
1859                  //
1860                  // lazy option 1 implies v = -1;
1861                  // strict option 1 would imply v = -value;
1862                  // option 2 implies v = value;
1863                  //
1864
1865                  v = -1;
1866                  r = -1;
1867                  p = -1;
1868                  break;
1869                case id_release:
1870                  r = value;
1871                  p = -1;
1872                  break;
1873                case id_patch:
1874                  p = value;
1875                  break;
1876                }
1877
1878              return (true);
1879            }
1880        }
1881      else if (numbers.find (c) == cmt_string::npos)
1882        {
1883          // A letter
1884          switch (state)
1885            {
1886            case starting:
1887              state = at_key;
1888              break;
1889            case at_key:
1890              // Multiple letter key (is it permitted??)
1891              break;
1892            case at_number:
1893              sscanf (buffer.c_str (), "%d", &value);
1894              switch (id)
1895                {
1896                case id_version:
1897                  v = value;
1898                  break;
1899                case id_release:
1900                  r = value;
1901                  break;
1902                case id_patch:
1903                  p = value;
1904                  break;
1905                }
1906              buffer = "";
1907              id++;
1908              state = at_key;
1909              break;
1910            }
1911        }
1912      else
1913        {
1914          // a number
1915          switch (state)
1916            {
1917            case starting:
1918              // not starting by a letter (syntax error)
1919              //return (false);
1920            case at_key:
1921              // the numeric field for the current id is starting now
1922              buffer += c;
1923              state = at_number;
1924              break;
1925            case at_number:
1926              // continuing the current numeric field
1927              buffer += c;
1928              break;
1929            }
1930        }
1931    }
1932
1933  switch (state)
1934    {
1935    case starting:
1936      // Empty version string
1937      return (false);
1938    case at_key:
1939      // Syntax error (when only letters. Ending letters is not an error)
1940      if (id == id_version) return (false);
1941      else return (true);
1942    case at_number:
1943      sscanf (buffer.c_str (), "%d", &value);
1944      switch (id)
1945        {
1946        case id_version:
1947          v = value;
1948          break;
1949        case id_release:
1950          r = value;
1951          break;
1952        case id_patch:
1953          p = value;
1954          break;
1955        }
1956      id++;
1957      state = at_key;
1958      return (true);
1959    }
1960
1961  return (false);
1962}
1963
1964//----------------------------------------------------------
1965//  Split a line into words. Separators are spaces and tabs
1966//  Text enclosed in double quotes is one word.
1967//----------------------------------------------------------
1968void CmtSystem::split (const cmt_string& text,
1969                       const cmt_string& separators,
1970                       cmt_string_vector& strings)
1971{
1972  static char* buffer = 0;
1973  static int allocated = 0;
1974
1975  bool finished = false;
1976
1977  strings.clear ();
1978
1979  if (text.size () == 0) return;
1980
1981  /*
1982    We are going to work in a copy of the text, since
1983    \0 will be inserted right after each found word.
1984
1985    Then the vector of strings is iteratively filled by each found word.
1986  */
1987
1988  if (buffer == 0)
1989    {
1990      allocated = text.size ();
1991      buffer = (char*) malloc (allocated + 1);
1992    }
1993  else
1994    {
1995      if (text.size () > allocated)
1996        {
1997          allocated = text.size ();
1998          buffer = (char*) realloc (buffer, allocated + 1);
1999        }
2000    }
2001
2002  strcpy (buffer, text.c_str ());
2003
2004  /*
2005    Algorithm :
2006
2007    We look for words separated by <separators> which may be
2008    o spaces (' ' or '\t')
2009    o other characters such as ':'
2010
2011    A word is a character string not containing any separator. A substring in
2012    this word my be enclosed between quotes (" or ') which permits separator
2013    inclusion within words.
2014  */
2015
2016  char* current_word = buffer;
2017
2018  while (*current_word != 0)
2019    {
2020      size_t prefix_length;
2021      size_t word_length;
2022
2023      /*
2024        while ((*current_word == ' ') ||
2025        (*current_word == '\t'))
2026        {
2027        current_word++;
2028        }
2029      */
2030
2031      // first skip all starting separators.
2032
2033      prefix_length = strspn (current_word, separators.c_str ());
2034      if (prefix_length > 0)
2035        {
2036          // Move to the first non-separator character
2037
2038          current_word += prefix_length;
2039        }
2040
2041      /*
2042        Parse the next word.
2043
2044        It may contain enclosures in quote characters or not.
2045        Quotes must be identical on both sides of each enclosure.
2046      */
2047
2048      char* running_char = current_word;
2049
2050      word_length = 0;
2051
2052      for (;;)
2053        {
2054          size_t unquoted_length;
2055          size_t separator_offset;
2056
2057          for (int p = 0;;)
2058            {
2059              unquoted_length = strcspn (running_char + p, "\"\'") + p;
2060              if ((unquoted_length > 0) && (running_char[unquoted_length-1] == '\\'))
2061                {
2062                  p = unquoted_length + 1;
2063                }
2064              else
2065                {
2066                  break;
2067                }
2068            }
2069
2070          separator_offset = strcspn (running_char, separators.c_str ());
2071
2072          if (separator_offset <= unquoted_length)
2073            {
2074              // no quote in this word -> we are finished for this one.
2075              running_char += separator_offset;
2076              break;
2077            }
2078
2079          // We have found a quoted enclosure. Move to it.
2080
2081          running_char += unquoted_length;
2082
2083          char quote = running_char[0];
2084
2085          // Remove it.
2086          {
2087            char* p = running_char;
2088            while (p[1] != 0)
2089              {
2090                *p = p[1];
2091                p++;
2092              }
2093            *p = 0;
2094          }
2095
2096          // Look for the next occurence of this quote.
2097          {
2098            char* p = strchr (running_char, quote);
2099            if (p == 0)
2100              {
2101                // Unmatched quote : the rest of the line will be taken as a word...
2102                running_char += strlen (running_char);
2103                finished = true;
2104                break;
2105              }
2106            else
2107              {
2108                running_char = p;
2109              }
2110          }
2111
2112          // Now we remove the ending quote from the word
2113          // (by shifting all remaining characters by one place to the left)
2114
2115          {
2116            char* p = running_char;
2117            while (p[1] != 0)
2118              {
2119                *p = p[1];
2120                p++;
2121              }
2122            *p = 0;
2123          }
2124        }
2125
2126      word_length = running_char - current_word;
2127
2128      if (current_word[word_length] == 0)
2129        {
2130          finished = true;
2131        }
2132      else
2133        {
2134          current_word[word_length] = 0;
2135        }
2136
2137      /*
2138        if ((t[0] == '"') ||
2139        (t[0] == '\'') ||
2140        (t[0] == ':'))
2141        {
2142        char* quote;
2143
2144        t++;
2145        quote = strchr (t, sep);
2146        if (quote != 0) *quote = 0;
2147        else finished = true;
2148        }
2149        else
2150        {
2151        int offset;
2152
2153        offset = strcspn (t, " \t:");
2154        if ((offset < 0) || (t[offset] == 0)) finished = true;
2155        if (!finished)
2156        {
2157        space = t + offset;
2158        *space = 0;
2159        }
2160        }
2161      */
2162
2163      // Store the current word into the vector of strings
2164
2165      {
2166        cmt_string& s = strings.add ();
2167        s = current_word;
2168      }
2169
2170      if (finished) break;
2171
2172      // Move to the next possible word.
2173      current_word += word_length + 1;
2174    }
2175}
2176
2177//-------------------------------------------------------------
2178//  Quote separators (spaces and tabs) with double quotes,
2179//  double quotes with single quotes,
2180//  single quotes with double quotes in text.
2181//  Note: quotes preceded by backslash (i.e., \" or \') are NOT quoted
2182//  (considered escaped)
2183//-------------------------------------------------------------
2184cmt_string CmtSystem::quote (const cmt_string& text,
2185                             const cmt_string& separators)
2186{
2187  //cerr << "quote: `" << text << "'" << endl;
2188  cmt_string result;
2189  if (text.size () == 0) return result;
2190
2191  size_t allocated = 3 * text.size (); // if EACH character of text quoted with " or '
2192  char* const buffer = (char*) malloc (allocated + 1);
2193  if (0 == buffer)
2194    {
2195      char num[32]; sprintf (num, "%u", allocated + 1);
2196      cmt_string msg ("Cannot allocate ");
2197      msg += num;
2198      msg += " bytes";
2199      cmt_string e = errno ? ": " + cmt_string (strerror (errno)) : "";
2200      CmtMessage::error (msg + e);
2201      exit (EXIT_FAILURE);
2202    }
2203  char* b (buffer);
2204
2205  //  char* const beg (buffer);
2206  const char* p = text.c_str ();
2207  const char* const text_c (p);
2208  //  const char* const beg_t (p);
2209  //  cerr << "quote: p = `" << p << "'" << endl;
2210
2211  while (*p)
2212    //  while (*p != '\0')
2213    {
2214      size_t l_nonsep = strcspn (p, separators.c_str ());
2215      //cerr << "quote: l_nonsep = " << l_nonsep << " *p = '" << *p << "'" << endl;
2216      while (l_nonsep--)
2217        {
2218          if (*p == '\"' &&
2219              (p > text_c && *(p - 1) != '\\' || p == text_c))
2220            { // quote " with '
2221              *b++ = '\'';
2222              *b++ = *p++;
2223              *b++ = '\'';
2224            }
2225          else if (*p == '\'' &&
2226                   (p > text_c && *(p - 1) != '\\' || p == text_c))
2227            { // quote ' with "
2228              *b++ = '\"';
2229              *b++ = *p++;
2230              *b++ = '\"';
2231            }
2232          else
2233            { // simply copy
2234              *b++ = *p++;
2235            }
2236        }
2237      size_t l_sep = strspn (p, separators.c_str ());
2238      //cerr << "quote: l_sep = " << l_sep << " *p = '" << *p << "'" << endl;
2239      if (l_sep)
2240        { // quote separators with "
2241          // place quote before all backslashes preceding separators, if any
2242          char* r = b;
2243          while (r > buffer && *(r - 1) == '\\')
2244            r--;
2245          if (r == b)
2246            *b++ = '\"';
2247          else
2248            *r = '\"', *b++ = '\\';
2249          while (l_sep--)
2250            *b++ = *p++;
2251          *b++ = '\"';
2252        }
2253    }
2254  *b = '\0';
2255  result = buffer;
2256  free (buffer);
2257  return result;
2258}
2259
2260//----------------------------------------------------------
2261//  Mangle text so that it consists solely of
2262//  letters, digits, and underscores
2263//  the first character being not digit
2264//  (any other characters are replaced with character '_').
2265//  The out can be used as shell variable name.
2266//  Return the number of characters replaced with character '_'.
2267//----------------------------------------------------------
2268int CmtSystem::mangle (const cmt_string& text, cmt_string& out)
2269{
2270  int result (0);
2271  size_t allocated = text.size ();
2272  //allocated = 3000000000;
2273  char* const buffer = (char*) malloc (allocated + 1);
2274  if (0 == buffer)
2275    {
2276      char num[32]; sprintf (num, "%u", allocated + 1);
2277      cmt_string msg ("Cannot allocate ");
2278      msg += num;
2279      msg += " bytes";
2280      cmt_string e = errno ? ": " + cmt_string (strerror (errno)) : "";
2281      CmtMessage::error (msg + e);
2282      exit (EXIT_FAILURE);
2283    }
2284  char* b (buffer);
2285  const char* t = text.c_str ();
2286  while (*t)
2287    {
2288      if (isalnum (*t) || *t == '_')
2289        *b++ = *t++;
2290      else
2291        {
2292          *b++ = '_'; t++;
2293          result++;
2294        }
2295    }
2296  *b = '\0';
2297  if (0 != allocated && isdigit (*buffer))
2298    {
2299      *buffer = '_';
2300      result++;
2301    }
2302  out = buffer;
2303  free (buffer);
2304  return result;
2305}
2306
2307cmt_string CmtSystem::mangle (const cmt_string& text)
2308{
2309  cmt_string result;
2310  mangle (text, result);
2311  return result;
2312}
2313
2314//----------------------------------------------------------
2315void CmtSystem::compress_path (const cmt_string& dir, cmt_string& new_dir)
2316{
2317  new_dir = dir;
2318
2319  compress_path (new_dir);
2320}
2321
2322//----------------------------------------------------------
2323//
2324//  We try to detect the aaaa/xxxx/../bbbb patterns which should be
2325// equivalent to aaaa/bbbb
2326//  this therefore consists in removing all /xxxx/../ when
2327//     xxxx is different from ".."
2328//     xxxx is different from "."
2329//     xxxx does not contain any macro reference
2330//
2331// Also replace "/.." with "/". One cannot walk down past the root.
2332//----------------------------------------------------------
2333void CmtSystem::compress_path (cmt_string& dir)
2334{
2335#ifdef WIN32
2336  static const char pattern[] = "\\..";
2337  static const char fs[] = "\\\\";
2338#else
2339  static const char pattern[] = "/..";
2340  static const char fs[] = "//";
2341#endif
2342
2343  if (dir.size () == 0) return;
2344
2345  //
2346  // We first synchronize to using file_separator() in any case.
2347  //
2348
2349  if (file_separator () == '/')
2350    {
2351      dir.replace_all ("\\", file_separator ());
2352    }
2353  else
2354    {
2355      dir.replace_all ("/", file_separator ());
2356    }
2357
2358  // Suppress all duplicated file separators
2359  dir.replace_all (fs, file_separator ());
2360
2361  for (;;)
2362    {
2363      int pos0 (0);
2364      int pos1;
2365      int pos2;
2366      int pos3;
2367
2368      //pos1 = dir.find (pattern);
2369      //if (pos1 == cmt_string::npos) break;
2370
2371      do
2372        {
2373          pos1 = dir.find (pos0, pattern);
2374          if (pos1 == cmt_string::npos) break;
2375          pos0 = pos1 + 3;
2376        }
2377      while (pos0 < dir.size () && dir[pos0] != file_separator ());
2378
2379      if (pos1 == cmt_string::npos) break;
2380
2381      //
2382      // One cannot walk down past the root: "/.." is the same as "/".
2383      //
2384#ifdef WIN32
2385      if (pos1 == 0)
2386        {
2387          dir.erase (pos1, 3);
2388          if (dir == "")
2389            dir = file_separator ();
2390          continue;
2391        }
2392      else if (pos1 == 2 && dir[1] == ':')
2393        {
2394          dir.erase (pos1, 3);
2395          if (dir.size () == 2)
2396            dir += file_separator ();
2397          continue;
2398        }
2399#else
2400      if (pos1 == 0)
2401        {
2402          dir.erase (pos1, 3);
2403          if (dir == "")
2404            dir = file_separator ();
2405          continue;
2406        }
2407#endif
2408
2409      //
2410      // extract "aaaa/xxxx" from "aaaa/xxxx/../bbbb"
2411      //
2412      cmt_string p = dir.substr (0, pos1);
2413
2414      cmt_string dn;
2415      basename (p, dn);
2416      if (dn == "..") break;
2417      if (dn == ".") break;
2418      if (dn == "") break;
2419     
2420      //
2421      // Is "aaaa/xxxx" only made of "xxxx" ?
2422      //
2423      pos2 = p.find_last_of (file_separator ());
2424     
2425      if (pos2 == cmt_string::npos) 
2426        {
2427          // the pattern was xxxx/../bbbb
2428          //
2429          // so xxxx is [0:pos1-1]
2430          //
2431          // erase the "xxxx/.." pattern
2432          // result will be "/bbbb"
2433          // so, need to process a little more
2434          //
2435          pos3 = p.find ("$");
2436          if (pos3 == cmt_string::npos)
2437            {
2438              dir.erase (0, pos1 + 3);
2439              if (dir.size () < 2)
2440                {
2441                  dir = ".";
2442                }
2443              else
2444                {
2445                  dir.erase (0, 1);
2446                }
2447            }
2448          else
2449            {
2450              break;
2451            }
2452        }
2453      else
2454        {
2455          //    01234567890123456
2456          //    aaaa/xxxx/../bbbb
2457          //        2    1   3
2458          //
2459          // erase the "/xxxx/.." pattern
2460          // result will be "aaaa/bbbb"
2461          //
2462          // Here xxxx is [pos2+1:pos1-1]
2463          //
2464
2465          pos3 = p.find (pos2, "$");
2466          if (pos3 == cmt_string::npos)
2467            {
2468              dir.erase (pos2, pos1 + 3 - pos2);
2469#ifdef WIN32
2470              if (dir == "")
2471                {
2472                  dir = file_separator ();
2473                }
2474              else if (dir.size () == 2 && dir[1] == ':')
2475                {
2476                  dir += file_separator ();
2477                }
2478#else
2479              if (dir == "")
2480                dir = file_separator ();
2481#endif
2482            }
2483          else
2484            {
2485              break;
2486            }
2487        }
2488    }
2489
2490  //if (dir[dir.size () - 1] == file_separator ()) dir.erase (dir.size () - 1);
2491}
2492
2493//----------------------------------------------------------
2494cmt_string CmtSystem::now ()
2495{
2496  cmt_string result;
2497
2498  time_t ltime;
2499  time (&ltime);
2500  result = ctime (&ltime);
2501
2502  result.replace_all ("\n", "");
2503
2504  return (result);
2505}
2506
2507//----------------------------------------------------------
2508cmt_string CmtSystem::user ()
2509{
2510#ifdef _WIN32
2511  cmt_string result = getenv ("USERNAME");
2512#else
2513  cmt_string result = getenv ("USER");
2514#endif
2515
2516  return (result);
2517}
2518
2519//----------------------------------------------------------
2520void CmtSystem::get_cvsroot (cmt_string& cvsroot)
2521{
2522  cvsroot = "";
2523
2524  const char* env = ::getenv ("CVSROOT");
2525  if (env != 0)
2526    {
2527      cvsroot = env;
2528      return;
2529    }
2530
2531#ifdef WIN32
2532  LONG status;
2533  HKEY key = 0;
2534
2535  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
2536                         0, KEY_READ, &key);
2537  if (status == ERROR_SUCCESS)
2538    {
2539      char temp[256];
2540      DWORD length = sizeof (temp) - 1;
2541      DWORD type;
2542
2543      status = RegQueryValueEx (key, "CVSROOT", 0, &type, 
2544                                (LPBYTE) temp, &length);
2545      if (status == ERROR_SUCCESS)
2546        {
2547          cvsroot = temp;
2548          return;
2549        }
2550    }
2551#endif
2552}
2553
2554//----------------------------------------------------------
2555bool CmtSystem::get_home_directory (cmt_string& dir)
2556{
2557  bool status = false;
2558
2559#ifdef WIN32
2560  const char* homedrive = ::getenv ("HOMEDRIVE");
2561  const char* homepath = ::getenv ("HOMEPATH");
2562
2563  if ((homedrive != 0) && (homepath != 0))
2564    {
2565      dir = homedrive;
2566      dir += homepath;
2567      status = true;
2568    }
2569
2570#else
2571  const char* home_env = ::getenv ("HOME");
2572  if (home_env != 0)
2573    {
2574      dir = home_env;
2575      status = true;
2576    }
2577#endif
2578
2579  return (status);
2580}
2581
2582//----------------------------------------------------------
2583cmt_string CmtSystem::get_makefile_suffix ()
2584{
2585#ifdef WIN32
2586  return "nmake";
2587#else
2588  return "make";
2589#endif
2590}
2591
2592//----------------------------------------------------------
2593void CmtSystem::close_ostream (FILE *stream, const cmt_string& name)
2594{
2595  cmt_string msg ("Cannot write");
2596  cmt_string n = (name != "") ? " " + name : "";
2597  if (stream == NULL)
2598    {
2599      CmtMessage::error (msg + n);
2600      exit (EXIT_FAILURE);
2601    }
2602
2603  int prev_fail = ferror (stream);
2604  int fclose_fail = fclose (stream);
2605//   bool prev_fail = ferror (stream);
2606//   bool fclose_fail = fclose (stream);
2607
2608  if (prev_fail || fclose_fail)
2609    {
2610      int err = fclose_fail ? errno : 0;
2611      cmt_string e = err ? ": " + cmt_string (strerror (err)) : "";
2612      CmtMessage::error (msg + n + e);
2613      exit (EXIT_FAILURE);
2614    }
2615}
2616
2617//----------------------------------------------------------
2618void CmtSystem::close_stdout (void)
2619{
2620  /*
2621   * The idea and, to some extent, implementation of this function
2622   * were borrowed from the GNU core utilities of the Free Software Foundation
2623   * http://www.gnu.org/software/coreutils/
2624   */
2625  close_ostream (stdout, "stdout");
2626}
2627
2628//----------------------------------------------------------
2629FilePath::FilePath ()
2630{
2631  p_name = "";
2632  l_name = "";
2633  alternates.resize (0);
2634}
2635
2636//----------------------------------------------------------
2637FilePath::FilePath (const FilePath& other)
2638{
2639  set (other);
2640}
2641
2642//----------------------------------------------------------
2643FilePath::FilePath (const cmt_string& other)
2644{
2645  set (other);
2646}
2647
2648//----------------------------------------------------------
2649FilePath::FilePath (const char* other)
2650{
2651  set (other);
2652}
2653
2654//----------------------------------------------------------
2655FilePath& FilePath::operator = (const FilePath& other)
2656{
2657  FilePath& me = *this;
2658
2659  me.set (other);
2660
2661  return (me);
2662}
2663
2664//----------------------------------------------------------
2665FilePath& FilePath::operator = (const cmt_string& other)
2666{
2667  FilePath& me = *this;
2668
2669  me.set (other);
2670
2671  return (me);
2672}
2673
2674//----------------------------------------------------------
2675FilePath& FilePath::operator = (const char* other)
2676{
2677  FilePath& me = *this;
2678
2679  me.set (other);
2680
2681  return (me);
2682}
2683
2684//----------------------------------------------------------
2685bool FilePath::operator == (const FilePath& other) const
2686{
2687  if (other.p_name == p_name) return (true);
2688  return (false);
2689}
2690
2691//----------------------------------------------------------
2692bool FilePath::operator == (const cmt_string& other) const
2693{
2694  if (p_name == other) return (true);
2695  if (l_name == other) return (true);
2696
2697  for (int i = 0; i < alternates.size (); i++)
2698    {
2699      if (alternates[i] == other) return (true);
2700    }
2701
2702  cmt_string here = CmtSystem::pwd ();
2703  CmtSystem::cd (other);
2704  cmt_string p = CmtSystem::pwd ();
2705  CmtSystem::cd (here);
2706
2707  if (p_name == p) return (true);
2708
2709  return (false);
2710}
2711
2712//----------------------------------------------------------
2713bool FilePath::operator == (const char* other) const
2714{
2715  const FilePath& me = *this;
2716
2717  const cmt_string o = other;
2718
2719  return ((me == o));
2720
2721  return (false);
2722}
2723
2724//----------------------------------------------------------
2725bool FilePath::operator != (const FilePath& other) const
2726{
2727  const FilePath& me = *this;
2728
2729  return (!(me == other));
2730}
2731
2732//----------------------------------------------------------
2733bool FilePath::operator != (const cmt_string& other) const
2734{
2735  const FilePath& me = *this;
2736
2737  return (!(me == other));
2738}
2739
2740//----------------------------------------------------------
2741bool FilePath::operator != (const char* other) const
2742{
2743  const FilePath& me = *this;
2744
2745  return (!(me == other));
2746}
2747
2748//----------------------------------------------------------
2749bool FilePath::cd () const
2750{
2751  CmtSystem::cd (l_name);
2752
2753  return (false);
2754}
2755
2756//----------------------------------------------------------
2757void FilePath::set (const FilePath& other)
2758{
2759  p_name = other.p_name;
2760  l_name = other.l_name;
2761  alternates = other.alternates;
2762}
2763
2764//----------------------------------------------------------
2765void FilePath::set (const cmt_string& other)
2766{
2767  // Skip if no change.
2768
2769  if (p_name == other) return;
2770  if (l_name == other) return;
2771
2772  for (int i = 0; i < alternates.size (); i++)
2773    {
2774      if (alternates[i] == other) return;
2775    }
2776
2777  // Something changes
2778
2779  cmt_string here = CmtSystem::pwd ();
2780  CmtSystem::cd (other);
2781  cmt_string p = CmtSystem::pwd ();
2782  CmtSystem::cd (here);
2783
2784  if (p == p_name)
2785    {
2786      // The physical name does not change => we are just adding a new logical
2787
2788      if (l_name == "")
2789        {
2790          // the logical name was not set => set it
2791          l_name = other;
2792        }
2793      else
2794        {
2795          // add a new logical name
2796          cmt_string& n = alternates.add ();
2797          n = other;
2798        }
2799    }
2800  else
2801    {
2802      // The physical names differ => we completely reset the object
2803
2804      p_name = p;
2805      l_name = other;
2806      alternates.resize (0);
2807    }
2808}
2809
2810//----------------------------------------------------------
2811void FilePath::set (const char* other)
2812{
2813  const cmt_string o = other;
2814  set (o);
2815}
2816
2817//----------------------------------------------------------
2818const cmt_string& FilePath::name () const
2819{
2820  if (l_name != "") return (l_name);
2821  else return (p_name);
2822}
2823
2824//----------------------------------------------------------
2825FilePath::operator const cmt_string& () const
2826{
2827  if (l_name != "") return (l_name);
2828  else return (p_name);
2829}
2830
2831//----------------------------------------------------------
2832bool FilePath::in (const FilePath& other) const
2833{
2834  const cmt_string& o = other.name ();
2835
2836  return (in (o));
2837
2838  return (false);
2839}
2840
2841//----------------------------------------------------------
2842bool FilePath::in (const cmt_string& other) const
2843{
2844  if (p_name.find (other) == 0) return (true);
2845  if (l_name.find (other) == 0) return (true);
2846
2847  for (int i = 0; i < alternates.size (); i++)
2848    {
2849      const cmt_string& a = alternates[i];
2850      if (a.find (other) == 0) return (true);
2851    }
2852
2853  return (false);
2854}
2855
2856//----------------------------------------------------------
2857bool FilePath::in (const char* other) const
2858{
2859  const cmt_string o = other;
2860
2861  return (in (o));
2862}
2863
2864
Note: See TracBrowser for help on using the repository browser.