1 // file      : xsd/cxx/parser/generator.cxx
2 // copyright : Copyright (c) 2005-2017 Code Synthesis Tools CC
3 // license   : GNU GPL v2 + exceptions; see accompanying LICENSE file
4 
5 #include <algorithm>
6 #include <iostream>
7 #include <fstream>
8 
9 #include <cutl/re.hxx>
10 
11 #include <cutl/compiler/code-stream.hxx>
12 #include <cutl/compiler/cxx-indenter.hxx>
13 #include <cutl/compiler/sloc-counter.hxx>
14 
15 #include <xsd-frontend/semantic-graph.hxx>
16 
17 #include <type-map/lexer.hxx>
18 #include <type-map/parser.hxx>
19 #include <type-map/type-map.hxx>
20 
21 #include <cxx/parser/elements.hxx>
22 #include <cxx/parser/generator.hxx>
23 
24 #include <cxx/parser/validator.hxx>
25 #include <cxx/parser/name-processor.hxx>
26 #include <cxx/parser/state-processor.hxx>
27 #include <cxx/parser/type-processor.hxx>
28 
29 #include <cxx/parser/parser-header.hxx>
30 #include <cxx/parser/parser-inline.hxx>
31 #include <cxx/parser/parser-source.hxx>
32 #include <cxx/parser/parser-forward.hxx>
33 
34 #include <cxx/parser/impl-header.hxx>
35 #include <cxx/parser/impl-source.hxx>
36 #include <cxx/parser/driver-source.hxx>
37 
38 #include <cxx/parser/element-validation-source.hxx>
39 #include <cxx/parser/attribute-validation-source.hxx>
40 #include <cxx/parser/characters-validation-source.hxx>
41 
42 #include <cxx/parser/options.hxx>
43 
44 #include "../../../libxsd/xsd/cxx/version.hxx"
45 
46 using std::endl;
47 using std::wcerr;
48 using std::wcout;
49 
50 using namespace XSDFrontend::SemanticGraph;
51 
52 //
53 //
54 typedef std::wifstream WideInputFileStream;
55 typedef std::wofstream WideOutputFileStream;
56 typedef std::ifstream NarrowInputFileStream;
57 
58 namespace CXX
59 {
60   namespace
61   {
62     char const copyright_gpl[] =
63     "// Copyright (c) 2005-2017 Code Synthesis Tools CC\n"
64     "//\n"
65     "// This program was generated by CodeSynthesis XSD, an XML Schema to\n"
66     "// C++ data binding compiler.\n"
67     "//\n"
68     "// This program is free software; you can redistribute it and/or modify\n"
69     "// it under the terms of the GNU General Public License version 2 as\n"
70     "// published by the Free Software Foundation.\n"
71     "//\n"
72     "// This program is distributed in the hope that it will be useful,\n"
73     "// but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
74     "// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
75     "// GNU General Public License for more details.\n"
76     "//\n"
77     "// You should have received a copy of the GNU General Public License\n"
78     "// along with this program; if not, write to the Free Software\n"
79     "// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
80     "//\n"
81     "// In addition, as a special exception, Code Synthesis Tools CC gives\n"
82     "// permission to link this program with the Xerces-C++ library (or with\n"
83     "// modified versions of Xerces-C++ that use the same license as Xerces-C++),\n"
84     "// and distribute linked combinations including the two. You must obey\n"
85     "// the GNU General Public License version 2 in all respects for all of\n"
86     "// the code used other than Xerces-C++. If you modify this copy of the\n"
87     "// program, you may extend this exception to your version of the program,\n"
88     "// but you are not obligated to do so. If you do not wish to do so, delete\n"
89     "// this exception statement from your version.\n"
90     "//\n"
91     "// Furthermore, Code Synthesis Tools CC makes a special exception for\n"
92     "// the Free/Libre and Open Source Software (FLOSS) which is described\n"
93     "// in the accompanying FLOSSE file.\n"
94     "//\n\n";
95 
96     char const copyright_proprietary[] =
97     "// Copyright (c) 2005-2017 Code Synthesis Tools CC\n"
98     "//\n"
99     "// This program was generated by CodeSynthesis XSD, an XML Schema\n"
100     "// to C++ data binding compiler, in the Proprietary License mode.\n"
101     "// You should have received a proprietary license from Code Synthesis\n"
102     "// Tools CC prior to generating this code. See the license text for\n"
103     "// conditions.\n"
104     "//\n\n";
105 
106     char const copyright_impl[] =
107     "// Not copyrighted - public domain.\n"
108     "//\n"
109     "// This sample parser implementation was generated by CodeSynthesis XSD,\n"
110     "// an XML Schema to C++ data binding compiler. You may use it in your\n"
111     "// programs without any restrictions.\n"
112     "//\n\n";
113   }
114 
115   void Parser::Generator::
usage()116   usage ()
117   {
118     CXX::Parser::options::print_usage (wcout);
119     CXX::options::print_usage (wcout);
120   }
121 
122   namespace
123   {
124     template <typename S>
125     void
open(S & ifs,NarrowString const & path)126     open (S& ifs, NarrowString const& path)
127     {
128       try
129       {
130         Path fs_path (path);
131         ifs.open (fs_path.string ().c_str (),
132                   std::ios_base::in | std::ios_base::binary);
133 
134         if (!ifs.is_open ())
135         {
136           wcerr << path.c_str () << ": error: unable to open in read mode"
137                 << endl;
138 
139           throw Parser::Generator::Failed ();
140         }
141       }
142       catch (InvalidPath const&)
143       {
144         wcerr << "error: '" << path.c_str () << "' is not a valid "
145               << "filesystem path" << endl;
146 
147         throw Parser::Generator::Failed ();
148       }
149     }
150 
151     void
append(WideOutputFileStream & os,NarrowString const & path,WideInputFileStream & default_is)152     append (WideOutputFileStream& os,
153             NarrowString const& path,
154             WideInputFileStream& default_is)
155     {
156       using std::ios_base;
157 
158       if (path)
159       {
160         WideInputFileStream is;
161         open (is, path);
162         os << is.rdbuf ();
163       }
164       else if (default_is.is_open ())
165       {
166         os << default_is.rdbuf ();
167         default_is.seekg (0, ios_base::beg);
168       }
169     }
170 
171     void
append(WideOutputFileStream & os,NarrowStrings const & primary,NarrowStrings const & def)172     append (WideOutputFileStream& os,
173             NarrowStrings const& primary,
174             NarrowStrings const& def)
175     {
176       NarrowStrings const& v (primary.empty () ? def : primary);
177 
178       for (NarrowStrings::const_iterator i (v.begin ()), e (v.end ());
179            i != e; ++i)
180       {
181         os << i->c_str () << endl;
182       }
183     }
184   }
185 
186 
187   size_t Parser::Generator::
generate(Parser::options const & ops,Schema & schema,Path const & file_path,bool fpt,StringLiteralMap const & string_literal_map,bool gen_driver,const WarningSet & disabled_warnings,FileList & file_list,AutoUnlinks & unlinks)188   generate (Parser::options const& ops,
189             Schema& schema,
190             Path const& file_path,
191             bool fpt,
192             StringLiteralMap const& string_literal_map,
193             bool gen_driver,
194             const WarningSet& disabled_warnings,
195             FileList& file_list,
196             AutoUnlinks& unlinks)
197   {
198     using std::ios_base;
199 
200     typedef cutl::re::regexsub Regex;
201 
202     try
203     {
204       bool generate_xml_schema (ops.generate_xml_schema ());
205 
206       // We could be compiling several schemas at once in which case
207       // handling of the --generate-xml-schema option gets tricky: we
208       // will need to rely on the presence of the --extern-xml-schema
209       // to tell us which (fake) schema file corresponds to XML Schema.
210       //
211       if (generate_xml_schema)
212       {
213         if (NarrowString name = ops.extern_xml_schema ())
214         {
215           if (file_path.string () != name)
216             generate_xml_schema = false;
217         }
218       }
219 
220       bool impl (!generate_xml_schema &&
221                     (ops.generate_noop_impl () ||
222                      ops.generate_print_impl ()));
223 
224       bool driver (gen_driver && !generate_xml_schema &&
225                       ops.generate_test_driver ());
226 
227       // Evaluate the graph for possibility of generating something useful.
228       //
229       {
230         Validator validator;
231         if (!validator.validate (
232               ops, schema, file_path, driver, disabled_warnings))
233           throw Failed ();
234       }
235 
236       // Process names.
237       //
238       {
239         NameProcessor proc;
240         proc.process (ops, schema, file_path, string_literal_map);
241       }
242 
243       bool validation ((ops.xml_parser () == "expat" ||
244                            ops.generate_validation ()) &&
245                           !ops.suppress_validation ());
246 
247       // Compute state machine info.
248       //
249       if (validation)
250       {
251         StateProcessor proc;
252         proc.process (schema, file_path);
253       }
254 
255       // Read-in type maps.
256       //
257       TypeMap::Namespaces type_map;
258       {
259         using namespace TypeMap;
260 
261         NarrowStrings const& files (ops.type_map ());
262 
263         for (NarrowStrings::const_iterator f (files.begin ());
264              f != files.end (); ++f )
265         {
266           NarrowInputFileStream ifs;
267           open (ifs, *f);
268 
269           Lexer l (ifs, *f);
270           TypeMap::Parser p (l, *f);
271 
272           if (!p.parse (type_map))
273             throw Failed ();
274         }
275 
276         // Add the built-in mappings at the end.
277         //
278 
279         // String-based types.
280         //
281         String char_type (ops.char_type ());
282         String string_type;
283 
284         if (char_type == L"char")
285           string_type = L"::std::string";
286         else if (char_type == L"wchar_t")
287           string_type = L"::std::wstring";
288         else
289           string_type = L"::std::basic_string< " + char_type + L" >";
290 
291         String xns;
292         String auto_ptr;
293         {
294           Context ctx (std::wcerr, schema, file_path, ops, 0, 0, 0, 0);
295           xns = ctx.xs_ns_name ();
296           auto_ptr = ctx.auto_ptr;
297         }
298 
299         String buffer (auto_ptr + L"< " + xns + L"::buffer >");
300         TypeMap::Namespace xsd ("http://www\\.w3\\.org/2001/XMLSchema");
301 
302         xsd.types_push_back ("string", string_type);
303         xsd.types_push_back ("normalizedString", string_type);
304         xsd.types_push_back ("token", string_type);
305         xsd.types_push_back ("Name", string_type);
306         xsd.types_push_back ("NMTOKEN", string_type);
307         xsd.types_push_back ("NMTOKENS", xns + L"::string_sequence");
308         xsd.types_push_back ("NCName", string_type);
309 
310         xsd.types_push_back ("ID", string_type);
311         xsd.types_push_back ("IDREF", string_type);
312         xsd.types_push_back ("IDREFS", xns + L"::string_sequence");
313 
314         xsd.types_push_back ("language", string_type);
315         xsd.types_push_back ("anyURI", string_type);
316         xsd.types_push_back ("QName", xns + L"::qname");
317 
318         xsd.types_push_back ("base64Binary", buffer, buffer);
319         xsd.types_push_back ("hexBinary", buffer, buffer);
320 
321         xsd.types_push_back ("gDay", xns + L"::gday");
322         xsd.types_push_back ("gMonth", xns + L"::gmonth");
323         xsd.types_push_back ("gYear", xns + L"::gyear");
324         xsd.types_push_back ("gMonthDay", xns + L"::gmonth_day");
325         xsd.types_push_back ("gYearMonth", xns + L"::gyear_month");
326         xsd.types_push_back ("date", xns + L"::date");
327         xsd.types_push_back ("time", xns + L"::time");
328         xsd.types_push_back ("dateTime", xns + L"::date_time");
329         xsd.types_push_back ("duration", xns + L"::duration");
330 
331         // Fundamental C++ types.
332         //
333         xsd.types_push_back ("boolean", "bool", "bool");
334 
335         xsd.types_push_back ("byte", "signed char", "signed char");
336         xsd.types_push_back ("unsignedByte",
337                              "unsigned char",
338                              "unsigned char");
339 
340         xsd.types_push_back ("short", "short", "short");
341         xsd.types_push_back ("unsignedShort",
342                              "unsigned short",
343                              "unsigned short");
344 
345         xsd.types_push_back ("int", "int", "int");
346         xsd.types_push_back ("unsignedInt", "unsigned int", "unsigned int");
347 
348         xsd.types_push_back ("long", "long long", "long long");
349         xsd.types_push_back ("unsignedLong",
350                              "unsigned long long",
351                              "unsigned long long");
352 
353         xsd.types_push_back ("integer", "long long", "long long");
354 
355         xsd.types_push_back ("negativeInteger", "long long", "long long");
356         xsd.types_push_back ("nonPositiveInteger", "long long", "long long");
357 
358         xsd.types_push_back ("positiveInteger",
359                              "unsigned long long",
360                              "unsigned long long");
361         xsd.types_push_back ("nonNegativeInteger",
362                              "unsigned long long",
363                              "unsigned long long");
364 
365         xsd.types_push_back ("float", "float", "float");
366         xsd.types_push_back ("double", "double", "double");
367         xsd.types_push_back ("decimal", "double", "double");
368 
369         type_map.push_back (xsd);
370 
371         // Everything else maps to void.
372         //
373         TypeMap::Namespace rest (".*");
374         rest.types_push_back (".*", "void", "void");
375         type_map.push_back (rest);
376       }
377 
378       // Process types.
379       //
380       {
381         TypeProcessor proc;
382         proc.process (ops, schema, gen_driver, type_map);
383       }
384 
385       //
386       //
387       bool inline_ (ops.generate_inline () && !generate_xml_schema);
388       bool source (!generate_xml_schema);
389 
390       // Generate code.
391       //
392       NarrowString name (file_path.leaf ().string ());
393       NarrowString skel_suffix (ops.skel_file_suffix ());
394       NarrowString impl_suffix (ops.impl_file_suffix ());
395 
396       NarrowString hxx_suffix (ops.hxx_suffix ());
397       NarrowString ixx_suffix (ops.ixx_suffix ());
398       NarrowString cxx_suffix (ops.cxx_suffix ());
399 
400       Regex hxx_expr (
401         ops.hxx_regex ().empty ()
402         ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + hxx_suffix + "#"
403         : ops.hxx_regex ());
404 
405       Regex ixx_expr (
406         ops.ixx_regex ().empty ()
407         ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + ixx_suffix + "#"
408         : ops.ixx_regex ());
409 
410       Regex cxx_expr (
411         ops.cxx_regex ().empty ()
412         ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + cxx_suffix + "#"
413         : ops.cxx_regex ());
414 
415 
416       Regex hxx_impl_expr;
417       Regex cxx_impl_expr;
418       Regex cxx_driver_expr;
419 
420       if (impl || driver)
421       {
422         hxx_impl_expr =
423           "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + hxx_suffix + "#";
424 
425         cxx_impl_expr =
426           "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + cxx_suffix + "#";
427 
428         cxx_driver_expr =
429           "#^(.+?)(\\.[^./\\\\]+)?$#$1-driver" + cxx_suffix + "#";
430       }
431 
432       if (!hxx_expr.match (name))
433       {
434         wcerr << "error: header expression '" <<
435           hxx_expr.regex ().str ().c_str () << "' does not match '" <<
436           name.c_str () << "'" << endl;
437         throw Failed ();
438       }
439 
440       if (inline_ && !ixx_expr.match (name))
441       {
442         wcerr << "error: inline expression '" <<
443           ixx_expr.regex ().str ().c_str () << "' does not match '" <<
444           name.c_str () << "'" << endl;
445         throw Failed ();
446       }
447 
448       if (source && !cxx_expr.match (name))
449       {
450         wcerr << "error: source expression '" <<
451           cxx_expr.regex ().str ().c_str () << "' does not match '" <<
452           name.c_str () << "'" << endl;
453         throw Failed ();
454       }
455 
456       if (impl || driver)
457       {
458         if (!hxx_impl_expr.match (name))
459         {
460           wcerr << "error: implementation header expression '" <<
461             hxx_impl_expr.regex ().str ().c_str () << "' does not match '" <<
462             name.c_str () << "'" << endl;
463           throw Failed ();
464         }
465 
466         if (!cxx_impl_expr.match (name))
467         {
468           wcerr << "error: implementation source expression '" <<
469             cxx_impl_expr.regex ().str ().c_str () << "' does not match '" <<
470             name.c_str () << "'" << endl;
471           throw Failed ();
472         }
473 
474         if (!cxx_driver_expr.match (name))
475         {
476           wcerr << "error: driver source expression '" <<
477             cxx_driver_expr.regex ().str ().c_str () << "' does not match '" <<
478             name.c_str () << "'" << endl;
479           throw Failed ();
480         }
481       }
482 
483       NarrowString hxx_name (hxx_expr.replace (name));
484       NarrowString ixx_name (inline_ ? ixx_expr.replace (name) : NarrowString ());
485       NarrowString cxx_name (source ? cxx_expr.replace (name) : NarrowString ());
486 
487       NarrowString hxx_impl_name;
488       NarrowString cxx_impl_name;
489       NarrowString cxx_driver_name;
490 
491       if (impl || driver)
492       {
493         hxx_impl_name = hxx_impl_expr.replace (name);
494         cxx_impl_name = cxx_impl_expr.replace (name);
495         cxx_driver_name = cxx_driver_expr.replace (name);
496       }
497 
498       Path hxx_path (hxx_name);
499       Path ixx_path (ixx_name);
500       Path cxx_path (cxx_name);
501 
502       Path hxx_impl_path;
503       Path cxx_impl_path;
504       Path cxx_driver_path;
505 
506       if (impl || driver)
507       {
508         hxx_impl_path = Path (hxx_impl_name);
509         cxx_impl_path = Path (cxx_impl_name);
510         cxx_driver_path = Path (cxx_driver_name);
511       }
512 
513       Path out_dir;
514 
515       if (NarrowString dir = ops.output_dir ())
516       {
517         try
518         {
519           out_dir = Path (dir);
520         }
521         catch (InvalidPath const&)
522         {
523           wcerr << dir.c_str () << ": error: invalid path" << endl;
524           throw Failed ();
525         }
526       }
527 
528       if (fpt && !generate_xml_schema)
529       {
530         // In the file-per-type mode the schema files are always local
531         // unless the user added the directory so that we propagate this
532         // to the output files.
533         //
534         Path fpt_dir (file_path.directory ());
535 
536         if (!fpt_dir.empty ())
537           out_dir /= fpt_dir;
538       }
539 
540       if (!out_dir.empty ())
541       {
542         hxx_path = out_dir / hxx_path;
543         ixx_path = out_dir / ixx_path;
544         cxx_path = out_dir / cxx_path;
545 
546         if (impl || driver)
547         {
548           hxx_impl_path = out_dir / hxx_impl_path;
549           cxx_impl_path = out_dir / cxx_impl_path;
550           cxx_driver_path = out_dir /cxx_driver_path;
551         }
552       }
553 
554       // Open the impl files first so that if open fails, the skel files
555       // are not deleted.
556       //
557       WideOutputFileStream hxx_impl;
558       WideOutputFileStream cxx_impl;
559       WideOutputFileStream cxx_driver;
560 
561       if (impl)
562       {
563         if (!ops.force_overwrite ())
564         {
565           WideInputFileStream tmp (
566             hxx_impl_path.string ().c_str (), ios_base::in);
567 
568           if (tmp.is_open ())
569           {
570             wcerr << hxx_impl_path << ": error: cowardly refusing to " <<
571               "overwrite an existing file" << endl;
572             throw Failed ();
573           }
574 
575           tmp.close ();
576         }
577 
578         hxx_impl.open (hxx_impl_path.string ().c_str (), ios_base::out);
579 
580         if (!hxx_impl.is_open ())
581         {
582           wcerr << hxx_impl_path << ": error: unable to open in write mode"
583                 << endl;
584           throw Failed ();
585         }
586 
587         unlinks.add (hxx_impl_path);
588         file_list.push_back (hxx_impl_path.string ());
589 
590         if (!ops.force_overwrite ())
591         {
592           WideInputFileStream tmp (
593             cxx_impl_path.string ().c_str (), ios_base::in);
594 
595           if (tmp.is_open ())
596           {
597             wcerr << cxx_impl_path << ": error: cowardly refusing to " <<
598               "overwrite an existing file" << endl;
599             throw Failed ();
600           }
601 
602           tmp.close ();
603         }
604 
605         cxx_impl.open (cxx_impl_path.string ().c_str (), ios_base::out);
606 
607         if (!cxx_impl.is_open ())
608         {
609           wcerr << cxx_impl_path << ": error: unable to open in write mode"
610                 << endl;
611           throw Failed ();
612         }
613 
614         unlinks.add (cxx_impl_path);
615         file_list.push_back (cxx_impl_path.string ());
616       }
617 
618       if (driver)
619       {
620         if (!ops.force_overwrite ())
621         {
622           WideInputFileStream tmp (
623             cxx_driver_path.string ().c_str (), ios_base::in);
624 
625           if (tmp.is_open ())
626           {
627             wcerr << cxx_driver_path << ": error: cowardly refusing to " <<
628               "overwrite an existing file" << endl;
629             throw Failed ();
630           }
631 
632           tmp.close ();
633         }
634 
635         cxx_driver.open (cxx_driver_path.string ().c_str (), ios_base::out);
636 
637         if (!cxx_driver.is_open ())
638         {
639           wcerr << cxx_driver_path << ": error: unable to open in write " <<
640             "mode" << endl;
641           throw Failed ();
642         }
643 
644         unlinks.add (cxx_driver_path);
645         file_list.push_back (cxx_driver_path.string ());
646       }
647 
648       // Open the skel files.
649       //
650       WideOutputFileStream hxx (hxx_path.string ().c_str (), ios_base::out);
651       WideOutputFileStream ixx;
652       WideOutputFileStream cxx;
653 
654       if (!hxx.is_open ())
655       {
656         wcerr << hxx_path << ": error: unable to open in write mode" << endl;
657         throw Failed ();
658       }
659 
660       unlinks.add (hxx_path);
661       file_list.push_back (hxx_path.string ());
662 
663       if (inline_)
664       {
665         ixx.open (ixx_path.string ().c_str (), ios_base::out);
666 
667         if (!ixx.is_open ())
668         {
669           wcerr << ixx_path << ": error: unable to open in write mode" << endl;
670           throw Failed ();
671         }
672 
673         unlinks.add (ixx_path);
674         file_list.push_back (ixx_path.string ());
675       }
676 
677 
678       if (source)
679       {
680         cxx.open (cxx_path.string ().c_str (), ios_base::out);
681 
682         if (!cxx.is_open ())
683         {
684           wcerr << cxx_path << ": error: unable to open in write mode" << endl;
685           throw Failed ();
686         }
687 
688         unlinks.add (cxx_path);
689         file_list.push_back (cxx_path.string ());
690       }
691 
692       // Print copyright and license.
693       //
694       char const* copyright (
695         ops.proprietary_license () ? copyright_proprietary : copyright_gpl);
696 
697       hxx << copyright;
698 
699       if (inline_)
700         ixx << copyright;
701 
702       if (source)
703         cxx << copyright;
704 
705       if (impl)
706       {
707         hxx_impl << copyright_impl;
708         cxx_impl << copyright_impl;
709       }
710 
711       if (driver)
712         cxx_driver << copyright_impl;
713 
714       // Prologue.
715       //
716       WideInputFileStream prologue;
717       {
718         NarrowString name (ops.prologue_file ());
719 
720         if (name)
721           open (prologue, name);
722       }
723 
724       // Epilogue.
725       //
726       WideInputFileStream epilogue;
727       {
728         NarrowString name (ops.epilogue_file ());
729 
730         if (name)
731           open (epilogue, name);
732       }
733 
734       // SLOC counter.
735       //
736       size_t sloc_total (0);
737       bool show_sloc (ops.show_sloc ());
738 
739       typedef
740       compiler::ostream_filter<compiler::cxx_indenter, wchar_t>
741       ind_filter;
742 
743       typedef
744       compiler::ostream_filter<compiler::sloc_counter, wchar_t>
745       sloc_filter;
746 
747       //
748       //
749       Regex guard_expr ("/([a-z])([A-Z])/$1_$2/"); // Split words.
750 
751       NarrowString guard_prefix (ops.guard_prefix ());
752 
753       if (!guard_prefix)
754         guard_prefix = file_path.directory ().string ();
755 
756       if (guard_prefix)
757         guard_prefix += '_';
758 
759       // HXX
760       //
761       {
762         Context ctx (hxx,
763                      schema,
764                      file_path,
765                      ops,
766                      &string_literal_map,
767                      &hxx_expr,
768                      &ixx_expr,
769                      &hxx_impl_expr);
770 
771         sloc_filter sloc (hxx);
772 
773         String guard (guard_expr.replace (guard_prefix + hxx_name));
774         guard = ctx.escape (guard); // Make it a C++ id.
775         std::transform (guard.begin (), guard.end(), guard.begin (), upcase);
776 
777         hxx << "#ifndef " << guard << endl
778             << "#define " << guard << endl
779             << endl;
780 
781         if (ctx.std >= cxx_version::cxx11)
782         {
783           hxx << "#ifndef XSD_CXX11" << endl
784               << "#define XSD_CXX11" << endl
785               << "#endif" << endl
786               << endl;
787         }
788 
789         // Copy prologue.
790         //
791         hxx << "// Begin prologue." << endl
792             << "//" << endl;
793 
794         append (hxx, ops.hxx_prologue (), ops.prologue ());
795         append (hxx, ops.hxx_prologue_file (), prologue);
796 
797         hxx << "//" << endl
798             << "// End prologue." << endl
799             << endl;
800 
801         // Version check.
802         //
803         hxx << "#include <xsd/cxx/config.hxx>" << endl
804             << endl
805             << "#if (XSD_INT_VERSION != " << XSD_INT_VERSION << "L)" << endl
806             << "#error XSD runtime version mismatch" << endl
807             << "#endif" << endl
808             << endl;
809 
810         hxx << "#include <xsd/cxx/pre.hxx>" << endl
811             << endl;
812 
813         // Generate.
814         //
815         {
816           ind_filter ind (hxx); // We don't want to indent prologues/epilogues.
817 
818           if (!generate_xml_schema)
819             generate_parser_forward (ctx);
820 
821           generate_parser_header (ctx, generate_xml_schema);
822         }
823 
824         if (inline_)
825           hxx << "#include " << ctx.process_include_path (ixx_name) << endl;
826 
827         hxx << "#include <xsd/cxx/post.hxx>" << endl
828             << endl;
829 
830         // Copy epilogue.
831         //
832         hxx << "// Begin epilogue." << endl
833             << "//" << endl;
834 
835         append (hxx, ops.hxx_epilogue_file (), epilogue);
836         append (hxx, ops.hxx_epilogue (), ops.epilogue ());
837 
838         hxx << "//" << endl
839             << "// End epilogue." << endl
840             << endl;
841 
842         hxx << "#endif // " << guard << endl;
843 
844         if (show_sloc)
845           wcerr << hxx_path << ": " << sloc.stream ().count () << endl;
846 
847         sloc_total += sloc.stream ().count ();
848       }
849 
850 
851       // IXX
852       //
853       if (inline_)
854       {
855         Context ctx (ixx,
856                      schema,
857                      file_path,
858                      ops,
859                      &string_literal_map,
860                      &hxx_expr,
861                      &ixx_expr,
862                      &hxx_impl_expr);
863 
864         sloc_filter sloc (ixx);
865 
866         // Copy prologue.
867         //
868         ixx << "// Begin prologue." << endl
869             << "//" << endl;
870 
871         append (ixx, ops.ixx_prologue (), ops.prologue ());
872         append (ixx, ops.ixx_prologue_file (), prologue);
873 
874         ixx << "//" << endl
875             << "// End prologue." << endl
876             << endl;
877 
878         // Generate.
879         //
880         {
881           ind_filter ind (ixx); // We don't want to indent prologues/epilogues.
882           generate_parser_inline (ctx);
883         }
884 
885         // Copy epilogue.
886         //
887         ixx << "// Begin epilogue." << endl
888             << "//" << endl;
889 
890         append (ixx, ops.ixx_epilogue_file (), epilogue);
891         append (ixx, ops.ixx_epilogue (), ops.epilogue ());
892 
893         ixx << "//" << endl
894             << "// End epilogue." << endl
895             << endl;
896 
897         if (show_sloc)
898           wcerr << ixx_path << ": " << sloc.stream ().count () << endl;
899 
900         sloc_total += sloc.stream ().count ();
901       }
902 
903 
904       // CXX
905       //
906       if (source)
907       {
908         Context ctx (cxx,
909                      schema,
910                      file_path,
911                      ops,
912                      &string_literal_map,
913                      &hxx_expr,
914                      &ixx_expr,
915                      &hxx_impl_expr);
916 
917         sloc_filter sloc (cxx);
918 
919         // Copy prologue.
920         //
921         cxx << "// Begin prologue." << endl
922             << "//" << endl;
923 
924         append (cxx, ops.cxx_prologue (), ops.prologue ());
925         append (cxx, ops.cxx_prologue_file (), prologue);
926 
927         cxx << "//" << endl
928             << "// End prologue." << endl
929             << endl;
930 
931         cxx << "#include <xsd/cxx/pre.hxx>" << endl
932              << endl;
933 
934         cxx << "#include " << ctx.process_include_path (hxx_name) << endl
935             << endl;
936 
937         // Generate.
938         //
939         {
940           ind_filter ind (cxx); // We don't want to indent prologues/epilogues.
941 
942           if (!inline_)
943             generate_parser_inline (ctx);
944 
945           generate_parser_source (ctx);
946 
947           if (validation)
948           {
949             generate_element_validation_source (ctx);
950             generate_attribute_validation_source (ctx);
951             generate_characters_validation_source (ctx);
952           }
953         }
954 
955         cxx << "#include <xsd/cxx/post.hxx>" << endl
956             << endl;
957 
958         // Copy epilogue.
959         //
960         cxx << "// Begin epilogue." << endl
961             << "//" << endl;
962 
963         append (cxx, ops.cxx_epilogue_file (), epilogue);
964         append (cxx, ops.cxx_epilogue (), ops.epilogue ());
965 
966         cxx << "//" << endl
967             << "// End epilogue." << endl
968             << endl;
969 
970         if (show_sloc)
971           wcerr << cxx_path << ": " << sloc.stream ().count () << endl;
972 
973         sloc_total += sloc.stream ().count ();
974       }
975 
976       // HXX impl
977       //
978       if (impl)
979       {
980         Context ctx (hxx_impl,
981                      schema,
982                      file_path,
983                      ops,
984                      &string_literal_map,
985                      &hxx_expr,
986                      &ixx_expr,
987                      &hxx_impl_expr);
988 
989         String guard (guard_expr.replace (guard_prefix + hxx_impl_name));
990         guard = ctx.escape (guard); // Make it a C++ id.
991         std::transform (guard.begin (), guard.end(), guard.begin (), upcase);
992 
993         hxx_impl << "#ifndef " << guard << endl
994                  << "#define " << guard << endl
995                  << endl;
996 
997         hxx_impl << "#include " << ctx.process_include_path (hxx_name)
998                  << endl << endl;
999 
1000         {
1001           ind_filter ind (hxx_impl);
1002           generate_impl_header (ctx);
1003         }
1004 
1005         hxx_impl << "#endif // " << guard << endl;
1006       }
1007 
1008       // CXX impl
1009       //
1010       if (impl)
1011       {
1012         Context ctx (cxx_impl,
1013                      schema,
1014                      file_path,
1015                      ops,
1016                      &string_literal_map,
1017                      &hxx_expr,
1018                      &ixx_expr,
1019                      &hxx_impl_expr);
1020 
1021         cxx_impl << "#include " << ctx.process_include_path (hxx_impl_name)
1022                  << endl << endl;
1023 
1024         {
1025           ind_filter ind (cxx_impl);
1026           generate_impl_source (ctx);
1027         }
1028       }
1029 
1030       // CXX driver
1031       //
1032       if (driver)
1033       {
1034         Context ctx (cxx_driver,
1035                      schema,
1036                      file_path,
1037                      ops,
1038                      &string_literal_map,
1039                      &hxx_expr,
1040                      &ixx_expr,
1041                      &hxx_impl_expr);
1042 
1043         cxx_driver << "#include " << ctx.process_include_path (hxx_impl_name)
1044                    << endl << endl;
1045 
1046         {
1047           ind_filter ind (cxx_driver);
1048           generate_driver_source (ctx);
1049         }
1050       }
1051 
1052       return sloc_total;
1053     }
1054     catch (UnrepresentableCharacter const& e)
1055     {
1056       wcerr << "error: character at position " << e.position () << " "
1057             << "in string '" << e.string () << "' is unrepresentable in "
1058             << "the target encoding" << endl;
1059 
1060       wcerr << "info: use the --custom-literals option to provide custom "
1061             << "string literals mapping" << endl;
1062 
1063       throw Failed ();
1064     }
1065     catch (NoNamespaceMapping const& e)
1066     {
1067       wcerr << e.file () << ":" << e.line () << ":" << e.column ()
1068             << ": error: unable to map XML Schema namespace '" << e.ns ()
1069             << "' to C++ namespace" << endl;
1070 
1071       wcerr << e.file () << ":" << e.line () << ":" << e.column ()
1072             << ": info: use the --namespace-map or --namespace-regex option "
1073             << "to provide custom mapping" << endl;
1074 
1075       throw Failed ();
1076     }
1077     catch (InvalidNamespaceMapping const& e)
1078     {
1079       wcerr << "error: invalid XML to C++ namespace mapping specified: "
1080             << "'" << e.mapping () << "': " << e.reason () << endl;
1081 
1082       throw Failed ();
1083     }
1084     catch (cutl::re::format const& e)
1085     {
1086       wcerr << "error: invalid regex: '" <<
1087         e.regex ().c_str () << "': " <<
1088         e.description ().c_str () << endl;
1089 
1090       throw Failed ();
1091     }
1092     catch (cutl::re::wformat const& e)
1093     {
1094       wcerr << "error: invalid regex: '" <<
1095         e.regex () << "': " << e.description ().c_str () << endl;
1096 
1097       throw Failed ();
1098     }
1099   }
1100 }
1101