source: CMT/v1r20p20090520/source/cmt_system.cxx

Last change on this file was 497, checked in by rybkin, 15 years ago

See C.L. 392

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