source: CMT/v1r18p20060301/source/cmt_cvs.cxx @ 597

Last change on this file since 597 was 144, checked in by arnault, 18 years ago

Various fixes and message cleanups see CL 294 and 295

  • Property svn:eol-style set to native
File size: 58.9 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 = Symbol::get_env_value ("CMTPATH");
1021
1022    if (cmtpath.find (dir) == cmt_string::npos)
1023      {
1024        CMTPATH = dir;
1025        CMTPATH += ":";
1026        CMTPATH += cmtpath;
1027
1028        CmtSystem::putenv ("CMTPATH", CMTPATH);
1029      }
1030
1031    if (m_verbose)
1032      {
1033        cout << "#CMT> CMTPATH=" << CmtSystem::getenv ("CMTPATH") << endl;
1034      }
1035  }
1036
1037  /**
1038     Construct CVS management files in the top directory. This is needed
1039     if the top directory of a product is empty. (In this case the
1040     co -l results in nothing)
1041   */
1042  void make_management_files (const cmt_string& module,
1043                              const cmt_string& entries_text)
1044  {
1045    if (!CmtSystem::test_directory ("CVS"))
1046      {
1047        /**
1048         * The CVS repository had not been created (this is generally
1049         * due to the lack of top files)
1050         */
1051
1052        if (!mkdir ("CVS")) return;
1053
1054        CmtSystem::cd ("..");
1055
1056        cmt_string s;
1057       
1058        // Let's create first the CVS/Root file.
1059       
1060        CmtSystem::get_cvsroot (s);
1061        s += "\n";
1062       
1063        cmt_string f;
1064       
1065        f = "CVS";
1066        f += CmtSystem::file_separator ();
1067        f += "Root";
1068       
1069        if (m_simulation)
1070          {
1071            cout << "#CMT> Would fill in the CVS/Root file with " << endl;
1072            cout << s << endl;
1073          }
1074        else
1075          {
1076            if (m_verbose)
1077              {
1078                cout << "#CMT> Fill in the CVS/Root file with " << endl;
1079                cout << s << endl;
1080              }
1081            s.write (f);
1082          }
1083       
1084        // Now we create the CVS/Repository file
1085       
1086        f = "CVS";
1087        f += CmtSystem::file_separator ();
1088        f += "Repository";
1089       
1090        CmtSystem::get_cvsroot (s);
1091        if (s[0] == ':')
1092          {
1093            int pos = s.find (1, ":");
1094            s.erase (0, pos+1);
1095            pos = s.find (0, ":");
1096            s.erase (0, pos+1);
1097          }
1098        s += "/";
1099        s += module;
1100        s += "\n";
1101       
1102        if (m_simulation)
1103          {
1104            cout << "#CMT> Would fill in the CVS/Repository file with " << endl;
1105            cout << s << endl;
1106          }
1107        else
1108          {
1109            if (m_verbose)
1110              {
1111                cout << "#CMT> Fill in the CVS/Repository file with " << endl;
1112                cout << s << endl;
1113              }
1114            s.write (f);
1115          }
1116      }
1117   
1118    if (m_simulation)
1119      {
1120        cout << "#CMT> Would write the top CVS/Entries file with " << endl;
1121        cout << entries_text << endl;
1122      }
1123    else
1124      {
1125        cmt_string entries_file_name;
1126
1127        entries_file_name = "CVS";
1128        entries_file_name += CmtSystem::file_separator ();
1129        entries_file_name += "Entries";
1130   
1131        cmt_string text;
1132
1133        if (!text.read (entries_file_name))
1134          {
1135            // This happens when there were no top files
1136          }
1137
1138        text += entries_text;
1139
1140        // Now the CVS/Entries is ready to be created.
1141        if (m_verbose)
1142          {
1143            cout << "#CMT> Fill in the top CVS/Entries file with " << endl;
1144            cout << text << endl;
1145          }
1146
1147        text.write (entries_file_name);
1148      }
1149
1150  }
1151
1152  /**
1153     Specific checkout of one project
1154   */
1155  bool really_checkout_project_contents (const cmt_string& offset,
1156                                         const cmt_string& project,
1157                                         const cmt_string& version,
1158                                         const cmt_string& tag,
1159                                         const cmt_string& module,
1160                                         const cmt_string& basedir,
1161                                         bool at_head,
1162                                         const cmt_string& currentdir)
1163  {
1164    cmt_string dir = currentdir;
1165
1166    cout << "  # get project files into " << dir << endl;
1167
1168    cmt_string version_dir = version;
1169
1170    if (!mkdir (version_dir)) return (false);
1171
1172    dir += CmtSystem::file_separator ();
1173    dir += version_dir;
1174   
1175    cmt_string command = "cvs -Q co -P ";
1176    if (!at_head)
1177      {
1178        command += "-r ";
1179        command += (tag != "") ? tag : version;
1180      }
1181
1182    command += " -d cmt ";
1183
1184    command += " ";
1185    command += module;
1186    command += "/cmt";
1187
1188    execute_and_retry (command, "Error getting project CMT contents");
1189
1190    make_management_files (module, "D/cmt////\n");
1191
1192    return (true);
1193  }
1194
1195  /**
1196     Specific checkout of one package
1197
1198     1) get top files (no directories)
1199     2) construct the directory structure (with or without version directory)
1200     3) build the CVS/Entries file for subdirs and co individual subdirs
1201     4) write the CVS management files if CVS did not do it.
1202   */
1203  bool really_checkout_package_contents (const cmt_string& offset,
1204                                         const cmt_string& package,
1205                                         const cmt_string& version,
1206                                         const cmt_string& module,
1207                                         const cmt_string& basedir,
1208                                         bool at_head,
1209                                         const cmt_string& currentdir)
1210  {
1211    cmt_string dir = currentdir;
1212
1213    cout << "  # get top files " << endl;
1214           
1215    cmt_string command = "cvs -Q co -P -l ";
1216    if (!at_head)
1217      {
1218        command += "-r ";
1219        command += version;
1220      }
1221
1222    bool need_version = do_need_version ();
1223
1224    if (need_version)
1225      {
1226        command += " -d ";
1227        command += version;
1228      }
1229    else
1230      {
1231        command += " -d ";
1232        command += package;
1233
1234        // Must stand just above the package directory
1235        CmtSystem::cd ("..");
1236        CmtSystem::dirname (dir, dir);
1237      }
1238   
1239    command += " ";
1240    command += module;
1241   
1242    execute_and_retry (command, "Error getting package CMT contents");
1243
1244    if (need_version)
1245      {
1246        if (!mkdir (version)) return (false);
1247       
1248        dir += CmtSystem::file_separator ();
1249        dir += version;
1250      }
1251    else
1252      {
1253        if (!mkdir (package)) return (false);
1254       
1255        dir += CmtSystem::file_separator ();
1256        dir += package;
1257      }
1258   
1259    if (m_verbose)
1260      {
1261        cout << "#CMT> Now getting subdirectories pwd=" << CmtSystem::pwd () << " dir=" << dir << endl;
1262      }
1263
1264    cmt_string branches = CmtSystem::getenv ("CMTCVSBRANCHES");
1265   
1266    if (branches == "")
1267      {
1268        branches = branches_info;
1269      }
1270   
1271    CmtSystem::cmt_string_vector branch_vector;
1272   
1273    CmtSystem::split (branches, " \t", branch_vector);
1274   
1275    cout << "  # get branches " << branches << endl;
1276   
1277    cmt_string text = "";
1278   
1279    command = "";
1280   
1281    int i;
1282   
1283    for (i = 0; i < branch_vector.size (); i++)
1284      {
1285        cmt_string& branch = branch_vector[i];
1286       
1287        if (i > 0)
1288          {
1289            command += CmtSystem::command_separator ();
1290          }
1291       
1292        command += "cvs -Q co ";
1293       
1294        if (!at_head)
1295          {
1296            command += "-r ";
1297            command += version;
1298          }
1299       
1300        command += " -d ";
1301        command += branch;
1302        command += " ";
1303        command += module;
1304        command += "/";    // CVS uses the '/' notation on all platforms!!
1305        command += branch;
1306       
1307        text += "D/";
1308        text += branch;
1309        text += "////\n";
1310      }
1311   
1312    execute_and_retry (command, "Error getting package contents");
1313
1314    make_management_files (module, text);
1315
1316    return (true);
1317  }
1318
1319  /**
1320     Effective checkout of a package or a project
1321   */
1322  bool really_checkout (const cmt_string& offset,
1323                        const cmt_string& product,
1324                        const cmt_string& version,
1325                        const cmt_string& tag,
1326                        const cmt_string& module,
1327                        const cmt_string& basedir,
1328                        bool at_head)
1329  {
1330    cmt_string dir = basedir;
1331    cmt_string out;
1332   
1333    cout << "# ================= working on " << structure_info << " " << product
1334         << " version " << version;
1335
1336    if (at_head) cout << " (At head) ";
1337
1338    cmt_string full_offset;
1339
1340    full_offset = m_cvs_offset;
1341    full_offset += offset;
1342
1343    cmt_string echo_ppath;
1344       
1345    if (offset != "")
1346      {
1347        echo_ppath = " path ";
1348        echo_ppath += offset;
1349      }
1350   
1351    cout << echo_ppath << " in " << dir << endl;
1352
1353    if (do_need_version ())
1354      {
1355        // Move back to the product name.
1356        CmtSystem::dirname (dir, dir);
1357      }
1358
1359    if (!mkdir (dir)) return (false);
1360       
1361    if (structure_info == "package")
1362      {
1363        really_checkout_package_contents (offset,
1364                                          product,
1365                                          version,
1366                                          module,
1367                                          basedir,
1368                                          at_head,
1369                                          dir);
1370      }
1371    else if (structure_info == "project")
1372      {
1373        really_checkout_project_contents (offset,
1374                                          product,
1375                                          version,
1376                                          tag,
1377                                          module,
1378                                          basedir,
1379                                          at_head,
1380                                          dir);
1381      }
1382
1383    return (true);
1384  }
1385
1386  /**
1387     Find the most appropriate effective version directory corresponding to the
1388     specified version expression.
1389     The specified expression may contain wildcards (in the file manager sense). This
1390     FME is first converted into a RE then a directory search is performed.
1391     An empty string is returned if no match is found.
1392   */
1393  cmt_string find_matching_version (const cmt_string& expression)
1394  {
1395    cmt_string result;
1396
1397    //
1398    // Here expression takes the form
1399    //   <some path>/<expression with wild-card>
1400    //
1401
1402    cmt_string dir;
1403    CmtSystem::dirname (expression, dir);
1404    dir += CmtSystem::file_separator ();
1405   
1406    cmt_string version;
1407    CmtSystem::basename (expression, version);
1408
1409    if (version.find ("*") == cmt_string::npos)
1410      {
1411        // there is no wildcarding here. A simple test is enough.
1412        if (CmtSystem::test_directory (expression))
1413          {
1414            result = version;
1415          }
1416      }
1417    else
1418      {
1419        version.replace ("*", ".*");
1420       
1421        cmt_regexp exp (version);
1422       
1423        CmtSystem::cmt_string_vector list;
1424       
1425        CmtSystem::scan_dir (dir, exp, list);
1426       
1427        if (list.size () > 0)
1428          {
1429            result = list[0];
1430           
1431            CmtSystem::basename (result, result);
1432          }
1433      }
1434   
1435    return (result);
1436  }
1437 
1438  /**
1439   *   We provide a path to a requirements file. From it we read the use
1440   *  statements, and we try to checkout the corresponding packages.
1441   *
1442   *   A boolean return tells if any recursion occurred.
1443   */
1444  bool checkout_from_requirements (const cmt_string& requirements_path)
1445  {
1446    static cmt_regexp expression ("^[ \t]*use[ \t]");
1447
1448    cmt_string text;
1449
1450    text.read (requirements_path);
1451
1452    RecursivePass1 p1;
1453    p1.run (text, expression);
1454
1455    bool result = (p1.result () != "");
1456    RecursivePass2 p2 (*this);
1457    p2.run (p1.result ());
1458
1459    return (result);
1460  }
1461
1462  /**
1463   *   We provide a path to a project file. From it we read the use
1464   *  statements, and we try to checkout the corresponding projects.
1465   */
1466  void checkout_from_project_file (const cmt_string& file_name)
1467  {
1468    static cmt_regexp expression ("^[ \t]*use[ \t]");
1469
1470    cmt_string text;
1471
1472    text.read (file_name);
1473
1474    CvsImplementation& me = *this;
1475    CvsImplementation saved;
1476    saved = me;
1477    cmt_string here = CmtSystem::pwd ();
1478
1479    RecursivePass3 p3;
1480    p3.run (text, expression);
1481
1482    RecursivePass4 p4 (*this);
1483    p4.run (p3.result ());
1484
1485    Grep grep;
1486
1487    grep.run (text, "container");
1488    cmt_string container = grep.result ();
1489
1490    if (container != "")
1491      {
1492        static cmt_regexp container_expression ("^[ \t]*container[ \t]");
1493
1494        add_cmtpath (here);
1495
1496        cout << "  # --> now getting project packages from the " << container << " " << here << endl;
1497
1498        CmtSystem::cd (here);
1499
1500        RecursivePass1 p1;
1501        p1.run (text, container_expression);
1502
1503        RecursivePass2 p2 (*this);
1504
1505        m_home_dir = CmtSystem::pwd ();
1506        p2.run (p1.result ());
1507      }
1508
1509    CmtSystem::cd (here);
1510    me = saved;
1511  }
1512
1513  /**
1514     Check whether a given directory structure matches an expected product structure
1515     given by the structure info obtained from the most recent request to the CVS pluggin
1516   */
1517  bool check_structure (const cmt_string& dir)
1518  {
1519    bool result = false;
1520
1521    if (!CmtSystem::test_directory (dir))
1522      {
1523        return (false);
1524      }
1525
1526    if (structure_info == "package")
1527      {
1528        // Check if it is a true CMT package.
1529       
1530        cmt_string file_name;
1531       
1532        file_name = dir;
1533        file_name += CmtSystem::file_separator ();
1534        file_name += "cmt";
1535        file_name += CmtSystem::file_separator ();
1536        file_name += "requirements";
1537       
1538        if (CmtSystem::test_file (file_name))
1539          {
1540            result = true;
1541          }
1542        else
1543          {
1544            file_name = dir;
1545            file_name += CmtSystem::file_separator ();
1546            file_name += "mgr";
1547            file_name += CmtSystem::file_separator ();
1548            file_name += "requirements";
1549           
1550            if (CmtSystem::test_file (file_name))
1551              {
1552                result = true;
1553              }
1554          }
1555      }
1556    else if (structure_info == "project")
1557      {
1558        cmt_string file_name;
1559       
1560        file_name = dir;
1561        file_name += CmtSystem::file_separator ();
1562        file_name += "cmt";
1563        file_name += CmtSystem::file_separator ();
1564        file_name += "project.cmt";
1565       
1566        if (CmtSystem::test_file (file_name))
1567          {
1568            result = true;
1569          }
1570      }
1571
1572    return (result);
1573  }
1574
1575  /**
1576     Internal call from the initial do_checkout or from recursive passes
1577     Prepare the directory structure for the checkout
1578     Do the checkout
1579     Check if everything was done properly, if a package or a project has been created
1580     If needed recurse from the requirements or project file
1581
1582     For projects there may be two different specifications for
1583     - the version directory
1584     - the CVS tag
1585   */
1586  void do_checkout_phase2 (const cmt_string& offset,
1587                           const cmt_string& product,
1588                           const cmt_string& specified_version,
1589                           const cmt_string& tag)
1590  {
1591    if (m_verbose)
1592      {
1593        cout << "#CMT> do_checkout_phase2> offset=" << offset
1594             << " " << structure_info << "=" << product
1595             << " specified_version=" << specified_version
1596             << " tag=" << tag
1597             << " pwd=" << CmtSystem::pwd ()
1598             << endl;
1599      }
1600   
1601    cmt_string version = specified_version;
1602    cmt_string empty;
1603    cmt_string full_offset;
1604
1605    full_offset = m_cvs_offset;
1606    full_offset += offset;
1607   
1608    cmt_string echo_ppath;
1609   
1610    if (offset != "")
1611      {
1612        echo_ppath = " path ";
1613        echo_ppath += offset;
1614      }
1615   
1616    if (version == "")
1617      {
1618        cout << "# ================= No version specified for " << structure_info << " " << product << endl;
1619        return;
1620      }
1621   
1622    //
1623    //  First make an attempt to locate the specified version of
1624    //  this product "as-it-is" in the work area.
1625    //   Since 'version' may contain wild-card, it's likely that
1626    //  we should not simply use CmtSystem::test_directory but
1627    //  use the wild-card search.
1628    //
1629   
1630    cmt_string dir;
1631   
1632    dir = build_version_directory (offset, product, version);
1633   
1634    bool recursive = m_recursive;
1635   
1636    cmt_string effective_version = find_matching_version (dir);
1637   
1638    cmt_string module;
1639    get_module (full_offset, product, module);
1640
1641    cmt_string cvs_tag = (tag != "") ? tag : version;
1642    bool at_head = false;
1643
1644    if (effective_version != "")
1645      {
1646        version = effective_version;
1647      }
1648    else
1649      {
1650        //
1651        // get_version attempts to find the most appropriate version
1652        // tag matching the specification FROM the repository. However,
1653        // we should take into account situations where some versions have
1654        // already been checked out, in which case they might be sufficient
1655        // (or preferred?)
1656        //
1657       
1658        if (cvs_tag.find ("*") != cmt_string::npos)
1659          {
1660            cout << "# ================= " << structure_info << " " << product
1661                 << " version " << cvs_tag << echo_ppath
1662                 << " has wild cards and will not be considered." << endl;
1663            return;
1664          }
1665       
1666        if (!get_version (full_offset, product, cvs_tag, module,
1667                          cvs_tag, at_head))
1668          {
1669            return;
1670          }
1671       
1672        if (m_head)
1673          {
1674            m_head = false;
1675           
1676            at_head = true;
1677          }
1678        else
1679          {
1680            at_head = false;
1681          }
1682       
1683        //
1684        // Make a second try after having selected a CVS tag from all the
1685        // available tags compatible with the specified version
1686        //
1687
1688        if (tag == "")
1689          {
1690            // If tag was not specified, then the version directory has to match the CVS tag
1691            // Otherwise the original version specification is kept for the directory.
1692
1693            version = cvs_tag;
1694            dir = build_version_directory (offset, product, version);
1695          }
1696      }
1697   
1698
1699    if (check_structure (dir))
1700      {
1701        cout << "# ================= " << structure_info << " " << product
1702             << " version " << version << echo_ppath
1703             << " already installed." << endl;
1704       
1705        recursive = false;
1706      }
1707    else
1708      {
1709        //
1710        // Now we can say that we have to perform the real checkout.
1711        //
1712       
1713        if (!really_checkout (offset, product, version, cvs_tag, module, dir, at_head))
1714          {
1715            cout << "# bad return from really_checkout_product" << endl;
1716            return;
1717          }
1718      }
1719
1720    //
1721    //  Now reach the newly checked out product.
1722    //
1723   
1724    if (m_simulation)
1725      {
1726        cout << "#CMT> " << structure_info << " directory not really created " << dir << endl;
1727      }
1728    else if (structure_info == "package")
1729      {
1730        if (!CmtSystem::cd (dir))
1731          {
1732            cout << "#CMT> Package directory not created " << dir << endl;
1733            return;
1734          }
1735       
1736        // Check if it is a true CMT package.
1737       
1738        cmt_string file_name;
1739       
1740        file_name = "cmt";
1741        file_name += CmtSystem::file_separator ();
1742        file_name += "requirements";
1743       
1744        if (CmtSystem::test_file (file_name))
1745          {
1746            dir += CmtSystem::file_separator ();
1747            dir += "cmt";
1748            CmtSystem::cd ("cmt");
1749           
1750            if (!do_need_version ())
1751              {
1752                cmt_string text = version;
1753                text += "\n";
1754                text.write ("version.cmt");
1755              }
1756          }
1757        else
1758          {
1759            file_name = "mgr";
1760            file_name += CmtSystem::file_separator ();
1761            file_name += "requirements";
1762           
1763            if (CmtSystem::test_file (file_name))
1764              {
1765                dir += CmtSystem::file_separator ();
1766                dir += "mgr";
1767                CmtSystem::cd ("mgr");
1768              }
1769            else
1770              {
1771                cout << "# " << product << " not a CMT package" << endl;
1772                return;
1773              }
1774          }
1775       
1776        if (recursive)
1777          {
1778            cmt_string here = CmtSystem::pwd ();
1779
1780            bool did_recurse = checkout_from_requirements ("requirements");
1781
1782            CmtSystem::cd (here);
1783
1784            if (did_recurse) 
1785              {
1786                execute ("cmt -quiet broadcast cmt -quiet config");
1787              }
1788            else
1789              {
1790                execute ("cmt -quiet config");
1791              }
1792          }
1793        else
1794          {
1795            execute ("cmt -quiet config");
1796          }
1797      }
1798    else if (structure_info == "project")
1799      {
1800       
1801        if (m_verbose)
1802          {
1803            cout << "#CMT> dir=" << dir << endl;
1804          }
1805       
1806        if (!CmtSystem::cd (dir))
1807          {
1808            cout << "#CMT> Project directory not created " << dir << endl;
1809            return;
1810          }
1811       
1812        cmt_string file_name;
1813       
1814        file_name = "cmt";
1815        file_name += CmtSystem::file_separator ();
1816        file_name += "project.cmt";
1817       
1818        if (!CmtSystem::test_file (file_name))
1819          {
1820            cout << "# " << product << " not a CMT project" << endl;
1821            return;
1822          }
1823       
1824        if (recursive)
1825          {
1826            checkout_from_project_file (file_name);
1827          }
1828
1829        cout << "# ================= Project " << product << " completed" << endl;
1830
1831      }
1832  }
1833 
1834  /**
1835     Top level of the checkout operation, initiated from the command line arguments
1836
1837     Construct an history of the checkouts to avoid duplicating the
1838     checkouts during the recursivity
1839
1840     Eventually
1841     o perform the cmt config for packages.
1842
1843   */
1844  void do_checkout_phase1 (const cmt_string& module, 
1845                           const cmt_string& version_dir, 
1846                           const cmt_string& version_tag)
1847  {
1848    add_cmtpath (m_home_dir);
1849
1850    History& h = History::instance ();
1851
1852    h.clear ();
1853
1854    if (module == "")
1855      {
1856        if (m_verbose)
1857          {
1858            cout << "#CMT> Missing module name" << endl;
1859          }
1860        return;
1861      }
1862
1863    cmt_string offset;
1864    cmt_string product;
1865    cmt_string version;
1866    cmt_string tag;
1867   
1868    {
1869      cmt_string m;
1870      m = m_cvs_offset;
1871      m += module;
1872       
1873      get_cvs_infos_with_offset (m);
1874       
1875      if (error_info != "")
1876        {
1877          cout << error_info << endl;
1878          return;
1879        }
1880    }
1881
1882    if (version_tag == "")
1883      {
1884        Cut cut (0);
1885       
1886        if (tags_top_info != "") tag = tags_top_info;
1887        else tag = tags_info;
1888       
1889        cut.run (tag);
1890       
1891        tag = cut.result ();
1892      }
1893    else
1894      {
1895        tag = version_tag;
1896      }
1897
1898    version = (version_dir == "") ? tag : version_dir;
1899
1900    CmtSystem::dirname (module, offset);
1901    CmtSystem::basename (module, product);
1902   
1903    cmt_string top_dir;
1904       
1905    top_dir = m_home_dir;
1906    top_dir += CmtSystem::file_separator ();
1907    top_dir += m_checkout_dir;
1908    top_dir += CmtSystem::file_separator ();
1909    top_dir += offset;
1910    top_dir += CmtSystem::file_separator ();
1911    top_dir += product;
1912    top_dir += CmtSystem::file_separator ();
1913    top_dir += version;
1914
1915    CmtSystem::reduce_file_separators (top_dir);
1916
1917    if (m_verbose)
1918      {
1919        cout << "#CMT> about to checkout " << structure_info
1920             << " " << product << " version " << version << " into " << top_dir << endl;
1921      }
1922
1923    static const cmt_string empty;
1924    do_checkout_phase2 (offset, product, version, tag);
1925
1926    if (m_simulation) return;
1927
1928    if (!CmtSystem::cd (top_dir)) return;
1929
1930    if (structure_info == "project")
1931      {
1932        cmt_string file_name;
1933   
1934        file_name = "cmt";
1935        file_name += CmtSystem::file_separator ();
1936        file_name += "project.cmt";
1937       
1938        if (!CmtSystem::test_file (file_name))
1939          {
1940            cout << "# " << product << " was not properly checked out and is missing its cmt/project.cmt file" << endl;
1941            return;
1942          }
1943      }
1944    else
1945      {
1946        cmt_string file_name;
1947   
1948        file_name = "cmt";
1949        file_name += CmtSystem::file_separator ();
1950        file_name += "requirements";
1951       
1952        if (CmtSystem::test_file (file_name))
1953          {
1954            top_dir += CmtSystem::file_separator ();
1955            top_dir += "cmt";
1956            CmtSystem::cd ("cmt");
1957          }
1958        else
1959          {
1960            file_name = "mgr";
1961            file_name += CmtSystem::file_separator ();
1962            file_name += "requirements";
1963           
1964            if (CmtSystem::test_file (file_name))
1965              {
1966                top_dir += CmtSystem::file_separator ();
1967                top_dir += "mgr";
1968                CmtSystem::cd ("mgr");
1969              }
1970            else
1971              {
1972                cout << "# " << product << " was not properly checked out and is missing its cmt/requirements file" << endl;
1973                return;
1974              }
1975          }
1976       
1977        if (m_verbose)
1978          {
1979            cout << "#CMT> product " << product << " has been checked out" << endl;
1980          }
1981       
1982        if (!m_recursive)
1983          {
1984            execute ("cmt -quiet config");
1985          }
1986      }
1987  }
1988
1989  void help ()
1990  {
1991    cout << "> cd <some work area>" << endl;
1992    cout << "> cmt checkout [modifier ...] <package|project>" << endl;
1993    cout << "" << endl;
1994    cout << "   modifier :" << endl;
1995    cout << "   -l        Do not process used packages (default)." << endl;
1996    cout << "   -R        Process used products recursively." << endl;
1997    cout << "   -r rev    Check out version tag. (is sticky)" << endl;
1998    cout << "   -vd dir   Use this version directory instead of CVS tag." << endl;
1999    cout << "   -d dir    Check out into dir instead of module name." << endl;
2000    cout << "   -o offset Offset in the CVS repository" << endl;
2001    cout << "   -requirements <requirements file path>  Check out packages referenced in this requirements file" << endl;
2002    cout << "   -n        simulation mode on" << endl;
2003    cout << "   -v        verbose mode on" << endl;
2004    cout << "   --help    print this help" << endl;
2005    cout << "" << endl;
2006    cout << "> cmt cvstags <package|project>" << endl;
2007    cout << "> cmt cvssubpackages <directory>" << endl;
2008    cout << "> cmt cvssubprojects <directory>" << endl;
2009    cout << "" << endl;
2010  }
2011
2012  /**
2013     Implementation of the cmt cvstags
2014     Get the CVS tags of a module
2015   */
2016  void tags (const CmtSystem::cmt_string_vector& arguments)
2017  {
2018    if (arguments.size () < 1)
2019      {
2020        help ();
2021        return;
2022      }
2023   
2024    if (CmtSystem::getenv ("CVSROOT") == "")
2025      {
2026        cout << "# Please set CVSROOT first !" << endl;
2027        return;
2028      }
2029   
2030    m_cvs_offset = CmtSystem::getenv ("CMTCVSOFFSET");
2031    if (m_cvs_offset != "") 
2032      {
2033        m_cvs_offset += "/";
2034        m_cvs_offset.replace_all ("//", "/");
2035      }
2036   
2037    bool all = false;
2038   
2039    for (int arg = 0; arg < arguments.size (); arg++)
2040      {
2041        const cmt_string& option = arguments[arg];
2042       
2043        if (option == "-all")
2044          {
2045            all = true;
2046          }
2047        else
2048          {
2049            get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), option);
2050           
2051            if (error_info != "")
2052              {
2053                cout << error_info << endl;
2054              }
2055            else
2056              {
2057                cmt_string tags;
2058               
2059                if (all)
2060                  {
2061                    tags = cvsversions_top_info;
2062                    tags += " ";
2063                    tags += cvsversions_info;
2064                  }
2065                else
2066                  {
2067                    tags = tags_top_info;
2068                    tags += " ";
2069                    tags += tags_info;
2070                  }
2071               
2072                CmtSystem::cmt_string_vector v;
2073               
2074                CmtSystem::split (tags, " \t", v);
2075                for (int i = 0; i < v.size (); i++)
2076                  {
2077                    const cmt_string& s = v[i];
2078                    cout << s << endl;
2079                  }
2080              }
2081          }
2082      }
2083  }
2084
2085  /**
2086     Implementation of the cmt cvsbranches
2087     Get the subdirs of a module that are not subackages
2088   */
2089  void branches (const cmt_string& module)
2090  {
2091    cmt_string out;
2092   
2093    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), module);
2094   
2095    if (error_info != "")
2096      {
2097        cout << error_info << endl;
2098      }
2099    else
2100      {
2101        cout << branches_info << endl;
2102      }
2103  }
2104
2105  /**
2106     Implementation of the cmt cvssubpackages
2107     Get the subdirs of a module that ARE CMT subpackages
2108   */
2109  void subpackages (const cmt_string& module)
2110  {
2111    cmt_string out;
2112   
2113    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), (module == "") ? "." : module);
2114   
2115    if (error_info != "")
2116      {
2117        cout << error_info << endl;
2118      }
2119    else
2120      {
2121        cout << subpackages_info << endl;
2122      }
2123  }
2124 
2125  /**
2126     Implementation of the cmt cvssubrojects
2127     Get the subdirs of a module that ARE CMT projects
2128   */
2129  void subprojects (const cmt_string& module)
2130  {
2131    cmt_string out;
2132   
2133    get_cvs_infos (CmtSystem::getenv ("CMTCVSOFFSET"), (module == "") ? "." : module);
2134   
2135    if (error_info != "")
2136      {
2137        cout << error_info << endl;
2138      }
2139    else
2140      {
2141        cout << subprojects_info << endl;
2142      }
2143  }
2144
2145  /**
2146     Implementation of the cmt checkout
2147     Parse the arguments
2148     Then call do_checkout for each argument
2149   */
2150  void checkout (const CmtSystem::cmt_string_vector& arguments)
2151  {
2152    if (arguments.size () < 1)
2153      {
2154        help ();
2155        return;
2156      }
2157   
2158    if (CmtSystem::getenv ("CVSROOT") == "")
2159      {
2160        cout << "# Please set CVSROOT first !" << endl;
2161        return;
2162      }
2163   
2164    m_home_dir = CmtSystem::pwd ();
2165    m_checkout_dir = "";
2166    m_version_dir = "";
2167    m_cvs_offset = "";
2168
2169    cmt_string module;
2170   
2171    m_recursive = false;
2172   
2173    bool need_version_tag = false;
2174    cmt_string version_tag;
2175   
2176    bool need_checkout_dir = false;
2177    bool need_cvs_offset = false;
2178    bool need_requirements_file = false;
2179    bool need_version_dir = false;
2180   
2181    m_simulation = false;
2182    //m_verbose = true;
2183    m_verbose = false;
2184   
2185    m_head = true;
2186   
2187    m_cvs_offset = CmtSystem::getenv ("CMTCVSOFFSET");
2188    if (m_cvs_offset != "") 
2189      {
2190        m_cvs_offset += "/";
2191        m_cvs_offset.replace_all ("//", "/");
2192      }
2193   
2194    for (int arg = 0; arg < arguments.size (); arg++)
2195      {
2196        const cmt_string& option = arguments[arg];
2197       
2198        if (need_version_tag)
2199          {
2200            need_version_tag = false;
2201           
2202            if (option == "HEAD")
2203              {
2204                m_head = true;
2205              }
2206            else
2207              {
2208                version_tag = option;
2209              }
2210          }
2211        else if (need_checkout_dir)
2212          {
2213            need_checkout_dir = false;
2214            m_checkout_dir = option;
2215          }
2216        else if (need_version_dir)
2217          {
2218            need_version_dir = false;
2219            m_version_dir = option;
2220          }
2221        else if (need_cvs_offset)
2222          {
2223            need_cvs_offset = false;
2224            m_cvs_offset = option;
2225            m_cvs_offset += '/';
2226            m_cvs_offset.replace_all ("//", "/");
2227          }
2228        else if (need_requirements_file)
2229          {
2230            need_requirements_file = false;
2231            m_head = false;
2232            checkout_from_requirements (option);
2233          }
2234        else
2235          {
2236            if (option == "-R")
2237              {
2238                m_recursive = true;
2239              }
2240            else if (option == "-l")
2241              {
2242                m_recursive = false;
2243              }
2244            else if (option == "-r")
2245              {
2246                need_version_tag = true;
2247                m_head = false;
2248              }
2249            else if (option == "-d")
2250              {
2251                need_checkout_dir = true;
2252              }
2253            else if (option == "-o")
2254              {
2255                need_cvs_offset = true;
2256              }
2257            else if (option == "-n")
2258              {
2259                m_simulation = true;
2260              }
2261            else if (option == "-v")
2262              {
2263                m_verbose = true;
2264              }
2265            else if (option == "-vd")
2266              {
2267                need_version_dir = true;
2268              }
2269            else if (option == "-requirements")
2270              {
2271                need_requirements_file = true;
2272              }
2273            else if (option == "--help")
2274              {
2275                help ();
2276                return;
2277              }
2278            else if (option[0] == '-')
2279              {
2280                help ();
2281                return;
2282              }
2283            else
2284              {
2285                do_checkout_phase1 (option, m_version_dir, version_tag);
2286              }
2287          }
2288      }
2289  }
2290
2291private:
2292
2293  bool m_recursive;
2294  bool m_head;
2295  bool m_verbose;
2296  bool m_simulation;
2297
2298  cmt_string m_home_dir;
2299  cmt_string m_checkout_dir;
2300  cmt_string m_version_dir;
2301  cmt_string m_cvs_offset;
2302
2303  cmt_string m_protocol_level;
2304  cmt_string m_last_module;
2305  cmt_string m_last_cvs_infos;
2306  cmt_string structure_info;
2307  cmt_string error_info;
2308  cmt_string tags_top_info;
2309  cmt_string tags_info;
2310  cmt_string cvsversions_top_info;
2311  cmt_string cvsversions_info;
2312  cmt_string branches_info;
2313  cmt_string subpackages_info;
2314  cmt_string subprojects_info;
2315};
2316
2317//--------------------------------------------------------------------
2318
2319void Grep::begin ()
2320{
2321  m_result = "";
2322}
2323
2324void Grep::filter (const cmt_string& line)
2325{
2326    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "Grep::filter" << endl;
2327
2328  if (m_result != "") m_result += " ";
2329  m_result += line;
2330}
2331
2332const cmt_string& Grep::result () const
2333{
2334  return (m_result);
2335}
2336
2337//--------------------------------------------------------------------
2338
2339Cut::Cut (int field)
2340{
2341  m_field = field;
2342}
2343
2344void Cut::begin ()
2345{
2346    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "Cut::begin" << endl;
2347  m_result = "";
2348}
2349
2350void Cut::filter (const cmt_string& line)
2351{
2352    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "Cut::filter" << endl;
2353
2354  static CmtSystem::cmt_string_vector words;
2355 
2356  CmtSystem::split (line, " \t", words);
2357 
2358  if (words.size () <= m_field) return;
2359 
2360  if (m_result != "") m_result += " ";
2361  m_result += words[m_field];
2362}
2363
2364const cmt_string& Cut::result () const
2365{
2366  return (m_result);
2367}
2368
2369//--------------------------------------------------------------------
2370//--------------------------------------------------------------------
2371
2372History& History::instance ()
2373{
2374  static History h;
2375  return (h);
2376}
2377
2378void History::clear ()
2379{
2380  m_installed = "";
2381}
2382
2383void History::install (const cmt_string& line)
2384{
2385  m_installed += "|";
2386  m_installed += line;
2387  m_installed += "|";
2388}
2389
2390bool History::is_installed (const cmt_string& line)
2391{
2392  if (m_installed.find (line) != cmt_string::npos)
2393    {
2394      return (true);
2395    }
2396 
2397  return (false);
2398}
2399
2400History::History ()
2401{
2402}
2403
2404
2405//--------------------------------------------------------------------
2406
2407void RecursivePass1::begin ()
2408{
2409  m_first = true;
2410  m_result = "";
2411}
2412
2413void RecursivePass1::filter (const cmt_string& line)
2414{
2415    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "RecursivePass1::filter> "
2416    //                                          << "line=[" << line << "]" << endl;
2417
2418  if (line.find ("use CMT") != cmt_string::npos) return;
2419  if (line.find ("use cmt") != cmt_string::npos) return;
2420 
2421  History& h = History::instance ();
2422 
2423  if (h.is_installed (line)) return;
2424 
2425  CmtSystem::cmt_string_vector words;
2426 
2427  CmtSystem::split (line, " \t", words);
2428
2429  enum
2430  {
2431    need_package,
2432    need_version,
2433    need_path,
2434    no_need
2435  } state = need_package;
2436
2437  cmt_string package;
2438  cmt_string version;
2439  cmt_string path;
2440
2441  for (int i = 1; i < words.size (); i++)
2442    {
2443      const cmt_string& s = words[i];
2444
2445      if (s[0] == '-') continue;
2446
2447      switch (state)
2448        {
2449          case need_package:
2450            package = s;
2451            state = need_version;
2452            break;
2453          case need_version:
2454            version = s;
2455            state = need_path;
2456            break;
2457          case need_path:
2458            path = s;
2459            state = no_need;
2460            break;
2461        }
2462    }
2463 
2464  if (version.find ("*") != cmt_string::npos)
2465    {
2466        /*
2467      cout << "# ================= Package " << package
2468           << " version " << version << " " << path
2469           << " has wild cards and will not be considered." << endl;
2470        */
2471      return;
2472    }
2473
2474  /**
2475   *  At the first pass, we simply accumulate the not-yet handled
2476   *  use statements.
2477   */
2478 
2479  m_result += line;
2480  m_result += "\n";
2481 
2482  if (m_first)
2483    {
2484      m_first = false;
2485      cout << "  # --> now propagate cmt checkout to :" << endl;
2486    }
2487 
2488  cout << "  #     " << package << " " << version << " " << path << endl;
2489}
2490
2491const cmt_string& RecursivePass1::result () const
2492{
2493  return (m_result);
2494}
2495
2496//--------------------------------------------------------------------
2497
2498RecursivePass2::RecursivePass2 (CvsImplementation& cvs) : m_cvs (cvs)
2499{
2500}
2501
2502void RecursivePass2::begin ()
2503{
2504}
2505
2506void RecursivePass2::filter (const cmt_string& line)
2507{
2508    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "RecursivePass2::filter> "
2509    //                                          << "line=[" << line << "]" << endl;
2510
2511  /**
2512   *   At the second pass, the lines are really handled. Thus
2513   *   the lines are stored into m_installed so as to avoid
2514   *   later on re-installation.
2515   */
2516 
2517  History& h = History::instance ();
2518 
2519  if (h.is_installed (line)) return;
2520 
2521  h.install (line);
2522 
2523  CmtSystem::cmt_string_vector words;
2524 
2525  CmtSystem::split (line, " \t", words);
2526
2527  enum
2528  {
2529    need_package,
2530    need_version,
2531    need_path,
2532    no_need
2533  } state = need_package;
2534
2535  cmt_string package;
2536  cmt_string version;
2537  cmt_string path;
2538
2539  for (int i = 1; i < words.size (); i++)
2540    {
2541      const cmt_string& s = words[i];
2542
2543      if (s[0] == '-') continue;
2544
2545      switch (state)
2546        {
2547          case need_package:
2548            package = s;
2549            state = need_version;
2550            break;
2551          case need_version:
2552            version = s;
2553            state = need_path;
2554            break;
2555          case need_path:
2556            path = s;
2557            state = no_need;
2558            break;
2559        }
2560    }
2561
2562  if (version.find ("*") != cmt_string::npos)
2563    {
2564        /*
2565      cout << "# ================= Package " << package
2566           << " version " << version << " " << path
2567           << " has wild cards and will not be considered." << endl;
2568        */
2569    }
2570  else
2571    {
2572      static const cmt_string empty;
2573      m_cvs.do_checkout_phase2 (path, package, version, empty);
2574    }
2575}
2576
2577//--------------------------------------------------------------------
2578
2579void RecursivePass3::begin ()
2580{
2581  m_first = true;
2582  m_result = "";
2583}
2584
2585void RecursivePass3::filter (const cmt_string& line)
2586{
2587  History& h = History::instance ();
2588 
2589  if (h.is_installed (line)) return;
2590 
2591  CmtSystem::cmt_string_vector words;
2592 
2593  CmtSystem::split (line, " \t", words);
2594
2595  enum
2596  {
2597    need_project,
2598    need_version,
2599    need_tag,
2600    no_need
2601  } state = need_project;
2602
2603  cmt_string project;
2604  cmt_string version;
2605  cmt_string tag;
2606
2607  for (int i = 1; i < words.size (); i++)
2608    {
2609      const cmt_string& s = words[i];
2610
2611      switch (state)
2612        {
2613          case need_project:
2614            project = s;
2615            state = need_version;
2616            break;
2617          case need_version:
2618            version = s;
2619            state = need_tag;
2620            break;
2621          case need_tag:
2622            tag = s;
2623            state = no_need;
2624            break;
2625        }
2626    }
2627 
2628  if (version.find ("*") != cmt_string::npos)
2629    {
2630        /*
2631      cout << "# ================= Project " << project
2632           << " version " << version << " " << path
2633           << " has wild cards and will not be considered." << endl;
2634        */
2635      return;
2636    }
2637
2638  /**
2639   *  At the first pass, we simply accumulate the not-yet handled
2640   *  use statements.
2641   */
2642 
2643  m_result += line;
2644  m_result += "\n";
2645 
2646  if (m_first)
2647    {
2648      m_first = false;
2649      cout << "  # --> now propagate cmt checkout to :" << endl;
2650    }
2651 
2652  cout << "  #     " << project << " " << version << " " << tag << endl;
2653}
2654
2655const cmt_string& RecursivePass3::result () const
2656{
2657  return (m_result);
2658}
2659
2660//--------------------------------------------------------------------
2661
2662RecursivePass4::RecursivePass4 (CvsImplementation& cvs) : m_cvs (cvs)
2663{
2664}
2665
2666void RecursivePass4::begin ()
2667{
2668}
2669
2670void RecursivePass4::filter (const cmt_string& line)
2671{
2672    //if (CmtSystem::testenv ("CMTTESTAWK")) cout << "RecursivePass4::filter> "
2673    //                                          << "line=[" << line << "]" << endl;
2674
2675  /**
2676   *   At the second pass, the lines are really handled. Thus
2677   *   the lines are stored into m_installed so as to avoid
2678   *   later on re-installation.
2679   */
2680 
2681  History& h = History::instance ();
2682 
2683  if (h.is_installed (line)) return;
2684 
2685  h.install (line);
2686 
2687  CmtSystem::cmt_string_vector words;
2688 
2689  CmtSystem::split (line, " \t", words);
2690
2691  enum
2692  {
2693    need_project,
2694    need_version,
2695    need_tag,
2696    no_need
2697  } state = need_project;
2698
2699  cmt_string project;
2700  cmt_string version;
2701  cmt_string tag;
2702
2703  for (int i = 1; i < words.size (); i++)
2704    {
2705      const cmt_string& s = words[i];
2706
2707      switch (state)
2708        {
2709          case need_project:
2710            project = s;
2711            state = need_version;
2712            break;
2713          case need_version:
2714            version = s;
2715            state = need_tag;
2716            break;
2717          case need_tag:
2718            tag = s;
2719            state = no_need;
2720            break;
2721        }
2722    }
2723
2724  if (version.find ("*") != cmt_string::npos)
2725    {
2726        /*
2727      cout << "# ================= Project " << project
2728           << " version " << version
2729           << " has wild cards and will not be considered." << endl;
2730        */
2731    }
2732  else
2733    {
2734      static const cmt_string empty;
2735      m_cvs.do_checkout_phase2 (empty, project, version, tag);
2736    }
2737}
2738
2739//--------------------------------------------------------------------
2740
2741void Cvs::tags (const CmtSystem::cmt_string_vector& arguments)
2742{
2743  CvsImplementation cvs;
2744
2745  cvs.tags (arguments);
2746}
2747
2748void Cvs::branches (const cmt_string& module)
2749{
2750  CvsImplementation cvs;
2751
2752  cvs.branches (module);
2753}
2754
2755void Cvs::subpackages (const cmt_string& module)
2756{
2757  CvsImplementation cvs;
2758
2759  cvs.subpackages (module);
2760}
2761
2762void Cvs::subprojects (const cmt_string& module)
2763{
2764  CvsImplementation cvs;
2765
2766  cvs.subprojects (module);
2767}
2768
2769void Cvs::checkout (const CmtSystem::cmt_string_vector& arguments)
2770{
2771  CvsImplementation cvs;
2772
2773  cvs.checkout (arguments);
2774}
2775
Note: See TracBrowser for help on using the repository browser.