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

Last change on this file since 561 was 561, checked in by rybkin, 13 years ago

See C.L. 444

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