source: CMT/v1r18p20060505/source/cmt_cvs.cxx

Last change on this file was 178, checked in by garonne, 18 years ago

do the exact matching for extracting tag fron cvs

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