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

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

See C.L. 425

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