source: CMT/v1r18p20050501/source/cmt_cvs.cxx @ 658

Last change on this file since 658 was 45, checked in by arnault, 19 years ago

cmt cvs operations - See CL 272

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