source: CMT/v1r20p20090520/source/cmt_vcs.cxx @ 653

Last change on this file since 653 was 497, checked in by rybkin, 15 years ago

See C.L. 392

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