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

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

See C.L. 414

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