source: CMT/HEAD/source/cmt_generator.cxx @ 591

Last change on this file since 591 was 591, checked in by rybkin, 13 years ago

See C.L. 468

  • Property svn:eol-style set to native
File size: 40.6 KB
Line 
1//-----------------------------------------------------------
2// Copyright Christian Arnault LAL-Orsay CNRS
3// arnault@lal.in2p3.fr
4// See the complete license in cmt_license.txt "http://www.cecill.info".
5//-----------------------------------------------------------
6
7#include <errno.h>
8#include <stdio.h>
9
10#ifndef WIN32
11#include <unistd.h>
12#endif
13
14#include "cmt_generator.h"
15#include "cmt_use.h"
16#include "cmt_symbol.h"
17
18#include "cmt_generators.h"
19#include "cmt_log.h"
20#include "cmt_error.h"
21
22#include "cmt_install_area.h"
23
24//------------------------------------------------------------------------
25void SourceFile::set (const cmt_string name, Language& language, const cmt_string output)
26{
27  m_name = name;
28  m_language = &language;
29  m_output = output;
30 
31  CmtSystem::reduce_file_separators (m_name);
32}
33
34cmt_string SourceFile::name () const
35{
36  return (m_name);
37}
38
39Language& SourceFile::language () const
40{
41  return (*m_language);
42}
43
44cmt_string SourceFile::output () const
45{
46  return (m_output);
47}
48//------------------------------------------------------------------------
49
50//--------------------------------------------------
51CmtGenerator::CmtGenerator ()
52{
53  m_CONSTITUENT.set ("CONSTITUENT");
54  m_LINKMACRO.set ("LINKMACRO");
55  m_DEPENDENCIESOPTS.set ("DEPENDENCIESOPTS");
56  m_DOCPATH.set ("DOCPATH");
57  m_PACKAGEPATH.set ("PACKAGEPATH");
58  m_PACKAGEPREFIX.set ("PACKAGEPREFIX");
59  m_PACKAGE.set ("PACKAGE");
60  m_VERSION.set ("VERSION");
61  m_MGRSTYLE.set ("MGRSTYLE");
62  m_TITLE.set ("TITLE");
63  m_GROUP.set ("GROUP");
64  m_CONSTITUENTSUFFIX.set ("CONSTITUENTSUFFIX");
65  m_LIBRARYSUFFIX.set ("LIBRARYSUFFIX");
66  m_USER.set ("USER");
67  m_DATE.set ("DATE");
68  m_PROTOTARGET.set ("PROTOTARGET");
69  m_OBJS.set ("OBJS");
70  m_CLASSES.set ("CLASSES");
71  m_PROTOSTAMPS.set ("PROTOSTAMPS");
72  m_NAME.set ("NAME");
73  m_FILEPATH.set ("FILEPATH");
74  m_FILESUFFIX.set ("FILESUFFIX");
75  m_SUFFIX.set ("SUFFIX");
76  m_FILENAME.set ("FILENAME");
77  m_LINE.set ("LINE");
78  m_ADDINCLUDE.set ("ADDINCLUDE");
79  m_FULLNAME.set ("FULLNAME");
80  m_DIRNAME.set ("DIRNAME");
81  m_OUTPUTNAME.set ("OUTPUTNAME");
82  m_ALLOS9SOURCES.set ("ALLOS9SOURCES");
83  m_NODEBUGUSELINKOPTS.set ("NODEBUGUSELINKOPTS");
84  m_DEBUGUSELINKOPTS.set ("DEBUGUSELINKOPTS");
85  m_USEINCLUDES.set ("USEINCLUDES");
86  m_HASTARGETTAG.set ("HASTARGETTAG");
87  m_HASDEPENDENCIES.set ("HASDEPENDENCIES");
88}
89
90//--------------------------------------------------
91void CmtGenerator::reset ()
92{
93  m_CONSTITUENT = "";
94  m_LINKMACRO = "";
95  m_DEPENDENCIESOPTS = "";
96  m_DOCPATH = "";
97  m_PACKAGEPATH = "";
98  m_PACKAGEPREFIX = "";
99  m_PACKAGE = "";
100  m_VERSION = "";
101  m_MGRSTYLE = "";
102  m_TITLE = "";
103  m_GROUP = "";
104  m_CONSTITUENTSUFFIX = "";
105  m_LIBRARYSUFFIX = "";
106  m_USER = "";
107  m_DATE = "";
108  m_PROTOTARGET = "";
109  m_OBJS = "";
110  m_CLASSES = "";
111  m_PROTOSTAMPS = "";
112  m_NAME = "";
113  m_FILEPATH = "";
114  m_FILESUFFIX = "";
115  m_SUFFIX = "";
116  m_FILENAME = "";
117  m_LINE = "";
118  m_ADDINCLUDE = "";
119  m_FULLNAME = "";
120  m_OUTPUTNAME = "";
121  m_ALLOS9SOURCES = "";
122  m_NODEBUGUSELINKOPTS = "";
123  m_DEBUGUSELINKOPTS = "";
124  m_USEINCLUDES = "";
125  m_HASTARGETTAG = "";
126  m_HASDEPENDENCIES = "";
127  m_PACKINCLUDES = "";
128  m_PACKOS9      = false;
129  m_GENERATOR    = "";
130
131  is_library     = false;
132  is_application = false;
133  srcdir       = "";
134  docdir       = "";
135  cmtdir       = "";
136  incdir       = "";
137  src          = "$(src)";
138  doc          = "$(doc)";
139  inc          = "$(inc)";
140  mgr          = "$(mgr)";
141  cmt          = "$(cmt)";
142  protos       = "";
143  protonames   = "";
144  os9sources   = "";
145
146  m_source_files.clear ();
147  m_bin = "";
148  m_output_file_name = "";
149  m_output_file = 0;
150  m_constituent = 0;
151
152  Language::setup_all_fragments ();
153
154  CmtSystem::cd (Cmt::get_current_dir ());
155
156  cmt_string branch = CmtSystem::current_branch ();
157
158  if ((branch == "mgr") || (branch == "cmt"))
159    {
160      if (CmtSystem::test_directory ("../src"))
161        {
162          srcdir = "..";
163          srcdir += CmtSystem::file_separator ();
164          srcdir += "src";
165          srcdir += CmtSystem::file_separator ();
166        }
167      else
168        {
169          srcdir = "";
170        }
171
172      if (CmtSystem::test_directory ("../doc"))
173        {
174          docdir = "..";
175          docdir += CmtSystem::file_separator ();
176          docdir += "doc";
177          docdir += CmtSystem::file_separator ();
178        }
179      else
180        {
181          docdir = "";
182        }
183
184      if (CmtSystem::test_directory ("../cmt"))
185        {
186          cmtdir = "..";
187          cmtdir += CmtSystem::file_separator ();
188          cmtdir += "cmt";
189          cmtdir += CmtSystem::file_separator ();
190        }
191      else if (CmtSystem::test_directory ("../mgr"))
192        {
193          cmtdir = "..";
194          cmtdir += CmtSystem::file_separator ();
195          cmtdir += "mgr";
196          cmtdir += CmtSystem::file_separator ();
197        }
198      else
199        {
200          cmtdir = CmtSystem::pwd ();
201          cmtdir += CmtSystem::file_separator ();
202        }
203
204      if (CmtSystem::test_directory ("../src"))
205        {
206          incdir = "..";
207          incdir += CmtSystem::file_separator ();
208          incdir += "src";
209          incdir += CmtSystem::file_separator ();
210        }
211      else
212        {
213          incdir = "";
214        }
215    }
216  else
217    {
218      srcdir = ".";
219      srcdir += CmtSystem::file_separator ();
220      docdir = ".";
221      docdir += CmtSystem::file_separator ();
222      cmtdir = CmtSystem::pwd ();
223      cmtdir += CmtSystem::file_separator ();
224      incdir = ".";
225      incdir += CmtSystem::file_separator ();
226    }
227}
228
229//--------------------------------------------------
230bool CmtGenerator::prepare_output (const cmt_string& package,
231                                   const Constituent& constituent,
232                                   const cmt_string& file)
233{
234  m_PACKAGE = package;
235  m_CONSTITUENT = constituent.name;
236  m_CONSTITUENTSUFFIX = constituent.suffix;
237
238  m_PACKOS9 = constituent.need_OS9;
239
240  m_output_file_name = cmtdir + m_CONSTITUENT + ".";
241
242  if (Cmt::build_nmake ())
243    {
244      m_output_file_name += "nmake";
245    }
246  else
247    {
248      m_output_file_name += "make";
249    }
250 
251  if (file != "") m_output_file_name = file;
252
253  m_output_file_name += "new";
254
255
256  m_output_file = fopen (m_output_file_name.c_str (), "wb");
257  if (m_output_file != NULL)
258    {
259      return (true);
260    }
261  else
262    {
263      return (false);
264    }
265}
266
267//--------------------------------------------------
268void CmtGenerator::check (const cmt_string& name)
269{
270  static cmt_string old;
271  static cmt_string backup;
272
273  old = name;
274
275  int pos = old.find_last_of ("new");
276  old.erase (pos);
277
278  if (!CmtSystem::compare_files (old, name))
279    {
280      backup = old;
281      backup += "sav";
282
283      unlink (backup.c_str ());
284      rename (old.c_str (), backup.c_str ());
285      rename (name.c_str (), old.c_str ());
286    }
287  else
288    {
289      unlink (name);
290    }
291}
292
293//--------------------------------------------------
294void CmtGenerator::commit (const cmt_string& name)
295{
296  static cmt_string old;
297  static cmt_string backup;
298
299  old = name;
300
301  int pos = old.find_last_of ("new");
302  old.erase (pos);
303
304  if (CmtSystem::test_file (old))
305    {
306      backup = old;
307      backup += "sav";
308
309      unlink (backup.c_str ());
310      rename (old.c_str (), backup.c_str ());
311    }
312
313  rename (name.c_str (), old.c_str ());
314}
315
316//--------------------------------------------------
317void CmtGenerator::terminate ()
318{
319  CmtSystem::close_ostream (m_output_file, m_output_file_name);
320  //  fclose (m_output_file);
321
322  //--- Complete the operation --------------
323
324  commit (m_output_file_name);
325}
326
327//--------------------------------------------------
328void CmtGenerator::fill_names_outputs ()
329{
330  bool first = true;
331
332  m_LINE = "";
333  m_OBJS = "";
334
335  for (int i = 0; i < m_source_files.size (); i++)
336    {
337      const SourceFile& file = m_source_files[i];
338      const cmt_string name = file.name ();
339      const cmt_string output = file.output ();
340
341      if (output != "")
342        {
343          if (first)
344            {
345              first = false;
346            }
347          else
348            {
349              m_LINE += " ";
350              m_OBJS += " ";
351            }
352          m_LINE += name;
353          m_OBJS += output;
354        }
355
356      if (Cmt::get_debug ())
357        {
358          cout << "CmtGenerator::fill_names_outputs>" << endl;
359          cout << "name=" << name << " LINE=" << m_LINE << endl;
360          cout << "output=" << output << " OBJS=" << m_OBJS << endl;
361        }
362    }
363
364  filter_path (m_LINE.value);
365}
366
367//--------------------------------------------------
368void CmtGenerator::fill_outputs ()
369{
370  bool first = true;
371
372  m_OBJS = "";
373
374  for (int i = 0; i < m_source_files.size (); i++)
375    {
376      const SourceFile& file = m_source_files[i];
377      const cmt_string output = file.output ();
378
379      if (output != "")
380        {
381          if (first)
382            {
383              first = false;
384            }
385          else
386            {
387              m_OBJS += " ";
388            }
389
390          m_OBJS += output;
391        }
392
393      if (Cmt::get_debug ())
394        {
395          cout << "CmtGenerator::fill_outputs> output=" << output << " OBJS=" << m_OBJS << endl;
396        }
397
398    }
399
400  if (Cmt::get_debug ())
401    {
402      cout << "CmtGenerator::fill_outputs> OBJS=" << m_OBJS << endl;
403    }
404
405}
406
407/*
408//--------------------------------------------------
409void CmtGenerator::prepare_use_context ()
410{
411  cmt_string path;
412  cmt_string substitution;
413
414  Use* use = &Use::current ();
415
416  m_deps_builder.clear ();
417
418  if (use->include_path != "none")
419    {
420      if (use->include_path == "")
421        {
422          m_deps_builder.add (incdir, "$(src)");
423        }
424      else
425        {
426          substitution = use->include_path;
427         
428          path = substitution;
429          Symbol::expand (path);
430         
431          CmtSystem::reduce_file_separators (path);
432
433          m_deps_builder.add (path, substitution);
434        }
435    }
436
437  m_deps_builder.add_includes (*use);
438
439  Use::UsePtrVector& uses = Use::get_ordered_uses ();
440
441  if (uses.size () > 0)
442    {
443      int number;
444
445      for (number = 0; number < uses.size (); number++)
446        {
447          use = uses[number];
448          if (use->discarded) continue;
449
450          if (use->real_path != "")
451            {
452              if (use->include_path != "none")
453                {
454                  if (use->include_path == "")
455                    {
456                      use->get_full_path (path);
457                      path += CmtSystem::file_separator ();
458                      path += "src";
459
460                      substitution = "$(";
461                      substitution += use->prefix;
462                      substitution += "ROOT)";
463                      substitution += CmtSystem::file_separator ();
464                      substitution += "src";
465                      substitution += CmtSystem::file_separator ();
466                    }
467                  else
468                    {
469                      substitution = use->include_path;
470
471                      path = substitution;
472                      Symbol::expand (path);
473
474                      CmtSystem::reduce_file_separators (path);
475                    }
476
477                  m_deps_builder.add (path, substitution);
478                }
479
480              m_deps_builder.add_includes (*use);
481            }
482        }
483    }
484}
485*/
486//--------------------------------------------------
487void CmtGenerator::filter_path (cmt_string& text)
488{
489  CmtSystem::compress_path (text);
490
491  text.replace_all ("./../src/../", "../");
492  text.replace_all ("./../src/", "$(src)");
493
494  text.replace_all (".\\..\\src\\..\\", "..\\");
495  text.replace_all (".\\..\\src\\", "$(src)");
496
497  text.replace_all ("../src/../", "../");
498  text.replace_all ("../src/", "$(src)");
499
500  text.replace_all ("..\\src\\..\\", "..\\");
501  text.replace_all ("..\\src\\", "$(src)");
502
503  text.replace_all ("../doc/../", "../");
504  text.replace_all ("../doc/", "$(doc)");
505
506  text.replace_all ("..\\doc\\..\\", "..\\");
507  text.replace_all ("..\\doc\\", "$(doc)");
508
509  text.replace_all ("$(src)$(src)", "$(src)");
510}
511
512/**
513   Scan a complete file spec (with possibly wild cards and directory)
514   given in full_name ad fill in a vector of found file names.
515
516   Result of the scan is filtered against matching suffixes
517
518   Returns the count of non empty file names really found.
519
520*/
521int CmtGenerator::get_all_files (const cmt_string& full_name,
522                                 const cmt_vector<cmt_regexp>& exclude_exprs,
523                                 const cmt_vector<cmt_regexp>& select_exprs,
524                                 CmtSystem::cmt_string_vector& files)
525{
526  static cmt_string suffix;
527  static cmt_string name;
528
529  bool has_excludes = false;
530  bool has_selects = false;
531
532  suffix = "";
533  name = "";
534
535  files.clear ();
536
537  has_excludes = (exclude_exprs.size () > 0);
538  has_selects = (select_exprs.size () > 0);
539
540  CmtSystem::get_dot_suffix (full_name, suffix);
541
542  bool wilcarded_suffix = false;
543
544  if (suffix == ".*") wilcarded_suffix = true;
545
546  int count = 0;
547
548  if (full_name.find ('*') != cmt_string::npos)
549    {
550      CmtSystem::scan_dir (full_name, files);
551
552      if (Cmt::get_debug ())
553        {
554          cout << "CMT::get_all_files> full_name=" << full_name <<
555            " pwd=" << CmtSystem::pwd () << endl;
556          cout << "CMT::get_all_files> files.size=" <<  files.size () << endl;
557        }
558
559      /**
560
561      We have to treat patterns of the form *.xxx (ie with a
562      suffix) thus we filter out everything that could have been
563      collected with a different suffix because the
564      CmtSystem::scan_dir function only handles patterns of the
565      form xxx* (ie with trailing *)
566
567      [If the original suffix was empty (ie files specified using
568      xx*) this means getting files without any dot-suffix. This
569      may be incorrect??]
570
571      */
572
573      for (int j = 0; j < files.size (); j++)
574        {
575          cmt_string& n = files[j];
576
577          bool rejected = false;
578
579          if (n == "")
580            {
581              rejected = true;
582            }
583
584          if (!rejected && has_selects)
585            {
586              rejected = true;
587
588              for (int k = 0; k < select_exprs.size (); k++)
589                {
590                  const cmt_regexp& exp = select_exprs[k];
591                  if (exp.match (n))
592                    {
593                      rejected = false;
594                      break;
595                    }
596                }
597            }
598
599          if (!rejected && has_excludes)
600            {
601              for (int k = 0; k < exclude_exprs.size (); k++)
602                {
603                  const cmt_regexp& exp = exclude_exprs[k];
604                  if (exp.match (n))
605                    {
606                      rejected = true;
607                      break;
608                    }
609                }
610            }
611
612          if (!rejected)
613            {
614              static cmt_string s;
615
616              CmtSystem::get_dot_suffix (n, s);
617              if (!wilcarded_suffix && (s != suffix)) 
618                {
619                  rejected = true;
620                }
621              else
622                {
623                  count++;
624                }
625            }
626
627          if (Cmt::get_debug ())
628            {
629              if (rejected)
630                {
631                  cout << "CMT::get_all_files> reject " <<  n << endl;
632                }
633              else
634                {
635                  cout << "CMT::get_all_files> keep " <<  n << endl;
636                }
637            }
638
639          if (rejected)
640            {
641              n = "";
642            }
643        }
644    }
645  else
646    {
647      if (full_name != "")
648        {
649          bool rejected = false;
650
651          if (has_excludes)
652            {
653              for (int k = 0; k < exclude_exprs.size (); k++)
654                {
655                  const cmt_regexp& exp = exclude_exprs[k];
656                  if (exp.match (full_name))
657                    {
658                      rejected = true;
659                      break;
660                    }
661                }
662            }
663
664          if (!rejected)
665            {
666              cmt_string& n = files.add ();
667
668              n = full_name;
669
670              count++;
671            }
672        }
673    }
674
675  return (count);
676}
677
678//--------------------------------------------------
679void CmtGenerator::set_full_name (cmt_string& full_name, cmt_string& file)
680{
681  full_name = "";
682
683  Symbol::expand (file);
684
685  if (file == "") return;
686 
687  if (!CmtSystem::absolute_path (file))
688    {
689      full_name = srcdir;
690      if (full_name != "") full_name += CmtSystem::file_separator ();
691    }
692 
693  full_name += file;
694
695  CmtSystem::reduce_file_separators (full_name);
696}
697
698//------------------------------------------------------------------------
699//static ApplicationGenerator ApplicationContext;
700static LibraryGenerator LibraryContext;
701static DocumentGenerator DocumentContext;
702static ReadmeGenerator ReadmeContext;
703static PrototypeGenerator PrototypeContext;
704static DefaultMakefileGenerator DefaultMakefileContext;
705static MSDEVGenerator MSDEVContext;
706static VSNETGenerator VSNETContext;
707static MakeSetupGenerator MakeSetupContext;
708static ConstituentsMakefileGenerator ConstituentsMakefileContext;
709static PackagesMakefileGenerator PackagesMakefileContext;
710static DependencyGenerator DependencyContext;
711
712//--------------------------------------------------
713int Generator::build_msdev_workspace (const Constituent::ConstituentVector& constituents)
714{
715  return (MSDEVContext.build_workspace (constituents));
716}
717
718//--------------------------------------------------
719int Generator::build_msdev (const Constituent& constituent)
720{
721  return (MSDEVContext.build_project (constituent));
722}
723
724//--------------------------------------------------
725int Generator::build_vsnet_workspace (const Constituent::ConstituentVector& constituents)
726{
727  return (VSNETContext.build_workspace (constituents));
728}
729
730//--------------------------------------------------   
731int Generator::build_vsnet (const Constituent& constituent)
732{
733  return (VSNETContext.build_project (constituent));
734}
735
736//--------------------------------------------------
737void Generator::build_make_setup (const cmt_string& package)
738{
739  MakeSetupContext.build (package);
740}
741
742//--------------------------------------------------
743void Generator::build_constituents_makefile (const cmt_string& package,
744                                             const cmt_string& file)
745//                                           const CmtSystem::cmt_string_vector& arguments)
746{
747  ConstituentsMakefileContext.build (package, file);
748  //  ConstituentsMakefileContext.build (package, arguments);
749}
750
751//--------------------------------------------------
752int Generator::build_constituent_infile (const Constituent& constituent,
753                                         const cmt_string& outdir,
754                                         bool usecmt)
755{
756  cmt_string file (constituent.name + ".in");
757  if (outdir != "")
758    {
759      if (outdir [outdir.size () - 1] != CmtSystem::file_separator ())
760        file = outdir + CmtSystem::file_separator () + file;
761      else
762        file = outdir + file;
763    }
764
765  static InGenerator InGen (usecmt);
766
767  bool gen (true);
768  ostringstream os;
769  ofstream s;
770  //  s.open (file);
771  s.open (file, ios::in);
772  if (s) // file already exists
773    {
774      InGen.build (constituent, os);
775      ostringstream osn;
776      osn << s.rdbuf ();
777      if (os.str () == osn.str ())
778        {
779          //      cerr << file << " up-to-date" << endl;
780          gen = false;
781        }
782    }
783  s.clear ();
784  s.close ();
785  s.clear ();
786  if (gen)
787    {
788      s.open (file);
789      if (!s)
790        {
791          CmtError::set (CmtError::file_access_error, file);
792          return -1;
793        }
794      s.exceptions (ios::failbit | ios::badbit);
795      try
796        {
797          if (os.str ().size () != 0)
798            {
799              //  cerr << file << " contents already generated" << endl;
800              s << os.str ();
801            }
802          else
803            {
804              InGen.build (constituent, s);
805            }
806          s.close (); // ios_base::failbit
807        }
808      catch (const ios::failure& e)
809        {
810          CmtSystem::close_ostream (NULL, file + ": " + cmt_string (e.what ()));
811          return -1;
812        }
813    }
814
815      //  InGen.build (constituent, s);
816
817  //  s.close (); // ios_base::failbit
818
819  return 0;
820}
821
822//--------------------------------------------------
823int Generator::build_library_links_infile (const cmt_string& outdir)
824{
825#ifndef WIN32
826  cmt_string file ("library_links.in");
827  if (outdir != "")
828    {
829      if (outdir [outdir.size () - 1] != CmtSystem::file_separator ())
830        file = outdir + CmtSystem::file_separator () + file;
831      else
832        file = outdir + file;
833    }
834
835  bool wrote (false);
836  ostringstream ls;
837
838  Use::UsePtrVector& Uses = Use::get_ordered_uses ();
839  Use& current_use = Use::current ();
840  Use::UsePtrVector uses (Uses);
841  uses.push_back (&Use::current ());
842
843  cmt_string shlibsuffix;
844  {
845    Symbol* macro = Symbol::find ("shlibsuffix");
846    if (macro == 0)
847      {
848        CmtError::set(CmtError::configuration_error, "shlibsuffix undefined");
849        return -1;
850      }
851    shlibsuffix = macro->build_macro_value ();
852    Symbol::expand (shlibsuffix);
853  }
854
855  for (int i = 0; i < uses.size (); i++)
856    {
857      Use* use = uses[i];
858
859      if (use == 0) continue;
860      if (use->discarded) continue;
861      if (use->m_hidden) continue;
862
863      if (use->get_package_name () == "CMT") continue;
864      if (!use->located ()) continue;
865      //use->remove_library_links (cmtinstallarea, tag, shlibsuffix, symunlinkcmd);
866      cmt_string s (use->get_package_name ());
867      s += "_libraries";
868      const Symbol* libraries_macro = Symbol::find (s);
869      if (libraries_macro == 0) continue;
870      cmt_string libraries = libraries_macro->build_macro_value ();
871      Symbol::expand (libraries);
872      static CmtSystem::cmt_string_vector values;
873     
874      CmtSystem::split (libraries, " \t", values);
875     
876      for (int j = 0; j < values.size (); j++)
877        {
878          const cmt_string& library = values[j];
879         
880          static cmt_string libname;
881          static cmt_string name;
882         
883          // Is it a simple name or a complete path?
884         
885          libname = library;
886          Symbol::expand (libname);
887          if (0 == libname.size ()) continue;
888         
889          CmtSystem::cmt_string_vector paths;
890          use->absolute_library_path (libname,
891                                      shlibsuffix, 
892                                      paths);
893          for (int k = 0; k < paths.size (); k++)
894            {
895              cmt_string path (paths[k]);
896              Symbol::expand (path);
897              if (0 == path.size ()) continue;
898              if (wrote) ls << " ";
899              else wrote = true;
900              ls << CmtSystem::quote (path, " \t");
901            }
902        }
903    }
904  ostringstream os;
905  if (wrote)
906    {
907      os << "macro ";
908      os << current_use.get_package_name () + "_libraries ";
909      os << CmtSystem::quote (ls.str ().c_str (), " \t");
910      os << "\n";
911
912      os << "macro shlibsuffix ";
913      os << CmtSystem::quote (shlibsuffix, " \t");
914      os << "\n";
915
916      cmt_string symlinkcmd;
917      {
918        Symbol* macro = Symbol::find ("library_install_command");
919        if (macro != 0)
920          {
921            symlinkcmd = macro->build_macro_value ();
922            Symbol::expand (symlinkcmd);
923          }
924      }
925      os << "macro library_install_command ";
926      os << CmtSystem::quote (symlinkcmd, " \t");
927      os << "\n";
928
929      cmt_string symunlinkcmd;
930      {
931        Symbol* macro = Symbol::find ("symunlink");
932        if (macro != 0)
933          {
934            symunlinkcmd = macro->build_macro_value ();
935            Symbol::expand (symunlinkcmd);
936          }
937      }
938      os << "macro symunlink ";
939      os << CmtSystem::quote (symunlinkcmd, " \t");
940      os << "\n";
941
942      if (current_use.get_strategy ("InstallArea"))
943        {
944          os << "build_strategy with_installarea\n";
945
946          const CmtInstallAreaMgr& ia_mgr = CmtInstallAreaMgr::instance ();
947         
948          cmt_string cmtinstallarea = ia_mgr.get_installarea ();
949          {
950            Symbol* symbol = Symbol::find ("CMTINSTALLAREA");
951            if (symbol != 0)
952              {
953                cmtinstallarea = symbol->build_macro_value ();
954                Symbol::expand (cmtinstallarea);
955              }
956          }
957          os << "macro CMTINSTALLAREA ";
958          os << CmtSystem::quote (cmtinstallarea, " \t");
959          os << "\n";
960
961          cmt_string tag;
962          {
963            Symbol* macro = Symbol::find ("tag");
964            if (macro != 0)
965              {
966                tag = macro->build_macro_value ();
967                Symbol::expand (tag);
968              }
969          }
970          os << "macro tag ";
971          os << CmtSystem::quote (tag, " \t");
972          os << "\n";
973        }
974      //      cerr << os.str ();
975    }
976
977  // Write input requirements file
978
979  //  static InGenerator InGen (usecmt);
980
981  bool gen (true);
982  //  ostringstream os;
983  ofstream s;
984  //  s.open (file);
985  s.open (file, ios::in);
986  if (s) // file already exists
987    {
988      //      InGen.build (constituent, os);
989      ostringstream osn;
990      osn << s.rdbuf ();
991      if (os.str () == osn.str ())
992        {
993          //      cerr << file << " up-to-date" << endl;
994          gen = false;
995        }
996    }
997  s.clear ();
998  s.close ();
999  s.clear ();
1000  if (gen)
1001    {
1002      s.open (file);
1003      if (!s)
1004        {
1005          CmtError::set (CmtError::file_access_error, file);
1006          return -1;
1007        }
1008      s.exceptions (ios::failbit | ios::badbit);
1009      try
1010        {
1011          //      if (os.str ().size () != 0)
1012          //        {
1013              //  cerr << file << " contents already generated" << endl;
1014              s << os.str ();
1015              //            }
1016              //          else
1017              //            {
1018              //              InGen.build (constituent, s);
1019              //            }
1020          s.close (); // ios_base::failbit
1021        }
1022      catch (const ios::failure& e)
1023        {
1024          CmtSystem::close_ostream (NULL, file + ": " + cmt_string (e.what ()));
1025          return -1;
1026        }
1027    }
1028
1029#endif
1030  return 0;
1031}
1032
1033//--------------------------------------------------
1034int Generator::build_dependencies_infile (const Constituent* pconstituent,
1035                                          const cmt_string& outdir,
1036                                          bool usecmt)
1037{
1038  cmt_string file ("dependencies");
1039  if (0 != pconstituent)
1040    file += "_" + pconstituent->name;
1041  file += ".in";
1042
1043  if (outdir != "")
1044    {
1045      if (outdir [outdir.size () - 1] != CmtSystem::file_separator ())
1046        file = outdir + CmtSystem::file_separator () + file;
1047      else
1048        file = outdir + file;
1049    }
1050
1051  ostringstream os;
1052 
1053  cmt_string preprocessor;
1054  Symbol* macro = Symbol::find ("preprocessor_command");
1055  if (macro != 0)
1056    {
1057      preprocessor = macro->resolve_macro_value ();
1058    }
1059
1060  if (preprocessor == "")
1061    {
1062      const Use* current_use = &Use::current ();
1063      Use::UsePtrVector uses (Use::get_ordered_uses ());
1064      uses.push_back (&Use::current ());
1065     
1066      if (current_use->include_path == "none")
1067        os << "include_path none\n";
1068
1069      for (int i = uses.size () - 1; i >= 0; i--)
1070        {
1071          const Use* use = uses[i];
1072          if (use->discarded) continue;
1073          if (use->m_hidden) continue;
1074          if (!use->located ()) continue;
1075         
1076          cmt_string package_name = use->get_package_name ();
1077          if (package_name == "CMT") continue;
1078         
1079          const Symbol* filter_macro = Symbol::find (package_name + "_header_file_filter");
1080          if (filter_macro == 0) continue;
1081          const cmt_string filter_expr (filter_macro->resolve_macro_value ());
1082          if (filter_expr.size () == 0) continue;
1083         
1084          const Symbol* stamp_macro = Symbol::find (package_name + "_header_file_stamp");
1085          cmt_string stamp;
1086          if (stamp_macro != 0)
1087            {
1088              stamp = stamp_macro->resolve_macro_value ();
1089            }
1090          else
1091            {
1092              use->get_full_path (stamp);
1093              switch (use->style)
1094                {
1095                case cmt_style:
1096                  stamp += CmtSystem::file_separator ();
1097                  stamp += "cmt";
1098                  break;
1099                case mgr_style:
1100                  stamp += CmtSystem::file_separator ();
1101                  stamp += "mgr";
1102                  break;
1103                }
1104              stamp += CmtSystem::file_separator ();
1105              stamp += "cmt_header_file.stamp";
1106            }
1107          if (!CmtSystem::test_file (stamp)) continue;
1108
1109          os << "macro " + package_name + "_header_file_filter ";
1110          os << CmtSystem::quote (filter_expr, " \t");
1111          //os << CmtSystem::quote (filter_macro->resolve_macro_value (), " \t");
1112          os << "\n";
1113          os << "macro " + package_name + "_header_file_stamp ";
1114          os << CmtSystem::quote (stamp, " \t");
1115          os << "\n";
1116        }
1117    }
1118  else
1119    {
1120      os << "macro preprocessor_command ";
1121      os << CmtSystem::quote (preprocessor, " \t");
1122      os << "\n";
1123      macro = Symbol::find ("includes");
1124      if (0 != macro)
1125        {
1126          os << "macro includes ";
1127          os << CmtSystem::quote (macro->resolve_macro_value (), " \t");
1128          os << "\n";
1129        }
1130      if (0 == pconstituent)
1131        {
1132          const Constituent::ConstituentVector& constituents =
1133            Constituent::constituents ();
1134          for (int n = 0; n < constituents.size (); n++)
1135            {
1136              const Constituent& constituent = constituents[n];
1137              if (constituent.has_target_tag) continue;
1138              if (constituent.type == Document) continue;
1139              cmt_string prefix;
1140              switch (constituent.type)
1141                {
1142                case Application:
1143                  prefix = "app_";
1144                  break;
1145                case Library:
1146                  prefix = "lib_";
1147                  break;
1148                }
1149              cmt_string macro_name (prefix + constituent.name + "_cppflags");
1150              macro = Symbol::find (macro_name);
1151              if (0 != macro)
1152                {
1153                  os << "macro_append " + macro_name + " ";
1154                  os << CmtSystem::quote (macro->resolve_macro_value (), " \t");
1155                  os << "\n";
1156                }
1157            }
1158        }
1159      else if (pconstituent->type != Document)
1160        {
1161          const Constituent& constituent = *pconstituent;
1162          cmt_string prefix;
1163          switch (constituent.type)
1164            {
1165            case Application:
1166              prefix = "app_";
1167              break;
1168            case Library:
1169              prefix = "lib_";
1170              break;
1171            }
1172          cmt_string macro_name (prefix + constituent.name + "_cppflags");
1173          macro = Symbol::find (macro_name);
1174          if (0 != macro)
1175            {
1176              os << "macro_append " + macro_name + " ";
1177              os << CmtSystem::quote (macro->resolve_macro_value (), " \t");
1178              os << "\n";
1179            }
1180        }
1181    }
1182
1183  // Write input requirements file
1184
1185  bool gen (true);
1186  //  ostringstream os;
1187  ofstream s;
1188  //  s.open (file);
1189  s.open (file, ios::in);
1190  if (s) // file already exists
1191    {
1192      //      InGen.build (constituent, os);
1193      ostringstream osn;
1194      osn << s.rdbuf ();
1195      if (os.str () == osn.str ())
1196        {
1197          //      cerr << file << " up-to-date" << endl;
1198          gen = false;
1199        }
1200    }
1201  s.clear ();
1202  s.close ();
1203  s.clear ();
1204  if (gen)
1205    {
1206      s.open (file);
1207      if (!s)
1208        {
1209          CmtError::set (CmtError::file_access_error, file);
1210          return -1;
1211        }
1212      s.exceptions (ios::failbit | ios::badbit);
1213      try
1214        {
1215          //      if (os.str ().size () != 0)
1216          //        {
1217              //  cerr << file << " contents already generated" << endl;
1218              s << os.str ();
1219              //            }
1220              //          else
1221              //            {
1222              //              InGen.build (constituent, s);
1223              //            }
1224          s.close (); // ios_base::failbit
1225        }
1226      catch (const ios::failure& e)
1227        {
1228          CmtSystem::close_ostream (NULL, file + ": " + cmt_string (e.what ()));
1229          return -1;
1230        }
1231    }
1232
1233  return 0;
1234}
1235
1236//--------------------------------------------------
1237int Generator::build_constituent_makefile (const Constituent& constituent,
1238                                           bool& dependencies,
1239                                           const cmt_string& file)
1240{
1241  const cmt_string& package = Cmt::get_current_package ();
1242
1243  switch (constituent.type)
1244    {
1245    case Application:
1246      //ApplicationContext.build (package, constituent, file);
1247      //break;
1248    case Library:
1249      LibraryContext.build (package, constituent, dependencies, file);
1250      break;
1251    case Document:
1252      DocumentContext.build (package, constituent, dependencies, file);
1253      break;
1254    }
1255
1256  return (0);
1257}
1258
1259//--------------------------------------------------
1260void Generator::build_constituent_makefile (const CmtSystem::cmt_string_vector& arguments)
1261{
1262  cmt_string name;
1263  cmt_string file;
1264
1265  if (arguments.size () == 1)
1266    {
1267      file = "";
1268      name = arguments[0];
1269    }
1270  else if (arguments.size () == 2) // arguments[0].substr (0, 5) == "-out="
1271    {
1272      cmt_string arg = arguments[0];
1273      arg.erase (0, 5);
1274      file = arg;
1275      name = arguments[1];
1276    }
1277  else
1278    {
1279      CmtMessage::error ("build constituent_makefile : wrong arguments");
1280      //      cerr << "#CMT> build constituent_makefile : wrong arguments" << endl;
1281      return;
1282    }
1283
1284  const Constituent* constituent = Constituent::find (name);
1285  if (constituent != 0)
1286    {
1287      bool dependencies (false);
1288      build_constituent_makefile (*constituent, dependencies, file);
1289    }
1290}
1291
1292//--------------------------------------------------
1293void Generator::build_default_makefile ()
1294{
1295  DefaultMakefileContext.build ();
1296}
1297
1298//--------------------------------------------------
1299void Generator::build_packages_makefile (const cmt_string& package,
1300                                         const cmt_string& file)
1301{
1302  PackagesMakefileContext.build (package, file);
1303  //  UsesMakefileContext.build (package, file);
1304}
1305
1306//--------------------------------------------------
1307void Generator::build_dependencies (const CmtSystem::cmt_string_vector& arguments)
1308{
1309  DependencyContext.build (arguments);
1310}
1311
1312//--------------------------------------------------
1313void Generator::build_prototype (const cmt_string& file_name)
1314{
1315  PrototypeContext.build (file_name);
1316}
1317
1318//--------------------------------------------------
1319void Generator::build_readme (const CmtSystem::cmt_string_vector& arguments)
1320{
1321  ReadmeContext.build (arguments);
1322}
1323
1324class WinDefAwk : public PAwk
1325{
1326public :
1327  WinDefAwk (const cmt_string& library_name)
1328  {
1329    m_name = library_name;
1330  }
1331
1332  void begin ()
1333  {
1334    cout << "LIBRARY " << m_name << endl;
1335    cout << "EXPORTS" << endl;
1336  }
1337
1338  void filter (const cmt_string& line)
1339  {
1340    if (line.find ("External") == cmt_string::npos) return;
1341    if (line.find ("??_") != cmt_string::npos)
1342      {
1343        if (line.find ("operator/=") == cmt_string::npos) return;
1344        // Keep operator /= .
1345      }
1346
1347    CmtSystem::cmt_string_vector words;
1348    CmtSystem::split (line, " \t", words);
1349    if (words.size () >= 7)
1350      {
1351        int pos = 7;
1352
1353        cmt_string& fifth_word = words[4];
1354        if (fifth_word == "()") pos = 7;
1355        else if (fifth_word == "External") pos = 6;
1356        else return;
1357
1358        cmt_string& symbol = words[pos];
1359        if (symbol[0] == '_') symbol.erase (0, 1);
1360        symbol.replace_all ("\r", "");
1361        symbol.replace_all ("\n", "");
1362
1363        if ((pos == 6) && 
1364            ((line.find(": static") != cmt_string::npos) ||
1365             (line.find("(class") != cmt_string::npos)) )
1366          {
1367            // static data members are not DATA :
1368            // extern objects are not DATA :
1369            cout << " " << symbol << " " << endl;
1370          } 
1371        else if (pos == 6)
1372          {
1373            // DATA :
1374            cout << " " << symbol << "\tDATA" << endl;
1375          } 
1376        else
1377          {
1378            // code :
1379            cout << " " << symbol << " " << endl;
1380          } 
1381      }
1382  }
1383
1384  void end ()
1385  {
1386  }
1387
1388private:
1389  cmt_string m_name;
1390};
1391
1392//--------------------------------------------------
1393//void Generator::build_windefs (const cmt_string& library_name)
1394void Generator::build_windefs (const CmtSystem::cmt_string_vector& arguments)
1395{
1396  cmt_string name;
1397  //  CmtSystem::cmt_string_vector files;
1398  cmt_string files;
1399
1400  for (int i = 0; i < arguments.size (); i++)
1401    {
1402      const cmt_string& w = arguments[i];
1403      if (w.substr (0, 6) == "-name=" || w.substr (0, 6) == "-name:" ||
1404          w.substr (0, 6) == "/name:" || w.substr (0, 6) == "/name=")
1405        w.substr (6, name);
1406      else if (w.substr (0, 1) == "@" && w.size () > 1)
1407        {
1408          cmt_string commandfile;
1409          w.substr (1, commandfile);
1410          if (!CmtSystem::test_file (commandfile))
1411            {
1412              CmtMessage::warning ("No such file `" + commandfile + "'.");
1413              continue;
1414            }
1415          cmt_string text;
1416          if (!text.read (commandfile))
1417            {
1418              CmtMessage::warning ("Could not read `" + commandfile + "'.");
1419              continue;
1420            }
1421          text.replace_all ("\r", " ");
1422          text.replace_all ("\n", " ");
1423          files += " " + text;
1424          /*
1425          CmtSystem::cmt_string_vector words;
1426          CmtSystem::split (text, " \t", words);
1427          for (int i = 0; i < words.size (); i++)
1428            {
1429              files.push_back (words[i]);
1430            }
1431          */
1432        }
1433      else
1434        files += " " + w;
1435      //        files.push_back (w);
1436    }
1437
1438  if (files.size () == 0)
1439    {
1440      CmtMessage::error ("build_windefs: no files specified");
1441      return;
1442    }
1443  if (name == "")
1444    {
1445      CmtSystem::cmt_string_vector words;
1446      CmtSystem::split (files, " \t", words);
1447      if (words.size () == 0)
1448        {
1449          CmtMessage::error ("build_windefs: no files specified");
1450          return;
1451        }
1452      cmt_string suffix;
1453      CmtSystem::get_dot_suffix (words[0], suffix);
1454      CmtSystem::basename (words[0], suffix, name);
1455    }
1456  if (name == "")
1457    {
1458      CmtMessage::error ("build_windefs: cannot determine library name");
1459      return;
1460    }
1461
1462  //  cmt_string bin;
1463
1464  //  CmtSystem::dirname (library_name, bin);
1465  //  CmtSystem::get_dot_suffix (library_name, suffix);
1466  //  CmtSystem::basename (library_name, suffix, name);
1467 
1468  //  if (!CmtSystem::cd (bin)) return;
1469 
1470  //  cmt_string command;
1471 
1472  cmt_string command ("dumpbin /symbols");
1473  //  command += library_name;
1474  /*
1475  for (int i = 0; i < files.size (); i++)
1476    {
1477      command += " " + files[i];
1478    }
1479  */
1480  command += " " + files;
1481       
1482  WinDefAwk filter (name);
1483 
1484  filter.run (command, "SECT");
1485}
1486
1487//--------------------------------------------------
1488void Packager::begin ()
1489{
1490  m_package_name = "";
1491}
1492
1493void Packager::filter (const cmt_string& line)
1494{
1495  CmtSystem::cmt_string_vector words;
1496
1497  CmtSystem::split (line, " ", words);
1498  if (words.size () > 1)
1499    {
1500      cmt_string& w = words[0];
1501
1502      if (w == "package")
1503        {
1504          m_package_name = words[1];
1505
1506          int pos = m_package_name.find (";");
1507          if (pos != cmt_string::npos) m_package_name.erase (pos);
1508          m_package_name.replace_all (".", CmtSystem::file_separator ());
1509        }
1510    }
1511}
1512
1513cmt_string& Packager::package_name ()
1514{
1515  return (m_package_name);
1516}
1517
1518//------------------------------------------------------------------------
1519InGenerator::Buffer::Buffer ()
1520  : m_initialized (false), m_usecmt (true)
1521{ }
1522
1523//------------------------------------------------------------------------
1524void InGenerator::Buffer::set_names (const CmtSystem::cmt_string_vector& names)
1525{
1526  m_names = names;
1527}
1528
1529//------------------------------------------------------------------------
1530int InGenerator::Buffer::print (ostream& s)
1531{
1532  initialize ();
1533  s << m_buffer.str ();
1534  return 0;
1535}
1536
1537//------------------------------------------------------------------------
1538void InGenerator::Buffer::set_uses (bool usecmt)
1539{
1540  m_usecmt = usecmt;
1541}
1542
1543//------------------------------------------------------------------------
1544int InGenerator::Languages::initialize ()
1545{
1546  int errors (0);
1547  if (m_initialized) return - errors;
1548  m_initialized = true;
1549  for (int i = 0; i < m_names.size (); i++)
1550    {
1551      const cmt_string& name = m_names[i];
1552      Language& p (Language::find (name));
1553      if (Language::null () == p) { errors += 1; continue; }
1554      p.show (Requirements, m_buffer);
1555      /*
1556      if (!p->use->get_package ()->is_cmt ())
1557        p->print (Requirements, m_buffer);
1558      */
1559    }
1560  //  cerr << "Languages::initialize: " << m_buffer.str ();
1561  return - errors;
1562}
1563
1564//------------------------------------------------------------------------
1565int InGenerator::Fragments::initialize ()
1566{
1567  int errors (0);
1568  if (m_initialized) return - errors;
1569  m_initialized = true;
1570  for (int i = 0; i < m_names.size (); i++)
1571    {
1572      const cmt_string& name = m_names[i];
1573      Fragment* fragment (Fragment::find (name));
1574      if (0 == fragment) { errors += 1; continue; }
1575      if (m_usecmt || !fragment->use->get_package ()->is_cmt ())
1576        fragment->print (Requirements, m_buffer);
1577    }
1578  //  cerr << "initialize: " << m_buffer.str ();
1579  return - errors;
1580}
1581
1582//------------------------------------------------------------------------
1583InGenerator::InGenerator (bool usecmt)
1584  : m_usecmt (usecmt)
1585{
1586  CmtSystem::cmt_string_vector common;
1587  common.push_back ("make_header");
1588  common.push_back ("dependencies");
1589  common.push_back ("cleanup_header");
1590  m_common.set_names (common);
1591  m_common.set_uses (usecmt);
1592
1593  CmtSystem::cmt_string_vector application;
1594  application.push_back ("java_header");
1595  application.push_back ("application_header");
1596  application.push_back ("application");
1597  application.push_back ("check_java");
1598  application.push_back ("cleanup_application");
1599  application.push_back ("cleanup_objects");
1600  application.push_back ("check_application");
1601
1602  CmtSystem::cmt_string_vector application_library;
1603  application_library.push_back ("protos_header");
1604  application_library.push_back ("buildproto");
1605  application_library.push_back ("dependencies_and_triggers");
1606  application_library.push_back ("java");
1607  application_library.push_back ("java_copy");
1608  application_library.push_back ("cleanup");
1609  application_library.push_back ("cleanup_java");
1610  m_application_library.set_names (application_library);
1611  m_application_library.set_uses (usecmt);
1612
1613  CmtSystem::cmt_string_vector library;
1614  library.push_back ("jar_header");
1615  library.push_back ("library_header");
1616  library.push_back ("jar");
1617  library.push_back ("library_no_share");
1618  library.push_back ("library_no_static");
1619  library.push_back ("library");
1620  library.push_back ("cleanup_library");
1621
1622  CmtSystem::cmt_string_vector languages;
1623  Language::LanguageVector& Languages = Language::languages ();
1624  for (int n = 0; n < Languages.size (); n++)
1625    {
1626      Language& language = Languages[n];
1627      //language.setup_fragments ();
1628      application.push_back (language.fragment_name); //application.name ();
1629      library.push_back (language.fragment_name + "_library"); //library.name ();
1630      if (m_usecmt || !language.m_use->get_package ()->is_cmt ())
1631//       if (!language.m_use->get_package ()->is_cmt ())
1632        {
1633          languages.push_back (language.m_name);
1634        }
1635      else if (language.dependencies_options () !=
1636          language.dependencies_options_expanded ())
1637        {
1638          languages.push_back (language.m_name);
1639        }
1640    }
1641  m_languages.set_names (languages);
1642  m_languages.set_uses (usecmt);
1643  m_application.set_names (application);
1644  m_application.set_uses (usecmt);
1645  m_library.set_names (library);
1646  m_library.set_uses (usecmt);
1647 
1648  CmtSystem::cmt_string_vector document;
1649  document.push_back ("document_header");
1650  document.push_back ("dependency");
1651  m_document.set_names (document);
1652  m_document.set_uses (usecmt);
1653}
1654
1655//------------------------------------------------------------------------
1656int InGenerator::build (const Constituent& constituent, ostream& s)
1657{
1658  constituent.show (s);
1659  m_common.print (s);
1660
1661  switch (constituent.type)
1662    {
1663    case Application:
1664      m_application.print (s);
1665      m_application_library.print (s);
1666      m_languages.print (s);
1667      break;
1668    case Library:
1669      m_application_library.print (s);
1670      m_library.print (s);
1671      m_languages.print (s);
1672      break;
1673    case Document:
1674      Fragment* fragment (Fragment::find (constituent.generator));
1675      if (0 == fragment) return -1;
1676      if (m_usecmt || !fragment->use->get_package ()->is_cmt ())
1677        fragment->print (Requirements, s);
1678      if ("" != fragment->header)
1679        {
1680          Fragment* header (Fragment::find (fragment->header));
1681          if (0 == header) return -1;
1682          //      if (header->use != fragment->use &&
1683          if (m_usecmt || !header->use->get_package ()->is_cmt ())
1684            header->print (Requirements, s);
1685        }
1686      if ("" != fragment->trailer)
1687        {
1688          Fragment* trailer (Fragment::find (fragment->trailer));
1689          if (0 == trailer) return -1;
1690          //      if (trailer->use != fragment->use &&
1691          if (m_usecmt || !trailer->use->get_package ()->is_cmt ())
1692            trailer->print (Requirements, s);
1693        }
1694      m_document.print (s);
1695      break;
1696    }
1697
1698  return 0;
1699}
1700//------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.