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

Last change on this file since 533 was 533, checked in by rybkin, 14 years ago

See C.L. 420

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