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

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

See C.L. 422

  • Property svn:eol-style set to native
File size: 56.7 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, 0);
1416        //      m_project = factory.create_project (project_name, npath, path_source, m_project);
1417        /*
1418        if ((path_source == "CMTUSERCONTEXT") ||
1419            (path_source == "CMTHOME"))
1420          {
1421            m_project = 0;
1422          }
1423        */
1424      }
1425  }
1426
1427  Project* m_project;
1428};
1429
1430CMTPathManager& CMTPathManager::instance ()
1431{
1432  static CMTPathManager me;
1433 
1434  return (me);
1435}
1436
1437void CMTPathManager::reset ()
1438{
1439  static CMTPathManager& me = instance ();
1440  me.do_reset ();
1441}
1442
1443void CMTPathManager::add_cmt_path (const cmt_string& path,
1444                                   const cmt_string& path_source,
1445                                   IProjectFactory& factory)
1446{
1447  static CMTPathManager& me = instance ();
1448  me.do_add_cmt_path (path, path_source, factory);
1449}
1450
1451
1452
1453//----------------------------------------------------------
1454static void add_cmt_paths_from_text (const cmt_string& text,
1455                                     const cmt_string& context,
1456                                     IProjectFactory& factory)
1457{
1458  static CmtSystem::cmt_string_vector path_vector;
1459  int i;
1460
1461  CmtSystem::split (text, CmtSystem::path_separator (), path_vector);
1462
1463  for (i = 0; i < path_vector.size (); i++)
1464    {
1465      const cmt_string& path = path_vector[i];
1466
1467      CMTPathManager::add_cmt_path (path, context, factory);
1468    }
1469}
1470
1471//----------------------------------------------------------
1472static void add_cmt_paths_from_file (const cmt_string& file_name, IProjectFactory& factory)
1473{
1474  if (!CmtSystem::test_file (file_name)) return;
1475
1476  static cmt_string text;
1477
1478  text.read (file_name);
1479
1480  int pos = text.find ("CMTPATH");
1481  if (pos == cmt_string::npos) return;
1482  pos += strlen ("CMTPATH");
1483  pos = text.find (pos, "=");
1484  if (pos == cmt_string::npos) return;
1485  pos++;
1486
1487  text.erase (0, pos);
1488
1489  int nl = text.find (pos, "\n");
1490  if (nl != cmt_string::npos) text.erase (nl);
1491
1492  add_cmt_paths_from_text (text, file_name, factory);
1493}
1494
1495//----------------------------------------------------------
1496//
1497// With this function we analyse all possible ways of
1498// externally entering CMTPATH items
1499//  + from the environment variable
1500//  + from .cmtrc files
1501//  + from registry on Windows
1502//  + from EV settings for CMTUSERCONTEXT and CMTHOME
1503//
1504// Then projects are created from these settings.
1505//
1506// (The other way to enter project graph is through project files)
1507//----------------------------------------------------------
1508void CmtSystem::get_cmt_paths (IProjectFactory& factory, 
1509                               const cmt_string& init_text,
1510                               const cmt_string& cmt_user_context,
1511                               const cmt_string& cmt_home)
1512{
1513  CMTPathManager::reset ();
1514
1515  CMTPathManager::add_cmt_path (cmt_user_context, "CMTUSERCONTEXT", factory);
1516  CMTPathManager::add_cmt_path (cmt_home, "CMTHOME", factory);
1517  /*
1518  if (init_text != "")
1519    {
1520      add_cmt_paths_from_text (init_text, "initialization", factory);
1521    }
1522  */
1523
1524#ifdef WIN32
1525  LONG status;
1526  HKEY key = 0;
1527
1528  status = RegOpenKeyEx (HKEY_CURRENT_USER, "Software\\CMT\\path",
1529                         0, KEY_READ, &key);
1530  if (status == ERROR_SUCCESS)
1531    {
1532      DWORD index = 0;
1533      char name[256];
1534      char temp[256];
1535
1536      for (;;)
1537        {
1538          DWORD name_length = sizeof (name) - 1;
1539          DWORD length = sizeof (temp) - 1;
1540          DWORD type;
1541          status = RegEnumValue (key, index,
1542                                 name, &name_length, 0, &type,
1543                                 (LPBYTE) temp, &length);
1544          if ((status == ERROR_SUCCESS) ||
1545              (status == 234))
1546            {
1547              const cmt_string path = temp;
1548              CMTPathManager::add_cmt_path (path, "HKEY_CURRENT_USER", factory);
1549            }
1550
1551          if (status == 259)
1552            {
1553              break;
1554            }
1555
1556          index++;
1557        }
1558    }
1559
1560  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT\\path",
1561                         0, KEY_READ, &key);
1562  if (status == ERROR_SUCCESS)
1563    {
1564      DWORD index = 0;
1565      char name[256];
1566      char temp[256];
1567
1568      for (;;)
1569        {
1570          DWORD type;
1571          DWORD name_length = sizeof (name) - 1;
1572          DWORD length = sizeof (temp) - 1;
1573
1574
1575          status = RegEnumValue (key, index,
1576                                 name, &name_length, 0, &type,
1577                                 (LPBYTE) temp, &length);
1578          if (status != ERROR_NO_MORE_ITEMS)
1579            {
1580              const cmt_string path = temp;
1581              CMTPathManager::add_cmt_path (path, "HKEY_LOCAL_MACHINE", factory);
1582            }
1583          else
1584            {
1585              break;
1586            }
1587          index++;
1588        }
1589    }
1590
1591#endif
1592
1593  //-----------------------------------------
1594  // look for .cmtrc files :
1595  //  first look in ./
1596  //  then in "~/"
1597  //  then in ${CMTROOT}/mgr
1598  //-----------------------------------------
1599  cmt_string rc_name;
1600
1601  add_cmt_paths_from_file (".cmtrc", factory);
1602
1603  if (get_home_directory (rc_name))
1604    {
1605      rc_name += file_separator ();
1606      rc_name += ".cmtrc";
1607      add_cmt_paths_from_file (rc_name, factory);
1608    }
1609
1610  rc_name = get_cmt_root ();
1611  rc_name += file_separator ();
1612  rc_name += "CMT";
1613  rc_name += file_separator ();
1614  cmt_string version;
1615  get_cmt_version (version);
1616  rc_name += version;
1617  rc_name += file_separator ();
1618  rc_name += "mgr";
1619  rc_name += file_separator ();
1620  rc_name += ".cmtrc";
1621
1622  add_cmt_paths_from_file (rc_name, factory);
1623
1624  /*
1625  CMTPathManager::add_cmt_path (cmt_user_context, "CMTUSERCONTEXT", factory);
1626  CMTPathManager::add_cmt_path (cmt_home, "CMTHOME", factory);
1627  */
1628  if (init_text != "")
1629    {
1630      add_cmt_paths_from_text (init_text, "initialization", factory);
1631    }
1632}
1633
1634//----------------------------------------------------------
1635int CmtSystem::execute (const cmt_string& command)
1636{
1637  //cerr << "CmtSystem::execute1> [" << command << "]" << endl;
1638  int status = system (command.c_str ());
1639  if (status == -1) // failed
1640    return -1;
1641  else
1642    return WEXITSTATUS(status); // return status of the command
1643  //  return (system (command.c_str ()));
1644}
1645
1646//----------------------------------------------------------
1647int CmtSystem::execute (const cmt_string& command, cmt_string& output)
1648{
1649  output = "";
1650
1651  //  cerr << "CmtSystem::execute2> [" << command << "]" << endl;
1652
1653  FILE* f = popen (command.c_str (), "r"); 
1654 
1655  if (f != 0) 
1656    { 
1657      char line[PATH_MAX]; 
1658      //      char line[256];
1659      char* ptr;
1660
1661      while ((ptr = fgets (line, sizeof (line), f)) != NULL) 
1662        {
1663          output += ptr;
1664        } 
1665
1666      int status = pclose (f);
1667      if (status == -1) // error reported by pclose ()
1668        {
1669          perror ("pclose");
1670          if (errno == ECHILD)
1671            {
1672              return (0);
1673            }
1674          return (-1);
1675        }
1676      else
1677        {
1678          return WEXITSTATUS(status); // return status of the command
1679          //return (0);
1680        }
1681    }
1682
1683  return (-2);
1684}
1685
1686//----------------------------------------------------------
1687bool CmtSystem::is_package_directory (const cmt_string& name)
1688{
1689  cmt_string_vector dirs;
1690
1691  cmt_regexp exp ("^[a-zA-Z.][0-9]+([a-zA-Z.][0-9]+([a-zA-Z.][0-9]+)?)?");
1692
1693  scan_dir (name, exp, dirs);
1694
1695  cmt_string req;
1696
1697  req = name;
1698  req += file_separator ();
1699  req += "cmt";
1700  req += file_separator ();
1701  req += "requirements";
1702
1703  if (test_file (req)) return (true);
1704
1705  if (dirs.size () == 0) 
1706    {
1707      return (false);
1708    }
1709
1710  for (int i = 0; i < dirs.size (); i++)
1711    {
1712      const cmt_string& d = dirs[i];
1713
1714      req = d;
1715      req += file_separator ();
1716      req += "mgr";
1717      req += file_separator ();
1718      req += "requirements";
1719
1720      if (test_file (req)) return (true);
1721
1722      req = d;
1723      req += file_separator ();
1724      req += "cmt";
1725      req += file_separator ();
1726      req += "requirements";
1727     
1728      if (test_file (req)) return (true);
1729    }
1730
1731  return (false);
1732}
1733
1734//----------------------------------------------------------
1735bool CmtSystem::is_version_directory (const cmt_string& name)
1736{
1737  int v;
1738  int r;
1739  int p;
1740
1741  return (is_version_directory (name, v, r, p));
1742}
1743
1744//----------------------------------------------------------
1745bool CmtSystem::is_version_directory (const cmt_string& name,
1746                                      int& v,
1747                                      int& r,
1748                                      int& p)
1749{
1750  if ((name == "HEAD") || (name == "head"))
1751    {
1752      v = 0;
1753      r = 0;
1754      p = 0;
1755
1756      return (true);
1757    }
1758
1759  static const cmt_string numbers = "0123456789";
1760
1761  static const int id_version = 0;
1762  static const int id_release = 1;
1763  static const int id_patch   = 2;
1764
1765  cmt_string buffer;
1766
1767  enum 
1768    {
1769      starting,
1770      at_key,
1771      at_number
1772    } state;
1773
1774  int id;
1775  int pos;
1776  int value;
1777
1778  v = 0;
1779  r = 0;
1780  p = 0;
1781
1782  //
1783  // version : v-field
1784  //         | v-field r-field
1785  //         | v-field r-field p-field
1786  //
1787  // v-field : field
1788  // r-field : field
1789  // p-field : field
1790  //
1791  // field   : key '*'
1792  //         | key number
1793  //
1794  // key     : letters
1795  //
1796
1797  state = starting;
1798  id    = id_version;
1799
1800  for (pos = 0; pos < name.size (); pos++)
1801    {
1802      char c = name[pos];
1803
1804      if (c == '*')
1805        {
1806          // A wild card
1807          switch (state)
1808            {
1809            case starting:
1810              // cannot start with a wild card ??
1811              v = -1;
1812              r = -1;
1813              p = -1;
1814              return (false);
1815            case at_key:
1816              // the numeric field is valued with a wild card
1817              switch (id)
1818                {
1819                case id_version:
1820                  v = -1;
1821                case id_release:
1822                  r = -1;
1823                case id_patch:
1824                  p = -1;
1825                  break;
1826                }
1827              return (true);
1828            case at_number:
1829              // question:
1830              // a number followed by a wild-card is considered as:
1831              //   1) a wild card on the number itself (1* comp with 1, 10, 12, 120, etc)
1832              //   2) a wild card on the next fields (1* comp with 1r1, 1-12 etc)
1833              //
1834
1835              //  Here we select option 1)
1836
1837              sscanf (buffer.c_str (), "%d", &value);
1838              switch (id)
1839                {
1840                case id_version:
1841                  //
1842                  // lazy option 1 implies v = -1;
1843                  // strict option 1 would imply v = -value;
1844                  // option 2 implies v = value;
1845                  //
1846
1847                  v = -1;
1848                  r = -1;
1849                  p = -1;
1850                  break;
1851                case id_release:
1852                  r = value;
1853                  p = -1;
1854                  break;
1855                case id_patch:
1856                  p = value;
1857                  break;
1858                }
1859
1860              return (true);
1861            }
1862        }
1863      else if (numbers.find (c) == cmt_string::npos)
1864        {
1865          // A letter
1866          switch (state)
1867            {
1868            case starting:
1869              state = at_key;
1870              break;
1871            case at_key:
1872              // Multiple letter key (is it permitted??)
1873              break;
1874            case at_number:
1875              sscanf (buffer.c_str (), "%d", &value);
1876              switch (id)
1877                {
1878                case id_version:
1879                  v = value;
1880                  break;
1881                case id_release:
1882                  r = value;
1883                  break;
1884                case id_patch:
1885                  p = value;
1886                  break;
1887                }
1888              buffer = "";
1889              id++;
1890              state = at_key;
1891              break;
1892            }
1893        }
1894      else
1895        {
1896          // a number
1897          switch (state)
1898            {
1899            case starting:
1900              // not starting by a letter (syntax error)
1901              //return (false);
1902            case at_key:
1903              // the numeric field for the current id is starting now
1904              buffer += c;
1905              state = at_number;
1906              break;
1907            case at_number:
1908              // continuing the current numeric field
1909              buffer += c;
1910              break;
1911            }
1912        }
1913    }
1914
1915  switch (state)
1916    {
1917    case starting:
1918      // Empty version string
1919      return (false);
1920    case at_key:
1921      // Syntax error (when only letters. Ending letters is not an error)
1922      if (id == id_version) return (false);
1923      else return (true);
1924    case at_number:
1925      sscanf (buffer.c_str (), "%d", &value);
1926      switch (id)
1927        {
1928        case id_version:
1929          v = value;
1930          break;
1931        case id_release:
1932          r = value;
1933          break;
1934        case id_patch:
1935          p = value;
1936          break;
1937        }
1938      id++;
1939      state = at_key;
1940      return (true);
1941    }
1942
1943  return (false);
1944}
1945
1946//----------------------------------------------------------
1947//  Split a line into words. Separators are spaces and tabs
1948//  Text enclosed in double quotes is one word.
1949//----------------------------------------------------------
1950void CmtSystem::split (const cmt_string& text,
1951                       const cmt_string& separators,
1952                       cmt_string_vector& strings)
1953{
1954  static char* buffer = 0;
1955  static int allocated = 0;
1956
1957  bool finished = false;
1958
1959  strings.clear ();
1960
1961  if (text.size () == 0) return;
1962
1963  /*
1964    We are going to work in a copy of the text, since
1965    \0 will be inserted right after each found word.
1966
1967    Then the vector of strings is iteratively filled by each found word.
1968  */
1969
1970  if (buffer == 0)
1971    {
1972      allocated = text.size ();
1973      buffer = (char*) malloc (allocated + 1);
1974    }
1975  else
1976    {
1977      if (text.size () > allocated)
1978        {
1979          allocated = text.size ();
1980          buffer = (char*) realloc (buffer, allocated + 1);
1981        }
1982    }
1983
1984  strcpy (buffer, text.c_str ());
1985
1986  /*
1987    Algorithm :
1988
1989    We look for words separated by <separators> which may be
1990    o spaces (' ' or '\t')
1991    o other characters such as ':'
1992
1993    A word is a character string not containing any separator. A substring in
1994    this word my be enclosed between quotes (" or ') which permits separator
1995    inclusion within words.
1996  */
1997
1998  char* current_word = buffer;
1999
2000  while (*current_word != 0)
2001    {
2002      size_t prefix_length;
2003      size_t word_length;
2004
2005      /*
2006        while ((*current_word == ' ') ||
2007        (*current_word == '\t'))
2008        {
2009        current_word++;
2010        }
2011      */
2012
2013      // first skip all starting separators.
2014
2015      prefix_length = strspn (current_word, separators.c_str ());
2016      if (prefix_length > 0)
2017        {
2018          // Move to the first non-separator character
2019
2020          current_word += prefix_length;
2021        }
2022
2023      /*
2024        Parse the next word.
2025
2026        It may contain enclosures in quote characters or not.
2027        Quotes must be identical on both sides of each enclosure.
2028      */
2029
2030      char* running_char = current_word;
2031
2032      word_length = 0;
2033
2034      for (;;)
2035        {
2036          size_t unquoted_length;
2037          size_t separator_offset;
2038
2039          for (int p = 0;;)
2040            {
2041              unquoted_length = strcspn (running_char + p, "\"\'") + p;
2042              if ((unquoted_length > 0) && (running_char[unquoted_length-1] == '\\'))
2043                {
2044                  p = unquoted_length + 1;
2045                }
2046              else
2047                {
2048                  break;
2049                }
2050            }
2051
2052          separator_offset = strcspn (running_char, separators.c_str ());
2053
2054          if (separator_offset <= unquoted_length)
2055            {
2056              // no quote in this word -> we are finished for this one.
2057              running_char += separator_offset;
2058              break;
2059            }
2060
2061          // We have found a quoted enclosure. Move to it.
2062
2063          running_char += unquoted_length;
2064
2065          char quote = running_char[0];
2066
2067          // Remove it.
2068          {
2069            char* p = running_char;
2070            while (p[1] != 0)
2071              {
2072                *p = p[1];
2073                p++;
2074              }
2075            *p = 0;
2076          }
2077
2078          // Look for the next occurence of this quote.
2079          {
2080            char* p = strchr (running_char, quote);
2081            if (p == 0)
2082              {
2083                // Unmatched quote : the rest of the line will be taken as a word...
2084                running_char += strlen (running_char);
2085                finished = true;
2086                break;
2087              }
2088            else
2089              {
2090                running_char = p;
2091              }
2092          }
2093
2094          // Now we remove the ending quote from the word
2095          // (by shifting all remaining characters by one place to the left)
2096
2097          {
2098            char* p = running_char;
2099            while (p[1] != 0)
2100              {
2101                *p = p[1];
2102                p++;
2103              }
2104            *p = 0;
2105          }
2106        }
2107
2108      word_length = running_char - current_word;
2109
2110      if (current_word[word_length] == 0)
2111        {
2112          finished = true;
2113        }
2114      else
2115        {
2116          current_word[word_length] = 0;
2117        }
2118
2119      /*
2120        if ((t[0] == '"') ||
2121        (t[0] == '\'') ||
2122        (t[0] == ':'))
2123        {
2124        char* quote;
2125
2126        t++;
2127        quote = strchr (t, sep);
2128        if (quote != 0) *quote = 0;
2129        else finished = true;
2130        }
2131        else
2132        {
2133        int offset;
2134
2135        offset = strcspn (t, " \t:");
2136        if ((offset < 0) || (t[offset] == 0)) finished = true;
2137        if (!finished)
2138        {
2139        space = t + offset;
2140        *space = 0;
2141        }
2142        }
2143      */
2144
2145      // Store the current word into the vector of strings
2146
2147      {
2148        cmt_string& s = strings.add ();
2149        s = current_word;
2150      }
2151
2152      if (finished) break;
2153
2154      // Move to the next possible word.
2155      current_word += word_length + 1;
2156    }
2157}
2158
2159//----------------------------------------------------------
2160void CmtSystem::compress_path (const cmt_string& dir, cmt_string& new_dir)
2161{
2162  new_dir = dir;
2163
2164  compress_path (new_dir);
2165}
2166
2167//----------------------------------------------------------
2168//
2169//  We try to detect the aaaa/xxxx/../bbbb patterns which should be
2170// equivalent to aaaa/bbbb
2171//  this therefore consists in removing all /xxxx/../ when
2172//     xxxx is different from ".."
2173//     xxxx is different from "."
2174//     xxxx does not contain any macro reference
2175//
2176// Also replace "/.." with "/". One cannot walk down past the root.
2177//----------------------------------------------------------
2178void CmtSystem::compress_path (cmt_string& dir)
2179{
2180#ifdef WIN32
2181  static const char pattern[] = "\\..";
2182  static const char fs[] = "\\\\";
2183#else
2184  static const char pattern[] = "/..";
2185  static const char fs[] = "//";
2186#endif
2187
2188  if (dir.size () == 0) return;
2189
2190  //
2191  // We first synchronize to using file_separator() in any case.
2192  //
2193
2194  if (file_separator () == '/')
2195    {
2196      dir.replace_all ("\\", file_separator ());
2197    }
2198  else
2199    {
2200      dir.replace_all ("/", file_separator ());
2201    }
2202
2203  // Suppress all duplicated file separators
2204  dir.replace_all (fs, file_separator ());
2205
2206  for (;;)
2207    {
2208      int pos0 (0);
2209      int pos1;
2210      int pos2;
2211      int pos3;
2212
2213      //pos1 = dir.find (pattern);
2214      //if (pos1 == cmt_string::npos) break;
2215
2216      do
2217        {
2218          pos1 = dir.find (pos0, pattern);
2219          if (pos1 == cmt_string::npos) break;
2220          pos0 = pos1 + 3;
2221        }
2222      while (pos0 < dir.size () && dir[pos0] != file_separator ());
2223
2224      if (pos1 == cmt_string::npos) break;
2225
2226      //
2227      // One cannot walk down past the root: "/.." is the same as "/".
2228      //
2229#ifdef WIN32
2230      if (pos1 == 0)
2231        {
2232          dir.erase (pos1, 3);
2233          if (dir == "")
2234            dir = file_separator ();
2235          continue;
2236        }
2237      else if (pos1 == 2 && dir[1] == ':')
2238        {
2239          dir.erase (pos1, 3);
2240          if (dir.size () == 2)
2241            dir += file_separator ();
2242          continue;
2243        }
2244#else
2245      if (pos1 == 0)
2246        {
2247          dir.erase (pos1, 3);
2248          if (dir == "")
2249            dir = file_separator ();
2250          continue;
2251        }
2252#endif
2253
2254      //
2255      // extract "aaaa/xxxx" from "aaaa/xxxx/../bbbb"
2256      //
2257      cmt_string p = dir.substr (0, pos1);
2258
2259      cmt_string dn;
2260      basename (p, dn);
2261      if (dn == "..") break;
2262      if (dn == ".") break;
2263      if (dn == "") break;
2264     
2265      //
2266      // Is "aaaa/xxxx" only made of "xxxx" ?
2267      //
2268      pos2 = p.find_last_of (file_separator ());
2269     
2270      if (pos2 == cmt_string::npos) 
2271        {
2272          // the pattern was xxxx/../bbbb
2273          //
2274          // so xxxx is [0:pos1-1]
2275          //
2276          // erase the "xxxx/.." pattern
2277          // result will be "/bbbb"
2278          // so, need to process a little more
2279          //
2280          pos3 = p.find ("$");
2281          if (pos3 == cmt_string::npos)
2282            {
2283              dir.erase (0, pos1 + 3);
2284              if (dir.size () < 2)
2285                {
2286                  dir = ".";
2287                }
2288              else
2289                {
2290                  dir.erase (0, 1);
2291                }
2292            }
2293          else
2294            {
2295              break;
2296            }
2297        }
2298      else
2299        {
2300          //    01234567890123456
2301          //    aaaa/xxxx/../bbbb
2302          //        2    1   3
2303          //
2304          // erase the "/xxxx/.." pattern
2305          // result will be "aaaa/bbbb"
2306          //
2307          // Here xxxx is [pos2+1:pos1-1]
2308          //
2309
2310          pos3 = p.find (pos2, "$");
2311          if (pos3 == cmt_string::npos)
2312            {
2313              dir.erase (pos2, pos1 + 3 - pos2);
2314#ifdef WIN32
2315              if (dir == "")
2316                {
2317                  dir = file_separator ();
2318                }
2319              else if (dir.size () == 2 && dir[1] == ':')
2320                {
2321                  dir += file_separator ();
2322                }
2323#else
2324              if (dir == "")
2325                dir = file_separator ();
2326#endif
2327            }
2328          else
2329            {
2330              break;
2331            }
2332        }
2333    }
2334
2335  //if (dir[dir.size () - 1] == file_separator ()) dir.erase (dir.size () - 1);
2336}
2337
2338//----------------------------------------------------------
2339cmt_string CmtSystem::now ()
2340{
2341  cmt_string result;
2342
2343  time_t ltime;
2344  time (&ltime);
2345  result = ctime (&ltime);
2346
2347  result.replace_all ("\n", "");
2348
2349  return (result);
2350}
2351
2352//----------------------------------------------------------
2353cmt_string CmtSystem::user ()
2354{
2355#ifdef _WIN32
2356  cmt_string result = getenv ("USERNAME");
2357#else
2358  cmt_string result = getenv ("USER");
2359#endif
2360
2361  return (result);
2362}
2363
2364//----------------------------------------------------------
2365void CmtSystem::get_cvsroot (cmt_string& cvsroot)
2366{
2367  cvsroot = "";
2368
2369  const char* env = ::getenv ("CVSROOT");
2370  if (env != 0)
2371    {
2372      cvsroot = env;
2373      return;
2374    }
2375
2376#ifdef WIN32
2377  LONG status;
2378  HKEY key = 0;
2379
2380  status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\CMT", 
2381                         0, KEY_READ, &key);
2382  if (status == ERROR_SUCCESS)
2383    {
2384      char temp[256];
2385      DWORD length = sizeof (temp) - 1;
2386      DWORD type;
2387
2388      status = RegQueryValueEx (key, "CVSROOT", 0, &type, 
2389                                (LPBYTE) temp, &length);
2390      if (status == ERROR_SUCCESS)
2391        {
2392          cvsroot = temp;
2393          return;
2394        }
2395    }
2396#endif
2397}
2398
2399//----------------------------------------------------------
2400bool CmtSystem::get_home_directory (cmt_string& dir)
2401{
2402  bool status = false;
2403
2404#ifdef WIN32
2405  const char* homedrive = ::getenv ("HOMEDRIVE");
2406  const char* homepath = ::getenv ("HOMEPATH");
2407
2408  if ((homedrive != 0) && (homepath != 0))
2409    {
2410      dir = homedrive;
2411      dir += homepath;
2412      status = true;
2413    }
2414
2415#else
2416  const char* home_env = ::getenv ("HOME");
2417  if (home_env != 0)
2418    {
2419      dir = home_env;
2420      status = true;
2421    }
2422#endif
2423
2424  return (status);
2425}
2426
2427//----------------------------------------------------------
2428cmt_string CmtSystem::get_makefile_suffix ()
2429{
2430#ifdef WIN32
2431  return "nmake";
2432#else
2433  return "make";
2434#endif
2435}
2436
2437//----------------------------------------------------------
2438void CmtSystem::close_ostream (FILE *stream, const cmt_string& name)
2439{
2440  cmt_string msg ("Cannot write");
2441  cmt_string n = (name != "") ? " " + name : "";
2442  if (stream == NULL)
2443    {
2444      CmtMessage::error (msg + n);
2445      exit (EXIT_FAILURE);
2446    }
2447
2448  bool prev_fail = ferror (stream);
2449  bool fclose_fail = fclose (stream);
2450
2451  if (prev_fail || fclose_fail)
2452    {
2453      int err = fclose_fail ? errno : 0;
2454      cmt_string e = err ? ": " + cmt_string (strerror (err)) : "";
2455      CmtMessage::error (msg + n + e);
2456      exit (EXIT_FAILURE);
2457    }
2458}
2459
2460//----------------------------------------------------------
2461void CmtSystem::close_stdout (void)
2462{
2463  /*
2464   * The idea and, to some extent, implementation of this function
2465   * were borrowed from the GNU core utilities of the Free Software Foundation
2466   * http://www.gnu.org/software/coreutils/
2467   */
2468  close_ostream (stdout, "stdout");
2469}
2470
2471//----------------------------------------------------------
2472FilePath::FilePath ()
2473{
2474  p_name = "";
2475  l_name = "";
2476  alternates.resize (0);
2477}
2478
2479//----------------------------------------------------------
2480FilePath::FilePath (const FilePath& other)
2481{
2482  set (other);
2483}
2484
2485//----------------------------------------------------------
2486FilePath::FilePath (const cmt_string& other)
2487{
2488  set (other);
2489}
2490
2491//----------------------------------------------------------
2492FilePath::FilePath (const char* other)
2493{
2494  set (other);
2495}
2496
2497//----------------------------------------------------------
2498FilePath& FilePath::operator = (const FilePath& other)
2499{
2500  FilePath& me = *this;
2501
2502  me.set (other);
2503
2504  return (me);
2505}
2506
2507//----------------------------------------------------------
2508FilePath& FilePath::operator = (const cmt_string& other)
2509{
2510  FilePath& me = *this;
2511
2512  me.set (other);
2513
2514  return (me);
2515}
2516
2517//----------------------------------------------------------
2518FilePath& FilePath::operator = (const char* other)
2519{
2520  FilePath& me = *this;
2521
2522  me.set (other);
2523
2524  return (me);
2525}
2526
2527//----------------------------------------------------------
2528bool FilePath::operator == (const FilePath& other) const
2529{
2530  if (other.p_name == p_name) return (true);
2531  return (false);
2532}
2533
2534//----------------------------------------------------------
2535bool FilePath::operator == (const cmt_string& other) const
2536{
2537  if (p_name == other) return (true);
2538  if (l_name == other) return (true);
2539
2540  for (int i = 0; i < alternates.size (); i++)
2541    {
2542      if (alternates[i] == other) return (true);
2543    }
2544
2545  cmt_string here = CmtSystem::pwd ();
2546  CmtSystem::cd (other);
2547  cmt_string p = CmtSystem::pwd ();
2548  CmtSystem::cd (here);
2549
2550  if (p_name == p) return (true);
2551
2552  return (false);
2553}
2554
2555//----------------------------------------------------------
2556bool FilePath::operator == (const char* other) const
2557{
2558  const FilePath& me = *this;
2559
2560  const cmt_string o = other;
2561
2562  return ((me == o));
2563
2564  return (false);
2565}
2566
2567//----------------------------------------------------------
2568bool FilePath::operator != (const FilePath& other) const
2569{
2570  const FilePath& me = *this;
2571
2572  return (!(me == other));
2573}
2574
2575//----------------------------------------------------------
2576bool FilePath::operator != (const cmt_string& other) const
2577{
2578  const FilePath& me = *this;
2579
2580  return (!(me == other));
2581}
2582
2583//----------------------------------------------------------
2584bool FilePath::operator != (const char* other) const
2585{
2586  const FilePath& me = *this;
2587
2588  return (!(me == other));
2589}
2590
2591//----------------------------------------------------------
2592bool FilePath::cd () const
2593{
2594  CmtSystem::cd (l_name);
2595
2596  return (false);
2597}
2598
2599//----------------------------------------------------------
2600void FilePath::set (const FilePath& other)
2601{
2602  p_name = other.p_name;
2603  l_name = other.l_name;
2604  alternates = other.alternates;
2605}
2606
2607//----------------------------------------------------------
2608void FilePath::set (const cmt_string& other)
2609{
2610  // Skip if no change.
2611
2612  if (p_name == other) return;
2613  if (l_name == other) return;
2614
2615  for (int i = 0; i < alternates.size (); i++)
2616    {
2617      if (alternates[i] == other) return;
2618    }
2619
2620  // Something changes
2621
2622  cmt_string here = CmtSystem::pwd ();
2623  CmtSystem::cd (other);
2624  cmt_string p = CmtSystem::pwd ();
2625  CmtSystem::cd (here);
2626
2627  if (p == p_name)
2628    {
2629      // The physical name does not change => we are just adding a new logical
2630
2631      if (l_name == "")
2632        {
2633          // the logical name was not set => set it
2634          l_name = other;
2635        }
2636      else
2637        {
2638          // add a new logical name
2639          cmt_string& n = alternates.add ();
2640          n = other;
2641        }
2642    }
2643  else
2644    {
2645      // The physical names differ => we completely reset the object
2646
2647      p_name = p;
2648      l_name = other;
2649      alternates.resize (0);
2650    }
2651}
2652
2653//----------------------------------------------------------
2654void FilePath::set (const char* other)
2655{
2656  const cmt_string o = other;
2657  set (o);
2658}
2659
2660//----------------------------------------------------------
2661const cmt_string& FilePath::name () const
2662{
2663  if (l_name != "") return (l_name);
2664  else return (p_name);
2665}
2666
2667//----------------------------------------------------------
2668FilePath::operator const cmt_string& () const
2669{
2670  if (l_name != "") return (l_name);
2671  else return (p_name);
2672}
2673
2674//----------------------------------------------------------
2675bool FilePath::in (const FilePath& other) const
2676{
2677  const cmt_string& o = other.name ();
2678
2679  return (in (o));
2680
2681  return (false);
2682}
2683
2684//----------------------------------------------------------
2685bool FilePath::in (const cmt_string& other) const
2686{
2687  if (p_name.find (other) == 0) return (true);
2688  if (l_name.find (other) == 0) return (true);
2689
2690  for (int i = 0; i < alternates.size (); i++)
2691    {
2692      const cmt_string& a = alternates[i];
2693      if (a.find (other) == 0) return (true);
2694    }
2695
2696  return (false);
2697}
2698
2699//----------------------------------------------------------
2700bool FilePath::in (const char* other) const
2701{
2702  const cmt_string o = other;
2703
2704  return (in (o));
2705}
2706
2707
Note: See TracBrowser for help on using the repository browser.