source: CMT/v1r18p20051101/source/cmt_cvs.cxx

Last change on this file was 101, checked in by arnault, 19 years ago

Fix back compatibility for cvs pluggin protocol see CL 287

  • Property svn:eol-style set to native
File size: 59.0 KB
Line 
1//-----------------------------------------------------------
2// Copyright Christian Arnault LAL-Orsay CNRS
3// arnault@lal.in2p3.fr
4// See the complete license in cmt_license.txt "http://www.cecill.info".
5//-----------------------------------------------------------
6
7#include <stdlib.h>
8#include <stdio.h>
9
10#include "cmt.h"
11#include "cmt_cvs.h"
12#include "cmt_awk.h"
13#include "cmt_symbol.h"
14#include "cmt_project.h"
15
16/**
17
18    Grep : perform a grep like operation onto a cmt_string
19
20    o All lines of the input string are collected when they contain
21      the specified pattern.
22    o The input string and the selector pattern are specified in
23      the constructor:
24
25         Grep (input_string, pattern)
26
27    o All selected lines are accumulated (appended) into the internal
28      variable m_result . 'space' is the separator.
29
30    o The accumulator is retrieved by the result () method.
31
32 */
33class Grep : public Awk
34{
35public:
36
37  void begin ();
38  void filter (const cmt_string& line);
39  const cmt_string& result () const;
40
41private:
42  cmt_string m_result;
43};
44
45/**
46
47     Cut : perform a cut-like operation :
48
49     o collect the <field>'th field of every line into the m_result
50       internal variable
51
52     o the field number is given in the constructor and starts at zero.
53
54     o selected fields are accumulated with a space as separator.
55
56 */
57class Cut : public Awk
58{
59public:
60  Cut (int field);
61  void begin ();
62  void filter (const cmt_string& line);
63  const cmt_string& result () const;
64
65private:
66  cmt_string m_result;
67  int m_field;
68};
69
70/**
71
72      History : maintains the history of checkouts during a
73                recursive checkout, so as to avoid double checkouts.
74
75 */
76class History
77{
78public:
79  static History& instance ();
80  void clear ();
81  void install (const cmt_string& line);
82  bool is_installed (const cmt_string& line);
83
84private:
85  History ();
86
87  cmt_string m_installed;
88};
89
90/**
91
92     RecursivePass1 : simply validate use statements in a requirements file
93                      and echo those that really need to be handled.
94
95 */
96class RecursivePass1 : public Awk
97{
98public:
99
100  void begin ();
101  void filter (const cmt_string& line);
102  const cmt_string& result () const;
103
104private:
105  cmt_string m_result;
106  bool m_first;
107};
108
109/**
110
111     RecursivePass2 : after filtering the use statements really perform the
112                      checkouts.
113
114 */
115class CvsImplementation;
116class RecursivePass2 : public Awk
117{
118public:
119  RecursivePass2 (CvsImplementation& cvs);
120  void begin ();
121  void filter (const cmt_string& line);
122
123private:
124  CvsImplementation& m_cvs;
125};
126
127/**
128
129     RecursivePass3 : simply validate use statements in a project file
130                      and echo those that really need to be handled.
131
132 */
133class RecursivePass3 : public Awk
134{
135public:
136
137  void begin ();
138  void filter (const cmt_string& line);
139  const cmt_string& result () const;
140
141private:
142  cmt_string m_result;
143  bool m_first;
144};
145
146/**
147
148     RecursivePass4 : after filtering the use statements really perform the
149                      project checkouts.
150
151 */
152class CvsImplementation;
153class RecursivePass4 : public Awk
154{
155public:
156  RecursivePass4 (CvsImplementation& cvs);
157  void begin ();
158  void filter (const cmt_string& line);
159
160private:
161  CvsImplementation& m_cvs;
162};
163
164/**
165
166   Internal implementation of CVS to CMT operations.
167   The Cvs class only provides abstract interface.
168
169 */
170class CvsImplementation
171{
172public:
173
174  CvsImplementation ()
175  {
176    clear ();
177  }
178
179  void clear ()
180  {
181    m_recursive  = false;
182    m_head       = false;
183    m_verbose    = false;
184    m_simulation = false;
185
186    m_home_dir     = "";
187    m_checkout_dir = "";
188    m_version_dir  = "";
189    m_cvs_offset   = "";
190
191    m_last_module        = "";
192    m_last_cvs_infos     = "";
193    structure_info       = "";
194    error_info           = "";
195    tags_top_info        = "";
196    tags_info            = "";
197    cvsversions_top_info = "";
198    cvsversions_info     = "";
199    branches_info        = "";
200    subpackages_info     = "";
201    subprojects_info     = "";
202
203    m_protocol_level     = "";
204
205    Symbol* symbol = Symbol::find ("cmt_cvs_protocol_level");
206    if (symbol != 0)
207      {
208        m_protocol_level = symbol->build_macro_value ();
209        Symbol::expand (m_protocol_level);
210      }
211  }
212
213  CvsImplementation& operator = (const CvsImplementation& other)
214  {
215    m_recursive  = other.m_recursive;
216    m_head       = other.m_head;
217    m_verbose    = other.m_verbose;
218    m_simulation = other.m_simulation;
219
220    m_home_dir       = other.m_home_dir;
221    m_checkout_dir   = other.m_checkout_dir;
222    m_version_dir    = other.m_version_dir;
223    m_cvs_offset     = other.m_cvs_offset;
224    m_protocol_level = other.m_protocol_level;
225    m_last_module    = other.m_last_module;
226    m_last_cvs_infos = other.m_last_cvs_infos;
227
228    structure_info       = other.structure_info;
229    error_info           = other.error_info;
230    tags_top_info        = other.tags_top_info;
231    tags_info            = other.tags_info;
232    cvsversions_top_info = other.cvsversions_top_info;
233    cvsversions_info     = other.cvsversions_info;
234    branches_info        = other.branches_info;
235    subpackages_info     = other.subpackages_info;
236    subprojects_info     = other.subprojects_info;
237
238    return (*this);
239  }
240
241  /**
242     Filter out the space-separated words of a text that don't match a regexp.
243   */
244  void filter_list (cmt_string& text, const cmt_regexp& exp)
245  {
246    CmtSystem::cmt_string_vector list;
247
248    CmtSystem::split (text, " ", list);
249
250    int i;
251
252    text = "";
253
254    for (i = 0; i < list.size (); i++)
255      {
256        const cmt_string& s = list[i];
257        if (exp.match (s))
258          {
259            if (i > 0) text += " ";
260            text += s;
261          }
262      }
263  }
264
265  int execute (const cmt_string& command)
266  {
267    int status = 0;
268
269    if (m_verbose || m_simulation)
270      {
271        cout << "#CMT> Executing [" << command << "]" << endl;
272      }
273   
274    if (!m_simulation)
275      {
276        status = CmtSystem::execute (command);
277      }
278
279    return (status);
280  }
281
282  void execute_and_retry (const cmt_string& command, const cmt_string& message)
283  {
284    int status;
285    int retry = 0;
286
287    for (;;)
288      {
289        status = execute (command);
290       
291        if (status != 0)
292          {
293            retry++;
294           
295            cout << "# " << message << ": status=" << status << endl;
296            cout << "#---------------------------------------------------------" << endl;
297           
298            if (retry > 5) exit(0);
299          }
300        else
301          {
302            break;
303          }
304      }
305  }
306
307  int execute (const cmt_string& command, cmt_string& out)
308  {
309    int status = 0;
310
311    if (m_verbose || m_simulation)
312      {
313        cout << "#CMT> Executing [" << command << "]" << endl;
314      }
315   
316    if (!m_simulation)
317      {
318        status = CmtSystem::execute (command, out);
319      }
320
321    return (status);
322  }
323
324
325  /**
326     This function will check the protocol level with the CVS pluggin.
327     The expected level is defined in a macro (in the requirements file of CMT)
328     The protocol level characterizes the structure of the message received from the CVS pluggin
329     and depends on its version.
330
331     In order to know if the pluggin is installed AND if the support for this version is
332     installed, we checkout CVSROOT/loginfo which should contain entries with selection patterns
333
334     .cmtcvsinfos/<protocol_level>
335
336     However old versions only offer as pattern:
337
338     .cmtcvsinfos
339
340     In this function we'll detect if the effective protocol level satisifies the
341     level expected for this release of CMT
342
343     In simulation mode we suppose the expected protocol has been found.
344   */
345  bool check_protocol ()
346  {
347    static bool done = false;
348    static bool found = true;
349
350    if (done) return (found);
351    done = true;
352
353    cmt_string cvsroot;
354       
355    CmtSystem::get_cvsroot (cvsroot);
356       
357    cmt_string command;
358       
359    command = "cvs";
360    if (cvsroot != "") 
361      {
362        command += " -d ";
363        command += cvsroot;
364      }
365    command += " -Q co -p CVSROOT/loginfo ";
366
367    found = false;
368
369    cmt_string pattern = ".cmtcvsinfos";
370
371    if (m_protocol_level != "")
372      {
373        pattern += "/";
374        pattern += m_protocol_level;
375      }
376
377    cmt_string loginfo;
378
379    if (m_simulation)
380      {
381        loginfo = pattern;
382      }
383
384    execute (command, loginfo);
385
386    int pos = loginfo.find (pattern);
387
388    if (pos != cmt_string::npos)
389      {
390        found = true;
391      }
392
393    if (m_verbose)
394      {
395        if (found)
396          {
397            cout << "#CMT> Protocol level " << m_protocol_level << endl;
398          }
399        else
400          {
401            cout << "#CMT> The CVS pluggin is not installed or is not at protocol level " << m_protocol_level << endl;
402          }
403      }
404   
405    return (found);
406  }
407
408  /**
409     Execute the CVS command that activates the CVS pluggin, ie this is a cvs import
410     using the conventional module .cmtcvsinfos/<protocol-level>/<module>
411
412     We create a temporary directory just to lauch the command. However nothing
413     should change in this temporary directory since the pluggin returns an error status.
414   */
415  void retreive_cvs_infos (const cmt_string& module)
416  {
417    static const cmt_string cmtcvsinfos = ".cmtcvsinfos";
418
419    cmt_string home_dir = CmtSystem::pwd ();
420       
421    //
422    // Activities related with .cmtcvsinfos will occur in a temporary directory
423    //
424    cmt_string tmp_dir = CmtSystem::getenv ("TMPDIR");
425    if (tmp_dir == "")
426      {
427        tmp_dir = CmtSystem::file_separator ();
428        tmp_dir += "tmp";
429      }
430   
431    if (!CmtSystem::cd (tmp_dir))
432      {
433        tmp_dir = home_dir;
434      }
435   
436    tmp_dir += CmtSystem::file_separator ();
437    tmp_dir += "cmtcvs";
438    {
439      cmt_string temp = CmtSystem::get_temporary_name ();
440      CmtSystem::basename (temp, temp);
441      // Suppress dots for Windows
442      temp.replace_all (".", "");
443      tmp_dir += temp;
444    }
445   
446    if (!CmtSystem::test_directory (tmp_dir))
447      {
448        if (!CmtSystem::mkdir (tmp_dir))
449          {
450            cout << "#CMT> Cannot create the temporary directory [" << tmp_dir << "]" << endl;
451            return;
452          }
453      }
454   
455    //trap "rm -rf ${tmp_dir}" 0 1 2 15
456   
457    if (!CmtSystem::cd (tmp_dir))
458      {
459        cout << "#CMT> Cannot move to the temporary directory " << tmp_dir << endl;
460       
461        if (m_verbose)
462          {
463            cout << "#CMT> now removing tmp_dir " << tmp_dir << " home=" << home_dir << endl;
464          }
465       
466        CmtSystem::remove_directory (tmp_dir);
467       
468        return;
469      }
470   
471    if (m_verbose)
472      {
473        cout << "#CMT> cvs infos are now obtained from the temporary directory " << CmtSystem::pwd () << endl; 
474      }
475   
476    /**
477       The script associated to such entries is supposed to :
478        1) extract the set of <infos> from ${module}/cmt/requirements
479                                        or ${module}/cmt/project.cmt
480        2) build an output of the form :
481            <infos>=info1 info2 info3 ...
482     
483       Currently this script can be found in
484     
485       ${CMTROOT}/cmt/cmt_buildcvsinfos2.sh
486       %CMTROOT%/cmt/cmt_buildcvsinfos.py
487       There is a C++ implementation as cmtcvs.exe
488     
489    */
490   
491    if (!CmtSystem::test_directory (cmtcvsinfos))
492      {
493        CmtSystem::mkdir (cmtcvsinfos);
494      }
495   
496    CmtSystem::cd (cmtcvsinfos);
497   
498    cmt_string cvsroot;
499   
500    CmtSystem::get_cvsroot (cvsroot);
501   
502    cmt_string command;
503   
504    command = "cvs";
505    if (cvsroot != "") 
506      {
507        command += " -d ";
508        command += cvsroot;
509      }
510    command += " -Q import -m cmt ";
511   
512    command += cmtcvsinfos;
513   
514    if (m_protocol_level != "")
515      {
516        command += "/";
517        command += m_protocol_level;
518      }
519    command += "/";
520    command += module;
521    command += " CMT v1";
522   
523    m_last_cvs_infos = "";
524
525    execute (command, m_last_cvs_infos);
526   
527    if (m_verbose)
528      {
529        cout << "#CMT> now removing tmp_dir " << tmp_dir << " home=" << home_dir << endl;
530      }
531   
532    CmtSystem::cd (home_dir);
533    CmtSystem::remove_directory (tmp_dir);     
534  }
535
536  /**
537     This method exploits the hook installed into the loginfo script.
538     A communication is setup with a dummy CVS module named .cmtcvsinfos/<module>
539   
540     At import time, the contents of the file will be used to parameterize
541     the script named cmt_buildcvsinfos2.sh (referenced in the loginfo script)
542   
543      This script performs a scan in the CVS repository for the following types of
544     information :
545   
546       the recognized structure below this module (none, project, package)
547       all top symbolic tags installed for the module
548       all symbolic tags installed for the module
549       all branches available below this module
550       all subpackages installed below the module.
551       all subprojects installed below the module.
552   
553        In principle, only modules corresponding to true CMT products (packages or projects) are considered.
554          o tags are obtained from the requirements or the project file
555          o branches are sub-directories which are not themselves packages
556          o subpackages are sub-directories which are CMT packages
557            (a subdirectory is always either a branch or a subpackage)
558  */
559  void get_cvs_infos_with_offset (const cmt_string& module)
560  {
561    if (!check_protocol ())
562      {
563        cout << "#CMT> The CVS pluggin is not installed or is not at protocol level " << m_protocol_level << endl;
564        return;
565      }
566
567    if (module == "")
568      {
569        cout << "#CMT> cmt cvs needs a module name" << endl;
570        return;
571      }
572
573    if (module == m_last_module)
574      {
575        if (m_verbose)
576          {
577            cout << "#CMT> cvs infos for module " << module << " already there" << endl;
578          }
579      }
580    else
581      {
582        m_last_module = module;
583       
584        retreive_cvs_infos (module);
585      }
586   
587    /**
588       Now retrieve all info fields :
589    */
590
591    Grep grep;
592
593    grep.run (m_last_cvs_infos, "structure=");
594   
595    if (grep.result () != "")
596      {
597        structure_info = grep.result ();
598        structure_info.replace ("structure=", "");
599      }
600    else
601      {
602        // This may happen for old protocol level < v1r1
603        structure_info = "package";
604      }
605   
606    grep.run (m_last_cvs_infos, "error=");
607   
608    if (grep.result () != "")
609      {
610        error_info = grep.result ();
611        error_info.replace ("error=", "");
612      }
613    else
614      {
615        error_info = "";
616      }
617   
618    grep.run (m_last_cvs_infos, "tags_top=");
619   
620    if (grep.result () != "")
621      {
622        tags_top_info = grep.result ();
623        tags_top_info.replace ("tags_top=", "");
624      }
625    else
626      {
627        tags_top_info = "";
628      }
629   
630    grep.run (m_last_cvs_infos, "tags=");
631   
632    if (grep.result () != "")
633      {
634        tags_info = grep.result ();
635        tags_info.replace ("tags=", "");
636      }
637    else
638      {
639        tags_info = "";
640      }
641   
642    grep.run (m_last_cvs_infos, "cvsversions_top=");
643   
644    if (grep.result () != "")
645      {
646        cvsversions_top_info = grep.result ();
647        cvsversions_top_info.replace ("cvsversions_top=", "");
648      }
649    else
650      {
651        cvsversions_top_info = "";
652      }
653   
654    grep.run (m_last_cvs_infos, "cvsversions=");
655   
656    if (grep.result () != "")
657      {
658        cvsversions_info = grep.result ();
659        cvsversions_info.replace ("cvsversions=", "");
660      }
661    else
662      {
663        cvsversions_info = "";
664      }
665   
666    grep.run (m_last_cvs_infos, "branches=");
667   
668    if (grep.result () != "")
669      {
670        branches_info = grep.result ();
671        branches_info.replace ("branches=", "");
672      }
673    else
674      {
675        branches_info = "";
676      }
677   
678    grep.run (m_last_cvs_infos, "subpackages=");
679   
680    if (grep.result () != "")
681      {
682        subpackages_info = grep.result ();
683        subpackages_info.replace ("subpackages=", "");
684      }
685    else
686      {
687        subpackages_info = "";
688      }
689   
690    grep.run (m_last_cvs_infos, "subprojects=");
691
692    if (grep.result () != "")
693      {
694        subprojects_info = grep.result ();
695        subprojects_info.replace ("subprojects=", "");
696      }
697    else
698      {
699        subprojects_info = "";
700      }
701
702    /**
703       The CMTCVSTAGFILTER env. var. may contain a regexp that will exclude some tags
704       from the answr of the CVS pluggin.
705       The pattern is a regexp but it may also contain the <package> template
706     */
707    cmt_string tag_filter = CmtSystem::getenv ("CMTCVSTAGFILTER");
708   
709    if (tag_filter != "")
710      {
711        cmt_string package;
712        CmtSystem::basename (module, package);
713       
714        cmt_string pattern = "<package>";
715       
716        tag_filter.replace_all (pattern, package);
717       
718        cmt_regexp exp (tag_filter);
719       
720        cmt_string text;
721       
722        filter_list (tags_top_info, exp);
723        filter_list (tags_info, exp);
724        filter_list (cvsversions_top_info, exp);
725        filter_list (cvsversions_info, exp);
726      }
727  }
728 
729  void get_cvs_infos (const cmt_string& cvs_offset, const cmt_string& module)
730  {
731    cmt_string full_name;
732
733    if (cvs_offset != "") 
734      {
735        full_name = cvs_offset;
736        full_name += "/";
737        while (full_name.find ("//") != cmt_string::npos)
738          {
739            full_name.replace_all ("//", "/");
740          }
741      }
742
743    full_name += module;
744
745    get_cvs_infos_with_offset (full_name);
746  }
747
748  /**
749     From a space-separated list of version tags, try to find one tag
750     matching a given regular expression.
751
752       o The first matching tag is returned into 'version'
753       o Success is returned as function value.
754
755   */
756  bool match_version_request (const cmt_string& text, 
757                              const cmt_regexp& version_exp,
758                              cmt_string& version)
759  {
760    CmtSystem::cmt_string_vector vs;
761     
762    CmtSystem::split (text, " \t", vs);
763
764    version = "";
765
766    for (int i = 0; i < vs.size (); i++)
767      {
768        const cmt_string& vv = vs[i];
769       
770        if (version_exp.match (vv))
771          {
772            version = vv;
773            return (true);
774          }
775      }
776
777    return (false);
778  }
779
780  void get_module (const cmt_string& offset,
781                   const cmt_string& product,
782                   cmt_string& module)
783  {
784    module = "";
785
786    if (offset != "")
787      {
788        module = offset;
789        module += "/"; // This is for CVS only thus we don't use the real separator.
790        while (module.find ("//") != cmt_string::npos)
791          {
792            module.replace_all ("//", "/");
793          }
794      }
795
796    module += product;
797  }
798
799  bool get_version (const cmt_string& offset,
800                    const cmt_string& product,
801                    const cmt_string& version_request,
802                    const cmt_string& module,
803                    cmt_string& version,
804                    bool& at_head)
805  {
806    Grep grep;
807    cmt_string topversions;
808    cmt_string versions;
809    cmt_string requested_version = version_request;
810   
811    at_head = false;
812           
813    /**
814     *   Try to figure out what is the effective version tag available
815     *   for the requested version expressions (which may contain
816     *   wild card)
817     *
818     *     the requested version may either be in the top tags (ie those
819     *     corresponding to the same most recent CVS version number before
820     *     the HEAD) or not. The returned at_head flag will show this.
821     *
822     *     then the requested product may either be located in the CVS repository
823     *     under the offset or not, the returned module will contain the effective
824     *     location where the requested version has been found.
825     */
826   
827    if (m_verbose)
828      {
829        cout << "#CMT> requesting cvs infos onto module " << module << endl;
830      }
831   
832    get_cvs_infos_with_offset (module);
833   
834    if (error_info != "")
835      {
836        versions = "";
837        cout << "#CMT> Product " << product << " not found in ${CVSROOT}" << endl;
838        return (false);
839      }
840
841    versions = tags_top_info;
842
843    cmt_string v = version_request;
844   
845    if (version_request.find ("*") != cmt_string::npos)
846      {
847        v.replace_all ("*", ".*");
848      }
849    else
850      {
851        // this is an exact match to the end of the word since there is no wild card
852        v += "$";
853      }
854   
855    cmt_regexp version_exp (v);
856   
857    if (!match_version_request (versions, version_exp, version))
858      {
859        // We try on non-top versions
860
861        versions = tags_info;
862
863        if (!match_version_request (versions, version_exp, version))
864          {
865            version = requested_version;
866            int pos = 0;
867            if ((pos = version.find ("*")) != cmt_string::npos)
868              {
869                //
870                //  There was a wild card but the expression does not match
871                // any of the existing tags in CVS.
872                //  Things will be retreived from HEAD but we have to build
873                // a reasonable version tag from the wild card expression.
874                //  If the letter before the * was a digit, then simply remove
875                // the * (v5* -> v5) otherwise add a zero (v5r* -> v5r0)
876                //
877                if (pos > 0)
878                  {
879                    char letter = version[pos-1];
880                   
881                    static const cmt_string digits = "0123456789";
882                   
883                    if (digits.find (letter) == cmt_string::npos)
884                      {
885                        // "v5r*" -> "v5r0"
886                        version.replace ("*", "0");
887                      }
888                    else
889                      {
890                        // "v5*" -> "v5"
891                        version.replace ("*", "");
892                      }
893                  }
894                else
895                  {
896                    // The expression was simply "*" !!!
897                    version = "v0";
898                  }
899              }
900            at_head = true;
901          }
902        else
903          {
904            at_head = false;
905          }
906      }
907    else
908      {
909        at_head = true;
910      }
911   
912    /**
913     *   Here we have at least one version matching the requested expression.
914     */
915   
916    return (true);
917  }
918
919  bool do_need_version ()
920  {
921    bool need_version = false;
922
923    if (structure_info == "project")
924      {
925        need_version = true;
926      }
927    else
928      {
929        CmtStructuringStyle style = Cmt::get_current_structuring_style ();
930
931        if (style == default_structuring_style)
932          {
933            Use& current_use = Use::current ();
934            if (current_use.get_strategy ("VersionDirectory"))
935              {
936                need_version = true;
937              }
938          }
939        else if (style == with_version_directory)
940          {
941            need_version = true;
942          }
943      }
944
945    return (need_version);
946  }
947
948  /**
949     Take care of structuring style for packages and of project vs package conventions
950   */
951  cmt_string build_version_directory (const cmt_string& offset,
952                                      const cmt_string& product,
953                                      const cmt_string& version)
954  {
955    cmt_string dir = m_home_dir;
956   
957    if (m_checkout_dir != "")
958      {
959        // consider the usual -d option
960 
961        dir += CmtSystem::file_separator ();
962        dir += m_checkout_dir;
963      }
964   
965    dir += CmtSystem::file_separator ();
966    dir += offset;
967    dir += CmtSystem::file_separator ();
968    dir += product;
969
970    if (do_need_version ())
971      {
972        dir += CmtSystem::file_separator ();
973        dir += version;
974      }
975   
976    CmtSystem::reduce_file_separators (dir);
977   
978    return (dir);
979  }
980
981  /**
982     Wrapper to mkdir handling simulation and verbose options.
983   */
984  bool mkdir (const cmt_string& dir)
985  {
986    if (m_simulation)
987      {
988        cout << "#CMT> Would create the " << dir << " directory" << endl;
989      }
990    else
991      {
992        if (!CmtSystem::cd (dir))
993          {
994            if (m_verbose)
995              {
996                cout << "#CMT> About to mkdir " << dir << endl;
997              }
998
999            CmtSystem::mkdir (dir);
1000            if (!CmtSystem::cd (dir))
1001              {
1002                cout << "# Error creating the directory :" << dir << endl;
1003                cout << "#---------------------------------------------------------" << endl;
1004                return (false);
1005              }
1006          }
1007      }
1008    return (true);
1009  }
1010
1011  /**
1012     When running cmt cvs commands, we stand by definition outside of any existing
1013     package context. Thus it's likely that CMTPATH are not completely defined.
1014     This function manually prepends CMTPATH entries to the environment variable.
1015   */
1016  void add_cmtpath (const cmt_string& dir)
1017  {
1018    static cmt_string CMTPATH;
1019
1020    cmt_string cmtpath = CmtSystem::getenv ("CMTPATH");
1021
1022    if (cmtpath.find (dir) == cmt_string::npos)
1023      {
1024        CMTPATH = "CMTPATH=";
1025        CMTPATH += dir;
1026        CMTPATH += ":";
1027        CMTPATH += cmtpath;
1028       
1029        CmtSystem::putenv (CMTPATH);
1030      }
1031
1032    if (m_verbose)
1033      {
1034        cout << "#CMT> CMTPATH=" << CmtSystem::getenv ("CMTPATH") << endl;
1035      }
1036  }
1037
1038  /**
1039     Construct CVS management files in the top directory. This is needed
1040     if the top directory of a product is empty. (In this case the
1041     co -l results in nothing)
1042   */
1043  void make_management_files (const cmt_string& module,
1044                              const cmt_string& entries_text)
1045  {
1046    if (!CmtSystem::test_directory ("CVS"))
1047      {
1048        /**
1049         * The CVS repository had not been created (this is generally
1050         * due to the lack of top files)
1051         */
1052
1053        if (!mkdir ("CVS")) return;
1054
1055        CmtSystem::cd ("..");
1056
1057        cmt_string s;
1058       
1059        // Let's create first the CVS/Root file.
1060       
1061        CmtSystem::get_cvsroot (s);
1062        s += "\n";
1063       
1064        cmt_string f;
1065       
1066        f = "CVS";
1067        f += CmtSystem::file_separator ();
1068        f += "Root";
1069       
1070        if (m_simulation)
1071          {
1072            cout << "#CMT> Would fill in the CVS/Root file with " << endl;
1073            cout << s << endl;
1074          }
1075        else
1076          {
1077            if (m_verbose)
1078              {
1079                cout << "#CMT> Fill in the CVS/Root file with " << endl;
1080                cout << s << endl;
1081              }
1082            s.write (f);
1083          }
1084       
1085        // Now we create the CVS/Repository file
1086       
1087        f = "CVS";
1088        f += CmtSystem::file_separator ();
1089        f += "Repository";
1090       
1091        CmtSystem::get_cvsroot (s);
1092        if (s[0] == ':')
1093          {
1094            int pos = s.find (1, ":");
1095            s.erase (0, pos+1);
1096            pos = s.find (0, ":");
1097            s.erase (0, pos+1);
1098          }
1099        s += "/";
1100        s += module;
1101        s += "\n";
1102       
1103        if (m_simulation)
1104          {
1105            cout << "#CMT> Would fill in the CVS/Repository file with " << endl;
1106            cout << s << endl;
1107          }
1108        else
1109          {
1110            if (m_verbose)
1111              {
1112                cout << "#CMT> Fill in the CVS/Repository file with " << endl;
1113                cout << s << endl;
1114              }
1115            s.write (f);
1116          }
1117      }
1118   
1119    if (m_simulation)
1120      {
1121        cout << "#CMT> Would write the top CVS/Entries file with " << endl;
1122        cout << entries_text << endl;
1123      }
1124    else
1125      {
1126        cmt_string entries_file_name;
1127
1128        entries_file_name = "CVS";
1129        entries_file_name += CmtSystem::file_separator ();
1130        entries_file_name += "Entries";
1131   
1132        cmt_string text;
1133
1134        if (!text.read (entries_file_name))
1135          {
1136            // This happens when there were no top files
1137          }
1138
1139        text += entries_text;
1140
1141        // Now the CVS/Entries is ready to be created.
1142        if (m_verbose)
1143          {
1144            cout << "#CMT> Fill in the top CVS/Entries file with " << endl;
1145            cout << text << endl;
1146          }
1147
1148        text.write (entries_file_name);
1149      }
1150
1151  }
1152
1153  /**
1154     Specific checkout of one project
1155   */
1156  bool really_checkout_project_contents (const cmt_string& offset,
1157                                         const cmt_string& project,
1158                                         const cmt_string& version,
1159                                         const cmt_string& tag,
1160                                         const cmt_string& module,
1161                                         const cmt_string& basedir,
1162                                         bool at_head,
1163                                         const cmt_string& currentdir)
1164  {
1165    cmt_string dir = currentdir;
1166
1167    cout << "  # get project files into " << dir << endl;
1168
1169    cmt_string version_dir = version;
1170
1171    if (!mkdir (version_dir)) return (false);
1172
1173    dir += CmtSystem::file_separator ();
1174    dir += version_dir;
1175   
1176    cmt_string command = "cvs -Q co -P ";
1177    if (!at_head)
1178      {
1179        command += "-r ";
1180        command += (tag != "") ? tag : version;
1181      }
1182
1183    command += " -d cmt ";
1184
1185    command += " ";
1186    command += module;
1187    command += "/cmt";
1188
1189    execute_and_retry (command, "Error getting project CMT contents");
1190
1191    make_management_files (module, "D/cmt////\n");
1192
1193    return (true);
1194  }
1195
1196  /**
1197     Specific checkout of one package
1198
1199     1) get top files (no directories)
1200     2) construct the directory structure (with or without version directory)
1201     3) build the CVS/Entries file for subdirs and co individual subdirs
1202     4) write the CVS management files if CVS did not do it.
1203   */
1204  bool really_checkout_package_contents (const cmt_string& offset,
1205                                         const cmt_string& package,
1206                                         const cmt_string& version,
1207                                         const cmt_string& module,
1208                                         const cmt_string& basedir,
1209                                         bool at_head,
1210                                         const cmt_string& currentdir)
1211  {
1212    cmt_string dir = currentdir;
1213
1214    cout << "  # get top files " << endl;
1215           
1216    cmt_string command = "cvs -Q co -P -l ";
1217    if (!at_head)
1218      {
1219        command += "-r ";
1220        command += version;
1221      }
1222
1223    bool need_version = do_need_version ();
1224
1225    if (need_version)
1226      {
1227        command += " -d ";
1228        command += version;
1229      }
1230    else
1231      {
1232        command += " -d ";
1233        command += package;
1234
1235        // Must stand just above the package directory
1236        CmtSystem::cd ("..");
1237        CmtSystem::dirname (dir, dir);
1238      }
1239   
1240    command += " ";
1241    command += module;
1242   
1243    execute_and_retry (command, "Error getting package CMT contents");
1244
1245    if (need_version)
1246      {
1247        if (!mkdir (version)) return (false);
1248       
1249        dir += CmtSystem::file_separator ();
1250        dir += version;
1251      }
1252    else
1253      {
1254        if (!mkdir (package)) return (false);
1255       
1256        dir += CmtSystem::file_separator ();
1257        dir += package;
1258      }
1259   
1260    if (m_verbose)
1261      {
1262        cout << "#CMT> Now getting subdirectories pwd=" << CmtSystem::pwd () << " dir=" << dir << endl;
1263      }
1264
1265    cmt_string branches = CmtSystem::getenv ("CMTCVSBRANCHES");
1266   
1267    if (branches == "")
1268      {
1269        branches = branches_info;
1270      }
1271   
1272    CmtSystem::cmt_string_vector branch_vector;
1273   
1274    CmtSystem::split (branches, " \t", branch_vector);
1275   
1276    cout << "  # get branches " << branches << endl;
1277   
1278    cmt_string text = "";
1279   
1280    command = "";
1281   
1282    int i;
1283   
1284    for (i = 0; i < branch_vector.size (); i++)
1285      {
1286        cmt_string& branch = branch_vector[i];
1287       
1288        if (i > 0)
1289          {
1290            command += CmtSystem::command_separator ();
1291          }
1292       
1293        command += "cvs -Q co ";
1294       
1295        if (!at_head)
1296          {
1297            command += "-r ";
1298            command += version;
1299          }
1300       
1301        command += " -d ";
1302        command += branch;
1303        command += " ";
1304        command += module;
1305        command += "/";    // CVS uses the '/' notation on all platforms!!
1306        command += branch;
1307       
1308        text += "D/";
1309        text += branch;
1310        text += "////\n";
1311      }
1312   
1313    execute_and_retry (command, "Error getting package contents");
1314
1315    make_management_files (module, text);
1316
1317    return (true);
1318  }
1319
1320  /**
1321     Effective checkout of a package or a project
1322   */
1323  bool really_checkout (const cmt_string& offset,
1324                        const cmt_string& product,
1325                        const cmt_string& version,
1326                        const cmt_string& tag,
1327                        const cmt_string& module,
1328                        const cmt_string& basedir,
1329                        bool at_head)
1330  {
1331    cmt_string dir = basedir;
1332    cmt_string out;
1333   
1334    cout << "# ================= working on " << structure_info << " " << product
1335         << " version " << version;
1336
1337    if (at_head) cout << " (At head) ";
1338
1339    cmt_string full_offset;
1340
1341    full_offset = m_cvs_offset;
1342    full_offset += offset;
1343
1344    cmt_string echo_ppath;
1345       
1346    if (offset != "")
1347      {
1348        echo_ppath = " path ";
1349        echo_ppath += offset;
1350      }
1351   
1352    cout << echo_ppath << " in " << dir << endl;
1353
1354    if (do_need_version ())
1355      {
1356        // Move back to the product name.
1357        CmtSystem::dirname (dir, dir);
1358      }
1359
1360    if (!mkdir (dir)) return (false);
1361       
1362    if (structure_info == "package")
1363      {
1364        really_checkout_package_contents (offset,
1365                                          product,
1366                                          version,
1367                                          module,
1368                                          basedir,
1369                                          at_head,
1370                                          dir);
1371      }
1372    else if (structure_info == "project")
1373      {
1374        really_checkout_project_contents (offset,
1375                                          product,
1376                                          version,
1377                                          tag,
1378                                          module,
1379                                          basedir,
1380                                          at_head,
1381                                          dir);
1382      }
1383
1384    return (true);
1385  }
1386
1387  /**
1388     Find the most appropriate effective version directory corresponding to the
1389     specified version expression.
1390     The specified expression may contain wildcards (in the file manager sense). This
1391     FME is first converted into a RE then a directory search is performed.
1392     An empty string is returned if no match is found.
1393   */
1394  cmt_string find_matching_version (const cmt_string& expression)
1395  {
1396    cmt_string result;
1397
1398    //
1399    // Here expression takes the form
1400    //   <some path>/<expression with wild-card>
1401    //
1402
1403    cmt_string dir;
1404    CmtSystem::dirname (expression, dir);
1405    dir += CmtSystem::file_separator ();
1406   
1407    cmt_string version;
1408    CmtSystem::basename (expression, version);
1409
1410    if (version.find ("*") == cmt_string::npos)
1411      {
1412        // there is no wildcarding here. A simple test is enough.
1413        if (CmtSystem::test_directory (expression))
1414          {
1415            result = version;
1416          }
1417      }
1418    else
1419      {
1420        version.replace ("*", ".*");
1421       
1422        cmt_regexp exp (version);
1423       
1424        CmtSystem::cmt_string_vector list;
1425       
1426        CmtSystem::scan_dir (dir, exp, list);
1427       
1428        if (list.size () > 0)
1429          {
1430            result = list[0];
1431           
1432            CmtSystem::basename (result, result);
1433          }
1434      }
1435   
1436    return (result);
1437  }
1438 
1439  /**
1440   *   We provide a path to a requirements file. From it we read the use
1441   *  statements, and we try to checkout the corresponding packages.
1442   *
1443   *   A boolean return tells if any recursion occurred.
1444   */
1445  bool checkout_from_requirements (const cmt_string& requirements_path)
1446  {
1447    static cmt_regexp expression ("^[ \t]*use[ \t]");
1448
1449    cmt_string text;
1450
1451    text.read (requirements_path);
1452
1453    RecursivePass1 p1;
1454    p1.run (text, expression);
1455
1456    bool result = (p1.result () != "");
1457    RecursivePass2 p2 (*this);
1458    p2.run (p1.result ());
1459
1460    return (result);
1461  }
1462
1463  /**
1464   *   We provide a path to a project file. From it we read the use
1465   *  statements, and we try to checkout the corresponding projects.
1466   */
1467  void checkout_from_project_file (const cmt_string& file_name)
1468  {
1469    static cmt_regexp expression ("^[ \t]*use[ \t]");
1470
1471    cmt_string text;
1472
1473    text.read (file_name);
1474
1475    CvsImplementation& me = *this;
1476    CvsImplementation saved;
1477    saved = me;
1478    cmt_string here = CmtSystem::pwd ();
1479
1480    RecursivePass3 p3;
1481    p3.run (text, expression);
1482
1483    RecursivePass4 p4 (*this);
1484    p4.run (p3.result ());
1485
1486    Grep grep;
1487
1488    grep.run (text, "container");
1489    cmt_string container = grep.result ();
1490
1491    if (container != "")
1492      {
1493        static cmt_regexp container_expression ("^[ \t]*container[ \t]");
1494
1495        add_cmtpath (here);
1496
1497        cout << "  # --> now getting project packages from the " << container << " " << here << endl;
1498
1499        CmtSystem::cd (here);
1500
1501        RecursivePass1 p1;
1502        p1.run (text, container_expression);
1503
1504        RecursivePass2 p2 (*this);
1505
1506        m_home_dir = CmtSystem::pwd ();
1507        p2.run (p1.result ());
1508      }
1509
1510    CmtSystem::cd (here);
1511    me = saved;
1512  }
1513
1514  /**
1515     Check whether a given directory structure matches an expected product structure
1516     given by the structure info obtained from the most recent request to the CVS pluggin
1517   */
1518  bool check_structure (const cmt_string& dir)
1519  {
1520    bool result = false;
1521
1522    if (!CmtSystem::test_directory (dir))
1523      {
1524        return (false);
1525      }
1526
1527    if (structure_info == "package")
1528      {
1529        // Check if it is a true CMT package.
1530       
1531        cmt_string file_name;
1532       
1533        file_name = dir;
1534        file_name += CmtSystem::file_separator ();
1535        file_name += "cmt";
1536        file_name += CmtSystem::file_separator ();
1537        file_name += "requirements";
1538       
1539        if (CmtSystem::test_file (file_name))
1540          {
1541            result = true;
1542          }
1543        else
1544          {
1545            file_name = dir;
1546            file_name += CmtSystem::file_separator ();
1547            file_name += "mgr";
1548            file_name += CmtSystem::file_separator ();
1549            file_name += "requirements";
1550           
1551            if (CmtSystem::test_file (file_name))
1552              {
1553                result = true;
1554              }
1555          }
1556      }
1557    else if (structure_info == "project")
1558      {
1559        cmt_string file_name;
1560       
1561        file_name = dir;
1562        file_name += CmtSystem::file_separator ();
1563        file_name += "cmt";
1564        file_name += CmtSystem::file_separator ();
1565        file_name += "project.cmt";
1566       
1567        if (CmtSystem::test_file (file_name))
1568          {
1569            result = true;
1570          }
1571      }
1572
1573    return (result);
1574  }
1575
1576  /**
1577     Internal call from the initial do_checkout or from recursive passes
1578     Prepare the directory structure for the checkout
1579     Do the checkout
1580     Check if everything was done properly, if a package or a project has been created
1581     If needed recurse from the requirements or project file
1582
1583     For projects there may be two different specifications for
1584     - the version directory
1585     - the CVS tag
1586   */
1587  void do_checkout_phase2 (const cmt_string& offset,
1588                           const cmt_string& product,
1589                           const cmt_string& specified_version,
1590                           const cmt_string& tag)
1591  {
1592    if (m_verbose)
1593      {
1594        cout << "#CMT> do_checkout_phase2> offset=" << offset
1595             << " " << structure_info << "=" << product
1596             << " specified_version=" << specified_version
1597             << " tag=" << tag
1598             << " pwd=" << CmtSystem::pwd ()
1599             << endl;
1600      }
1601   
1602    cmt_string version = specified_version;
1603    cmt_string empty;
1604    cmt_string full_offset;
1605
1606    full_offset = m_cvs_offset;
1607    full_offset += offset;
1608   
1609    cmt_string echo_ppath;
1610   
1611    if (offset != "")
1612      {
1613        echo_ppath = " path ";
1614        echo_ppath += offset;
1615      }
1616   
1617    if (version == "")
1618      {
1619        cout << "# ================= No version specified for " << structure_info << " " << product << endl;
1620        return;
1621      }
1622   
1623    //
1624    //  First make an attempt to locate the specified version of
1625    //  this product "as-it-is" in the work area.
1626    //   Since 'version' may contain wild-card, it's likely that
1627    //  we should not simply use CmtSystem::test_directory but
1628    //  use the wild-card search.
1629    //
1630   
1631    cmt_string dir;
1632   
1633    dir = build_version_directory (offset, product, version);
1634   
1635    bool recursive = m_recursive;
1636   
1637    cmt_string effective_version = find_matching_version (dir);
1638   
1639    cmt_string module;
1640    get_module (full_offset, product, module);
1641
1642    cmt_string cvs_tag = (tag != "") ? tag : version;
1643    bool at_head = false;
1644
1645    if (effective_version != "")
1646      {
1647        version = effective_version;
1648      }
1649    else
1650      {
1651        //
1652        // get_version attempts to find the most appropriate version
1653        // tag matching the specification FROM the repository. However,
1654        // we should take into account situations where some versions have
1655        // already been checked out, in which case they might be sufficient
1656        // (or preferred?)
1657        //
1658       
1659        if (cvs_tag.find ("*") != cmt_string::npos)
1660          {
1661            cout << "# ================= " << structure_info << " " << product
1662                 << " version " << cvs_tag << echo_ppath
1663                 << " has wild cards and will not be considered." << endl;
1664            return;
1665          }
1666       
1667        if (!get_version (full_offset, product, cvs_tag, module,
1668                          cvs_tag, at_head))
1669          {
1670            return;
1671          }
1672       
1673        if (m_head)
1674          {
1675            m_head = false;
1676           
1677            at_head = true;
1678          }
1679        else
1680          {
1681            at_head = false;
1682          }
1683       
1684        //
1685        // Make a second try after having selected a CVS tag from all the
1686        // available tags compatible with the specified version
1687        //
1688
1689        if (tag == "")
1690          {
1691            // If tag was not specified, then the version directory has to match the CVS tag
1692            // Otherwise the original version specification is kept for the directory.
1693
1694            version = cvs_tag;
1695            dir = build_version_directory (offset, product, version);
1696          }
1697      }
1698   
1699
1700    if (check_structure (dir))
1701      {
1702        cout << "# ================= " << structure_info << " " << product
1703             << " version " << version << echo_ppath
1704             << " already installed." << endl;
1705       
1706        recursive = false;
1707      }
1708    else
1709      {
1710        //
1711        // Now we can say that we have to perform the real checkout.
1712        //
1713       
1714        if (!really_checkout (offset, product, version, cvs_tag, module, dir, at_head))
1715          {
1716            cout << "# bad return from really_checkout_product" << endl;
1717            return;
1718          }
1719      }
1720
1721    //
1722    //  Now reach the newly checked out product.
1723    //
1724   
1725    if (m_simulation)
1726      {
1727        cout << "#CMT> " << structure_info << " directory not really created " << dir << endl;
1728      }
1729    else if (structure_info == "package")
1730      {
1731        if (!CmtSystem::cd (dir))
1732          {
1733            cout << "#CMT> Package directory not created " << dir << endl;
1734            return;
1735          }
1736       
1737        // Check if it is a true CMT package.
1738       
1739        cmt_string file_name;
1740       
1741        file_name = "cmt";
1742        file_name += CmtSystem::file_separator ();
1743        file_name += "requirements";
1744       
1745        if (CmtSystem::test_file (file_name))
1746          {
1747            dir += CmtSystem::file_separator ();
1748            dir += "cmt";
1749            CmtSystem::cd ("cmt");
1750           
1751            if (!do_need_version ())
1752              {
1753                cmt_string text = version;
1754                text += "\n";
1755                text.write ("version.cmt");
1756              }
1757          }
1758        else
1759          {
1760            file_name = "mgr";
1761            file_name += CmtSystem::file_separator ();
1762            file_name += "requirements";
1763           
1764            if (CmtSystem::test_file (file_name))
1765              {
1766                dir += CmtSystem::file_separator ();
1767                dir += "mgr";
1768                CmtSystem::cd ("mgr");
1769              }
1770            else
1771              {
1772                cout << "# " << product << " not a CMT package" << endl;
1773                return;
1774              }
1775          }
1776       
1777        if (recursive)
1778          {
1779            cmt_string here = CmtSystem::pwd ();
1780
1781            bool did_recurse = checkout_from_requirements ("requirements");
1782
1783            CmtSystem::cd (here);
1784
1785            if (did_recurse) 
1786              {
1787                execute ("cmt -quiet broadcast cmt -quiet config");
1788              }
1789            else
1790              {
1791                execute ("cmt -quiet config");
1792              }
1793          }
1794        else
1795          {
1796            execute ("cmt -quiet config");
1797          }
1798      }
1799    else if (structure_info == "project")
1800      {
1801       
1802        if (m_verbose)
1803          {
1804            cout << "#CMT> dir=" << dir << endl;
1805          }
1806       
1807        if (!CmtSystem::cd (dir))
1808          {
1809            cout << "#CMT> Project directory not created " << dir << endl;
1810            return;
1811          }
1812       
1813        cmt_string file_name;
1814       
1815        file_name = "cmt";
1816        file_name += CmtSystem::file_separator ();
1817        file_name += "project.cmt";
1818       
1819        if (!CmtSystem::test_file (file_name))
1820          {
1821            cout << "# " << product << " not a CMT project" << endl;
1822            return;
1823          }
1824       
1825        if (recursive)
1826          {
1827            checkout_from_project_file (file_name);
1828          }
1829
1830        cout << "# ================= Project " << product << " completed" << endl;
1831
1832      }
1833  }
1834 
1835  /**
1836     Top level of the checkout operation, initiated from the command line arguments
1837
1838     Construct an history of the checkouts to avoid duplicating the
1839     checkouts during the recursivity
1840
1841     Eventually
1842     o perform the cmt config for packages.
1843
1844   */
1845  void do_checkout_phase1 (const cmt_string& module, 
1846                           const cmt_string& version_dir, 
1847                           const cmt_string& version_tag)
1848  {
1849    add_cmtpath (m_home_dir);
1850
1851    History& h = History::instance ();
1852
1853    h.clear ();
1854
1855    if (module == "")
1856      {
1857        if (m_verbose)
1858          {
1859            cout << "#CMT> Missing module name" << endl;
1860          }
1861        return;
1862      }
1863
1864    cmt_string offset;
1865    cmt_string product;
1866    cmt_string version;
1867    cmt_string tag;
1868   
1869    {
1870      cmt_string m;
1871      m = m_cvs_offset;
1872      m += module;
1873       
1874      get_cvs_infos_with_offset (m);
1875       
1876      if (error_info != "")
1877        {
1878          cout << error_info << endl;
1879          return;
1880        }
1881    }
1882
1883    if (version_tag == "")
1884      {
1885        Cut cut (0);
1886       
1887        if (tags_top_info != "") tag = tags_top_info;
1888        else tag = tags_info;
1889       
1890        cut.run (tag);
1891       
1892        tag = cut.result ();
1893      }
1894    else
1895      {
1896        tag = version_tag;
1897      }
1898
1899    version = (version_dir == "") ? tag : version_dir;
1900
1901    CmtSystem::dirname (module, offset);
1902    CmtSystem::basename (module, product);
1903   
1904    cmt_string top_dir;
1905       
1906    top_dir = m_home_dir;
1907    top_dir += CmtSystem::file_separator ();
1908    top_dir += m_checkout_dir;
1909    top_dir += CmtSystem::file_separator ();
1910    top_dir += offset;
1911    top_dir += CmtSystem::file_separator ();
1912    top_dir += product;
1913    top_dir += CmtSystem::file_separator ();
1914    top_dir += version;
1915
1916    CmtSystem::reduce_file_separators (top_dir);
1917
1918    if (m_verbose)
1919      {
1920        cout << "#CMT> about to checkout " << structure_info
1921             << " " << product << " version " << version << " into " << top_dir << endl;
1922      }
1923
1924    static const cmt_string empty;
1925    do_checkout_phase2 (offset, product, version, tag);
1926
1927    if (m_simulation) return;
1928
1929    if (!CmtSystem::cd (top_dir)) return;
1930
1931    if (structure_info == "project")
1932      {
1933        cmt_string file_name;
1934   
1935        file_name = "cmt";
1936        file_name += CmtSystem::file_separator ();
1937        file_name += "project.cmt";
1938       
1939        if (!CmtSystem::test_file (file_name))
1940          {
1941            cout << "# " << product << " was not properly checked out and is missing its cmt/project.cmt file" << endl;
1942            return;
1943          }
1944      }
1945    else
1946      {
1947        cmt_string file_name;
1948   
1949        file_name = "cmt";
1950        file_name += CmtSystem::file_separator ();
1951        file_name += "requirements";
1952       
1953        if (CmtSystem::test_file (file_name))
1954          {
1955            top_dir += CmtSystem::file_separator ();
1956            top_dir += "cmt";
1957            CmtSystem::cd ("cmt");
1958          }
1959        else
1960          {
1961            file_name = "mgr";
1962            file_name += CmtSystem::file_separator ();
1963            file_name += "requirements";
1964           
1965            if (CmtSystem::test_file (file_name))
1966              {
1967                top_dir += CmtSystem::file_separator ();
1968                top_dir += "mgr";
1969                CmtSystem::cd ("mgr");
1970              }
1971            else
1972              {
1973                cout << "# " << product << " was not properly checked out and is missing its cmt/requirements file" << endl;
1974                return;
1975              }
1976          }
1977       
1978        if (m_verbose)
1979          {
1980            cout << "#CMT> product " << product << " has been checked out" << endl;
1981          }
1982       
1983        if (!m_recursive)
1984          {
1985            execute ("cmt -quiet config");
1986          }
1987      }
1988  }
1989
1990  void help ()
1991  {
1992    cout << "> cd <some work area>" << endl;
1993    cout << "> cmt checkout [modifier ...] <package|project>" << endl;
1994    cout << "" << endl;
1995    cout << "   modifier :" << endl;
1996    cout << "   -l        Do not process used packages (default)." << endl;
1997    cout << "   -R        Process used products recursively." << endl;
1998    cout << "   -r rev    Check out version tag. (is sticky)" << endl;
1999    cout << "   -vd dir   Use this version directory instead of CVS tag." << endl;
2000    cout << "   -d dir    Check out into dir instead of module name." << endl;
2001    cout << "   -o offset Offset in the CVS repository" << endl;
2002    cout << "   -requirements <requirements file path>  Check out packages referenced in this requirements file" << endl;
2003    cout << "   -n        simulation mode on" << endl;
2004    cout << "   -v        verbose mode on" << endl;
2005    cout << "   --help    print this help" << endl;
2006    cout << "" << endl;
2007    cout << "> cmt cvstags <package|project>" << endl;
2008    cout << "> cmt cvssubpackages <directory>" << endl;
2009    cout << "> cmt cvssubprojects <directory>" << endl;
2010    cout << "" << endl;
2011  }
2012
2013  /**
2014     Implementation of the cmt cvstags
2015     Get the CVS tags of a module
2016   */
2017  void tags (const CmtSystem::cmt_string_vector& arguments)
2018  {
2019    if (arguments.size () < 1)
2020      {
2021        help ();
2022        return;
2023      }
2024   
2025    if (CmtSystem::getenv ("CVSROOT") == "")
2026      {
2027        cout << "# Please set CVSROOT first !" << endl;
2028        return;
2029      }
2030   
2031    m_cvs_offset = CmtSystem::getenv ("CMTCVSOFFSET");
2032    if (m_cvs_offset != "") 
2033      {
2034        m_cvs_offset += "/";
2035        m_cvs_offset.replace_all ("//", "/");
2036      }
2037   
2038    bool all = false;
2039   
2040    for (int arg = 0; arg < arguments.size (); arg++)
2041      {
2042        const cmt_string& option = arguments[arg];
2043       
2044        if (option == "-all")
2045          {
2046            all = true;
2047          }
2048        else
2049          {
2050            get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), option);
2051           
2052            if (error_info != "")
2053              {
2054                cout << error_info << endl;
2055              }
2056            else
2057              {
2058                cmt_string tags;
2059               
2060                if (all)
2061                  {
2062                    tags = cvsversions_top_info;
2063                    tags += " ";
2064                    tags += cvsversions_info;
2065                  }
2066                else
2067                  {
2068                    tags = tags_top_info;
2069                    tags += " ";
2070                    tags += tags_info;
2071                  }
2072               
2073                CmtSystem::cmt_string_vector v;
2074               
2075                CmtSystem::split (tags, " \t", v);
2076                for (int i = 0; i < v.size (); i++)
2077                  {
2078                    const cmt_string& s = v[i];
2079                    cout << s << endl;
2080                  }
2081              }
2082          }
2083      }
2084  }
2085
2086  /**
2087     Implementation of the cmt cvsbranches
2088     Get the subdirs of a module that are not subackages
2089   */
2090  void branches (const cmt_string& module)
2091  {
2092    cmt_string out;
2093   
2094    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), module);
2095   
2096    if (error_info != "")
2097      {
2098        cout << error_info << endl;
2099      }
2100    else
2101      {
2102        cout << branches_info << endl;
2103      }
2104  }
2105
2106  /**
2107     Implementation of the cmt cvssubpackages
2108     Get the subdirs of a module that ARE CMT subpackages
2109   */
2110  void subpackages (const cmt_string& module)
2111  {
2112    cmt_string out;
2113   
2114    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), (module == "") ? "." : module);
2115   
2116    if (error_info != "")
2117      {
2118        cout << error_info << endl;
2119      }
2120    else
2121      {
2122        cout << subpackages_info << endl;
2123      }
2124  }
2125 
2126  /**
2127     Implementation of the cmt cvssubrojects
2128     Get the subdirs of a module that ARE CMT projects
2129   */
2130  void subprojects (const cmt_string& module)
2131  {
2132    cmt_string out;
2133   
2134    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), (module == "") ? "." : module);
2135   
2136    if (error_info != "")
2137      {
2138        cout << error_info << endl;
2139      }
2140    else
2141      {
2142        cout << subprojects_info << endl;
2143      }
2144  }
2145
2146  /**
2147     Implementation of the cmt checkout
2148     Parse the arguments
2149     Then call do_checkout for each argument
2150   */
2151  void checkout (const CmtSystem::cmt_string_vector& arguments)
2152  {
2153    if (arguments.size () < 1)
2154      {
2155        help ();
2156        return;
2157      }
2158   
2159    if (CmtSystem::getenv ("CVSROOT") == "")
2160      {
2161        cout << "# Please set CVSROOT first !" << endl;
2162        return;
2163      }
2164   
2165    m_home_dir = CmtSystem::pwd ();
2166    m_checkout_dir = "";
2167    m_version_dir = "";
2168    m_cvs_offset = "";
2169
2170    cmt_string module;
2171   
2172    m_recursive = false;
2173   
2174    bool need_version_tag = false;
2175    cmt_string version_tag;
2176   
2177    bool need_checkout_dir = false;
2178    bool need_cvs_offset = false;
2179    bool need_requirements_file = false;
2180    bool need_version_dir = false;
2181   
2182    m_simulation = false;
2183    //m_verbose = true;
2184    m_verbose = false;
2185   
2186    m_head = true;
2187   
2188    m_cvs_offset = CmtSystem::getenv ("CMTCVSOFFSET");
2189    if (m_cvs_offset != "") 
2190      {
2191        m_cvs_offset += "/";
2192        m_cvs_offset.replace_all ("//", "/");
2193      }
2194   
2195    for (int arg = 0; arg < arguments.size (); arg++)
2196      {
2197        const cmt_string& option = arguments[arg];
2198       
2199        if (need_version_tag)
2200          {
2201            need_version_tag = false;
2202           
2203            if (option == "HEAD")
2204              {
2205                m_head = true;
2206              }
2207            else
2208              {
2209                version_tag = option;
2210              }
2211          }
2212        else if (need_checkout_dir)
2213          {
2214            need_checkout_dir = false;
2215            m_checkout_dir = option;
2216          }
2217        else if (need_version_dir)
2218          {
2219            need_version_dir = false;
2220            m_version_dir = option;
2221          }
2222        else if (need_cvs_offset)
2223          {
2224            need_cvs_offset = false;
2225            m_cvs_offset = option;
2226            m_cvs_offset += '/';
2227            m_cvs_offset.replace_all ("//", "/");
2228          }
2229        else if (need_requirements_file)
2230          {
2231            need_requirements_file = false;
2232            m_head = false;
2233            checkout_from_requirements (option);
2234          }
2235        else
2236          {
2237            if (option == "-R")
2238              {
2239                m_recursive = true;
2240              }
2241            else if (option == "-l")
2242              {
2243                m_recursive = false;
2244              }
2245            else if (option == "-r")
2246              {
2247                need_version_tag = true;
2248                m_head = false;
2249              }
2250            else if (option == "-d")
2251              {
2252                need_checkout_dir = true;
2253              }
2254            else if (option == "-o")
2255              {
2256                need_cvs_offset = true;
2257              }
2258            else if (option == "-n")
2259              {
2260                m_simulation = true;
2261              }
2262            else if (option == "-v")
2263              {
2264                m_verbose = true;
2265              }
2266            else if (option == "-vd")
2267              {
2268                need_version_dir = true;
2269              }
2270            else if (option == "-requirements")
2271              {
2272                need_requirements_file = true;
2273              }
2274            else if (option == "--help")
2275              {
2276                help ();
2277                return;
2278              }
2279            else if (option[0] == '-')
2280              {
2281                help ();
2282                return;
2283              }
2284            else
2285              {
2286                do_checkout_phase1 (option, m_version_dir, version_tag);
2287              }
2288          }
2289      }
2290  }
2291
2292private:
2293
2294  bool m_recursive;
2295  bool m_head;
2296  bool m_verbose;
2297  bool m_simulation;
2298
2299  cmt_string m_home_dir;
2300  cmt_string m_checkout_dir;
2301  cmt_string m_version_dir;
2302  cmt_string m_cvs_offset;
2303
2304  cmt_string m_protocol_level;
2305  cmt_string m_last_module;
2306  cmt_string m_last_cvs_infos;
2307  cmt_string structure_info;
2308  cmt_string error_info;
2309  cmt_string tags_top_info;
2310  cmt_string tags_info;
2311  cmt_string cvsversions_top_info;
2312  cmt_string cvsversions_info;
2313  cmt_string branches_info;
2314  cmt_string subpackages_info;
2315  cmt_string subprojects_info;
2316};
2317
2318//--------------------------------------------------------------------
2319
2320void Grep::begin ()
2321{
2322  m_result = "";
2323}
2324
2325void Grep::filter (const cmt_string& line)
2326{
2327    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "Grep::filter" << endl;
2328
2329  if (m_result != "") m_result += " ";
2330  m_result += line;
2331}
2332
2333const cmt_string& Grep::result () const
2334{
2335  return (m_result);
2336}
2337
2338//--------------------------------------------------------------------
2339
2340Cut::Cut (int field)
2341{
2342  m_field = field;
2343}
2344
2345void Cut::begin ()
2346{
2347    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "Cut::begin" << endl;
2348  m_result = "";
2349}
2350
2351void Cut::filter (const cmt_string& line)
2352{
2353    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "Cut::filter" << endl;
2354
2355  static CmtSystem::cmt_string_vector words;
2356 
2357  CmtSystem::split (line, " \t", words);
2358 
2359  if (words.size () <= m_field) return;
2360 
2361  if (m_result != "") m_result += " ";
2362  m_result += words[m_field];
2363}
2364
2365const cmt_string& Cut::result () const
2366{
2367  return (m_result);
2368}
2369
2370//--------------------------------------------------------------------
2371//--------------------------------------------------------------------
2372
2373History& History::instance ()
2374{
2375  static History h;
2376  return (h);
2377}
2378
2379void History::clear ()
2380{
2381  m_installed = "";
2382}
2383
2384void History::install (const cmt_string& line)
2385{
2386  m_installed += "|";
2387  m_installed += line;
2388  m_installed += "|";
2389}
2390
2391bool History::is_installed (const cmt_string& line)
2392{
2393  if (m_installed.find (line) != cmt_string::npos)
2394    {
2395      return (true);
2396    }
2397 
2398  return (false);
2399}
2400
2401History::History ()
2402{
2403}
2404
2405
2406//--------------------------------------------------------------------
2407
2408void RecursivePass1::begin ()
2409{
2410  m_first = true;
2411  m_result = "";
2412}
2413
2414void RecursivePass1::filter (const cmt_string& line)
2415{
2416    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "RecursivePass1::filter> "
2417    //                                          << "line=[" << line << "]" << endl;
2418
2419  if (line.find ("use CMT") != cmt_string::npos) return;
2420  if (line.find ("use cmt") != cmt_string::npos) return;
2421 
2422  History& h = History::instance ();
2423 
2424  if (h.is_installed (line)) return;
2425 
2426  CmtSystem::cmt_string_vector words;
2427 
2428  CmtSystem::split (line, " \t", words);
2429
2430  enum
2431  {
2432    need_package,
2433    need_version,
2434    need_path,
2435    no_need
2436  } state = need_package;
2437
2438  cmt_string package;
2439  cmt_string version;
2440  cmt_string path;
2441
2442  for (int i = 1; i < words.size (); i++)
2443    {
2444      const cmt_string& s = words[i];
2445
2446      if (s[0] == '-') continue;
2447
2448      switch (state)
2449        {
2450          case need_package:
2451            package = s;
2452            state = need_version;
2453            break;
2454          case need_version:
2455            version = s;
2456            state = need_path;
2457            break;
2458          case need_path:
2459            path = s;
2460            state = no_need;
2461            break;
2462        }
2463    }
2464 
2465  if (version.find ("*") != cmt_string::npos)
2466    {
2467        /*
2468      cout << "# ================= Package " << package
2469           << " version " << version << " " << path
2470           << " has wild cards and will not be considered." << endl;
2471        */
2472      return;
2473    }
2474
2475  /**
2476   *  At the first pass, we simply accumulate the not-yet handled
2477   *  use statements.
2478   */
2479 
2480  m_result += line;
2481  m_result += "\n";
2482 
2483  if (m_first)
2484    {
2485      m_first = false;
2486      cout << "  # --> now propagate cmt checkout to :" << endl;
2487    }
2488 
2489  cout << "  #     " << package << " " << version << " " << path << endl;
2490}
2491
2492const cmt_string& RecursivePass1::result () const
2493{
2494  return (m_result);
2495}
2496
2497//--------------------------------------------------------------------
2498
2499RecursivePass2::RecursivePass2 (CvsImplementation& cvs) : m_cvs (cvs)
2500{
2501}
2502
2503void RecursivePass2::begin ()
2504{
2505}
2506
2507void RecursivePass2::filter (const cmt_string& line)
2508{
2509    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "RecursivePass2::filter> "
2510    //                                          << "line=[" << line << "]" << endl;
2511
2512  /**
2513   *   At the second pass, the lines are really handled. Thus
2514   *   the lines are stored into m_installed so as to avoid
2515   *   later on re-installation.
2516   */
2517 
2518  History& h = History::instance ();
2519 
2520  if (h.is_installed (line)) return;
2521 
2522  h.install (line);
2523 
2524  CmtSystem::cmt_string_vector words;
2525 
2526  CmtSystem::split (line, " \t", words);
2527
2528  enum
2529  {
2530    need_package,
2531    need_version,
2532    need_path,
2533    no_need
2534  } state = need_package;
2535
2536  cmt_string package;
2537  cmt_string version;
2538  cmt_string path;
2539
2540  for (int i = 1; i < words.size (); i++)
2541    {
2542      const cmt_string& s = words[i];
2543
2544      if (s[0] == '-') continue;
2545
2546      switch (state)
2547        {
2548          case need_package:
2549            package = s;
2550            state = need_version;
2551            break;
2552          case need_version:
2553            version = s;
2554            state = need_path;
2555            break;
2556          case need_path:
2557            path = s;
2558            state = no_need;
2559            break;
2560        }
2561    }
2562
2563  if (version.find ("*") != cmt_string::npos)
2564    {
2565        /*
2566      cout << "# ================= Package " << package
2567           << " version " << version << " " << path
2568           << " has wild cards and will not be considered." << endl;
2569        */
2570    }
2571  else
2572    {
2573      static const cmt_string empty;
2574      m_cvs.do_checkout_phase2 (path, package, version, empty);
2575    }
2576}
2577
2578//--------------------------------------------------------------------
2579
2580void RecursivePass3::begin ()
2581{
2582  m_first = true;
2583  m_result = "";
2584}
2585
2586void RecursivePass3::filter (const cmt_string& line)
2587{
2588  History& h = History::instance ();
2589 
2590  if (h.is_installed (line)) return;
2591 
2592  CmtSystem::cmt_string_vector words;
2593 
2594  CmtSystem::split (line, " \t", words);
2595
2596  enum
2597  {
2598    need_project,
2599    need_version,
2600    need_tag,
2601    no_need
2602  } state = need_project;
2603
2604  cmt_string project;
2605  cmt_string version;
2606  cmt_string tag;
2607
2608  for (int i = 1; i < words.size (); i++)
2609    {
2610      const cmt_string& s = words[i];
2611
2612      switch (state)
2613        {
2614          case need_project:
2615            project = s;
2616            state = need_version;
2617            break;
2618          case need_version:
2619            version = s;
2620            state = need_tag;
2621            break;
2622          case need_tag:
2623            tag = s;
2624            state = no_need;
2625            break;
2626        }
2627    }
2628 
2629  if (version.find ("*") != cmt_string::npos)
2630    {
2631        /*
2632      cout << "# ================= Project " << project
2633           << " version " << version << " " << path
2634           << " has wild cards and will not be considered." << endl;
2635        */
2636      return;
2637    }
2638
2639  /**
2640   *  At the first pass, we simply accumulate the not-yet handled
2641   *  use statements.
2642   */
2643 
2644  m_result += line;
2645  m_result += "\n";
2646 
2647  if (m_first)
2648    {
2649      m_first = false;
2650      cout << "  # --> now propagate cmt checkout to :" << endl;
2651    }
2652 
2653  cout << "  #     " << project << " " << version << " " << tag << endl;
2654}
2655
2656const cmt_string& RecursivePass3::result () const
2657{
2658  return (m_result);
2659}
2660
2661//--------------------------------------------------------------------
2662
2663RecursivePass4::RecursivePass4 (CvsImplementation& cvs) : m_cvs (cvs)
2664{
2665}
2666
2667void RecursivePass4::begin ()
2668{
2669}
2670
2671void RecursivePass4::filter (const cmt_string& line)
2672{
2673    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "RecursivePass4::filter> "
2674    //                                          << "line=[" << line << "]" << endl;
2675
2676  /**
2677   *   At the second pass, the lines are really handled. Thus
2678   *   the lines are stored into m_installed so as to avoid
2679   *   later on re-installation.
2680   */
2681 
2682  History& h = History::instance ();
2683 
2684  if (h.is_installed (line)) return;
2685 
2686  h.install (line);
2687 
2688  CmtSystem::cmt_string_vector words;
2689 
2690  CmtSystem::split (line, " \t", words);
2691
2692  enum
2693  {
2694    need_project,
2695    need_version,
2696    need_tag,
2697    no_need
2698  } state = need_project;
2699
2700  cmt_string project;
2701  cmt_string version;
2702  cmt_string tag;
2703
2704  for (int i = 1; i < words.size (); i++)
2705    {
2706      const cmt_string& s = words[i];
2707
2708      switch (state)
2709        {
2710          case need_project:
2711            project = s;
2712            state = need_version;
2713            break;
2714          case need_version:
2715            version = s;
2716            state = need_tag;
2717            break;
2718          case need_tag:
2719            tag = s;
2720            state = no_need;
2721            break;
2722        }
2723    }
2724
2725  if (version.find ("*") != cmt_string::npos)
2726    {
2727        /*
2728      cout << "# ================= Project " << project
2729           << " version " << version
2730           << " has wild cards and will not be considered." << endl;
2731        */
2732    }
2733  else
2734    {
2735      static const cmt_string empty;
2736      m_cvs.do_checkout_phase2 (empty, project, version, tag);
2737    }
2738}
2739
2740//--------------------------------------------------------------------
2741
2742void Cvs::tags (const CmtSystem::cmt_string_vector& arguments)
2743{
2744  CvsImplementation cvs;
2745
2746  cvs.tags (arguments);
2747}
2748
2749void Cvs::branches (const cmt_string& module)
2750{
2751  CvsImplementation cvs;
2752
2753  cvs.branches (module);
2754}
2755
2756void Cvs::subpackages (const cmt_string& module)
2757{
2758  CvsImplementation cvs;
2759
2760  cvs.subpackages (module);
2761}
2762
2763void Cvs::subprojects (const cmt_string& module)
2764{
2765  CvsImplementation cvs;
2766
2767  cvs.subprojects (module);
2768}
2769
2770void Cvs::checkout (const CmtSystem::cmt_string_vector& arguments)
2771{
2772  CvsImplementation cvs;
2773
2774  cvs.checkout (arguments);
2775}
2776
Note: See TracBrowser for help on using the repository browser.