| 1 | /*============================================================================= |
|---|
| 2 | Copyright (c) 2002 2004 Joel de Guzman |
|---|
| 3 | Copyright (c) 2004 Eric Niebler |
|---|
| 4 | http://spirit.sourceforge.net/ |
|---|
| 5 | |
|---|
| 6 | Use, modification and distribution is subject to the Boost Software |
|---|
| 7 | License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
|---|
| 8 | http://www.boost.org/LICENSE_1_0.txt) |
|---|
| 9 | =============================================================================*/ |
|---|
| 10 | #include "../block.hpp" |
|---|
| 11 | #include "../doc_info.hpp" |
|---|
| 12 | #include "./post_process.hpp" |
|---|
| 13 | #include "utils.hpp" |
|---|
| 14 | #include "actions.hpp" |
|---|
| 15 | #include <boost/spirit/iterator/position_iterator.hpp> |
|---|
| 16 | #include <boost/program_options.hpp> |
|---|
| 17 | #include <boost/filesystem/path.hpp> |
|---|
| 18 | #include <boost/filesystem/operations.hpp> |
|---|
| 19 | #include <boost/ref.hpp> |
|---|
| 20 | |
|---|
| 21 | #include <stdexcept> |
|---|
| 22 | #include <fstream> |
|---|
| 23 | #include <iostream> |
|---|
| 24 | #include <sstream> |
|---|
| 25 | |
|---|
| 26 | #if (defined(BOOST_MSVC) && (BOOST_MSVC <= 1310)) |
|---|
| 27 | #pragma warning(disable:4355) |
|---|
| 28 | #endif |
|---|
| 29 | |
|---|
| 30 | #define QUICKBOOK_VERSION "Quickbook Version 1.3" |
|---|
| 31 | |
|---|
| 32 | namespace quickbook |
|---|
| 33 | { |
|---|
| 34 | using namespace boost::spirit; |
|---|
| 35 | namespace fs = boost::filesystem; |
|---|
| 36 | tm* current_time; // the current time |
|---|
| 37 | tm* current_gm_time; // the current UTC time |
|---|
| 38 | bool debug_mode; // for quickbook developers only |
|---|
| 39 | unsigned qbk_major_version = 0; |
|---|
| 40 | unsigned qbk_minor_version = 0; |
|---|
| 41 | unsigned qbk_version_n = 0; // qbk_major_version * 100 + qbk_minor_version |
|---|
| 42 | bool ms_errors = false; // output errors/warnings as if for VS |
|---|
| 43 | |
|---|
| 44 | /////////////////////////////////////////////////////////////////////////// |
|---|
| 45 | // |
|---|
| 46 | // Load a file |
|---|
| 47 | // |
|---|
| 48 | /////////////////////////////////////////////////////////////////////////// |
|---|
| 49 | static int |
|---|
| 50 | load(char const* filename, file_storage& storage) |
|---|
| 51 | { |
|---|
| 52 | using std::cerr; |
|---|
| 53 | using std::endl; |
|---|
| 54 | using std::ios; |
|---|
| 55 | using std::ifstream; |
|---|
| 56 | using std::istream_iterator; |
|---|
| 57 | |
|---|
| 58 | ifstream in(filename, std::ios_base::in); |
|---|
| 59 | |
|---|
| 60 | if (!in) |
|---|
| 61 | { |
|---|
| 62 | detail::outerr(filename,1) |
|---|
| 63 | << "Could not open input file." << endl; |
|---|
| 64 | return 1; |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | // Turn off white space skipping on the stream |
|---|
| 68 | in.unsetf(ios::skipws); |
|---|
| 69 | |
|---|
| 70 | std::copy( |
|---|
| 71 | istream_iterator<char>(in), |
|---|
| 72 | istream_iterator<char>(), |
|---|
| 73 | std::back_inserter(storage)); |
|---|
| 74 | |
|---|
| 75 | // ensure that we have enough trailing newlines to eliminate |
|---|
| 76 | // the need to check for end of file in the grammar. |
|---|
| 77 | storage.push_back('\n'); |
|---|
| 78 | storage.push_back('\n'); |
|---|
| 79 | return 0; |
|---|
| 80 | } |
|---|
| 81 | |
|---|
| 82 | /////////////////////////////////////////////////////////////////////////// |
|---|
| 83 | // |
|---|
| 84 | // Parse a file |
|---|
| 85 | // |
|---|
| 86 | /////////////////////////////////////////////////////////////////////////// |
|---|
| 87 | int |
|---|
| 88 | parse(char const* filein_, actions& actor, bool ignore_docinfo) |
|---|
| 89 | { |
|---|
| 90 | using std::cerr; |
|---|
| 91 | using std::vector; |
|---|
| 92 | using std::string; |
|---|
| 93 | |
|---|
| 94 | file_storage storage; |
|---|
| 95 | int err = quickbook::load(filein_, storage); |
|---|
| 96 | if (err != 0) |
|---|
| 97 | return err; |
|---|
| 98 | |
|---|
| 99 | typedef position_iterator<file_storage::const_iterator> iterator_type; |
|---|
| 100 | iterator_type first(storage.begin(), storage.end(), filein_); |
|---|
| 101 | iterator_type last(storage.end(), storage.end()); |
|---|
| 102 | |
|---|
| 103 | doc_info_grammar<actions> l(actor); |
|---|
| 104 | parse_info<iterator_type> info = parse(first, last, l); |
|---|
| 105 | |
|---|
| 106 | if (info.hit || ignore_docinfo) |
|---|
| 107 | { |
|---|
| 108 | pre(actor.out, actor, ignore_docinfo); |
|---|
| 109 | |
|---|
| 110 | block_grammar<actions> g(actor); |
|---|
| 111 | info = parse(info.hit ? info.stop : first, last, g); |
|---|
| 112 | if (info.full) |
|---|
| 113 | { |
|---|
| 114 | post(actor.out, actor, ignore_docinfo); |
|---|
| 115 | } |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | if (!info.full) |
|---|
| 119 | { |
|---|
| 120 | file_position const pos = info.stop.get_position(); |
|---|
| 121 | detail::outerr(pos.file,pos.line) |
|---|
| 122 | << "Syntax Error near column " << pos.column << ".\n"; |
|---|
| 123 | return 1; |
|---|
| 124 | } |
|---|
| 125 | |
|---|
| 126 | return 0; |
|---|
| 127 | } |
|---|
| 128 | |
|---|
| 129 | static int |
|---|
| 130 | parse(char const* filein_, fs::path const& outdir, std::ostream& out, bool ignore_docinfo = false) |
|---|
| 131 | { |
|---|
| 132 | actions actor(filein_, outdir, out); |
|---|
| 133 | bool r = parse(filein_, actor); |
|---|
| 134 | if (actor.level != 0) |
|---|
| 135 | detail::outwarn(filein_,1) |
|---|
| 136 | << "Warning missing [endsect] detected at end of file." |
|---|
| 137 | << std::endl; |
|---|
| 138 | return r; |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | static int |
|---|
| 142 | parse( |
|---|
| 143 | char const* filein_ |
|---|
| 144 | , char const* fileout_ |
|---|
| 145 | , int indent |
|---|
| 146 | , int linewidth |
|---|
| 147 | , bool pretty_print) |
|---|
| 148 | { |
|---|
| 149 | int result = 0; |
|---|
| 150 | std::ofstream fileout(fileout_); |
|---|
| 151 | fs::path outdir = fs::path(fileout_, fs::native).branch_path(); |
|---|
| 152 | if (outdir.empty()) |
|---|
| 153 | outdir = "."; |
|---|
| 154 | if (pretty_print) |
|---|
| 155 | { |
|---|
| 156 | std::stringstream buffer; |
|---|
| 157 | result = parse(filein_, outdir, buffer); |
|---|
| 158 | if (result == 0) |
|---|
| 159 | { |
|---|
| 160 | post_process(buffer.str(), fileout, indent, linewidth); |
|---|
| 161 | } |
|---|
| 162 | } |
|---|
| 163 | else |
|---|
| 164 | { |
|---|
| 165 | result = parse(filein_, outdir, fileout); |
|---|
| 166 | } |
|---|
| 167 | return result; |
|---|
| 168 | } |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | /////////////////////////////////////////////////////////////////////////// |
|---|
| 172 | // |
|---|
| 173 | // Main program |
|---|
| 174 | // |
|---|
| 175 | /////////////////////////////////////////////////////////////////////////// |
|---|
| 176 | int |
|---|
| 177 | main(int argc, char* argv[]) |
|---|
| 178 | { |
|---|
| 179 | try |
|---|
| 180 | { |
|---|
| 181 | using boost::program_options::options_description; |
|---|
| 182 | using boost::program_options::variables_map; |
|---|
| 183 | using boost::program_options::store; |
|---|
| 184 | using boost::program_options::parse_command_line; |
|---|
| 185 | using boost::program_options::command_line_parser; |
|---|
| 186 | using boost::program_options::notify; |
|---|
| 187 | using boost::program_options::value; |
|---|
| 188 | using boost::program_options::positional_options_description; |
|---|
| 189 | |
|---|
| 190 | // First thing, the filesystem should record the current working directory. |
|---|
| 191 | boost::filesystem::initial_path(); |
|---|
| 192 | |
|---|
| 193 | options_description desc("Allowed options"); |
|---|
| 194 | desc.add_options() |
|---|
| 195 | ("help", "produce help message") |
|---|
| 196 | ("version", "print version string") |
|---|
| 197 | ("no-pretty-print", "disable XML pretty printing") |
|---|
| 198 | ("indent", value<int>(), "indent spaces") |
|---|
| 199 | ("linewidth", value<int>(), "line width") |
|---|
| 200 | ("input-file", value<std::string>(), "input file") |
|---|
| 201 | ("output-file", value<std::string>(), "output file") |
|---|
| 202 | ("debug", "debug mode (for developers)") |
|---|
| 203 | ("ms-errors", "use Microsoft Visual Studio style error & warn message format") |
|---|
| 204 | ; |
|---|
| 205 | |
|---|
| 206 | positional_options_description p; |
|---|
| 207 | p.add("input-file", -1); |
|---|
| 208 | |
|---|
| 209 | variables_map vm; |
|---|
| 210 | int indent = -1; |
|---|
| 211 | int linewidth = -1; |
|---|
| 212 | bool pretty_print = true; |
|---|
| 213 | store(command_line_parser(argc, argv).options(desc).positional(p).run(), vm); |
|---|
| 214 | notify(vm); |
|---|
| 215 | |
|---|
| 216 | if (vm.count("help")) |
|---|
| 217 | { |
|---|
| 218 | std::cout << desc << "\n"; |
|---|
| 219 | return 0; |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | if (vm.count("version")) |
|---|
| 223 | { |
|---|
| 224 | std::cout << QUICKBOOK_VERSION << std::endl; |
|---|
| 225 | return 0; |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | if (vm.count("ms-errors")) |
|---|
| 229 | quickbook::ms_errors = true; |
|---|
| 230 | |
|---|
| 231 | if (vm.count("no-pretty-print")) |
|---|
| 232 | pretty_print = false; |
|---|
| 233 | |
|---|
| 234 | if (vm.count("indent")) |
|---|
| 235 | indent = vm["indent"].as<int>(); |
|---|
| 236 | |
|---|
| 237 | if (vm.count("linewidth")) |
|---|
| 238 | linewidth = vm["linewidth"].as<int>(); |
|---|
| 239 | |
|---|
| 240 | if (vm.count("debug")) |
|---|
| 241 | { |
|---|
| 242 | static tm timeinfo; |
|---|
| 243 | timeinfo.tm_year = 2000 - 1900; |
|---|
| 244 | timeinfo.tm_mon = 12 - 1; |
|---|
| 245 | timeinfo.tm_mday = 20; |
|---|
| 246 | timeinfo.tm_hour = 12; |
|---|
| 247 | timeinfo.tm_min = 0; |
|---|
| 248 | timeinfo.tm_sec = 0; |
|---|
| 249 | timeinfo.tm_isdst = -1; |
|---|
| 250 | mktime(&timeinfo); |
|---|
| 251 | quickbook::current_time = &timeinfo; |
|---|
| 252 | quickbook::current_gm_time = &timeinfo; |
|---|
| 253 | quickbook::debug_mode = true; |
|---|
| 254 | } |
|---|
| 255 | else |
|---|
| 256 | { |
|---|
| 257 | time_t t = std::time(0); |
|---|
| 258 | static tm lt = *localtime(&t); |
|---|
| 259 | static tm gmt = *gmtime(&t); |
|---|
| 260 | quickbook::current_time = < |
|---|
| 261 | quickbook::current_gm_time = &gmt; |
|---|
| 262 | quickbook::debug_mode = false; |
|---|
| 263 | } |
|---|
| 264 | |
|---|
| 265 | if (vm.count("input-file")) |
|---|
| 266 | { |
|---|
| 267 | std::string filein = vm["input-file"].as<std::string>(); |
|---|
| 268 | std::string fileout; |
|---|
| 269 | |
|---|
| 270 | if (vm.count("output-file")) |
|---|
| 271 | { |
|---|
| 272 | fileout = vm["output-file"].as<std::string>(); |
|---|
| 273 | } |
|---|
| 274 | else |
|---|
| 275 | { |
|---|
| 276 | fileout = quickbook::detail::remove_extension(filein.c_str()); |
|---|
| 277 | fileout += ".xml"; |
|---|
| 278 | } |
|---|
| 279 | |
|---|
| 280 | std::cout << "Generating Output File: " |
|---|
| 281 | << fileout |
|---|
| 282 | << std::endl; |
|---|
| 283 | |
|---|
| 284 | return quickbook::parse(filein.c_str(), fileout.c_str(), indent, linewidth, pretty_print); |
|---|
| 285 | } |
|---|
| 286 | else |
|---|
| 287 | { |
|---|
| 288 | quickbook::detail::outerr("",0) << "Error: No filename given" << std::endl; |
|---|
| 289 | } |
|---|
| 290 | } |
|---|
| 291 | |
|---|
| 292 | catch(std::exception& e) |
|---|
| 293 | { |
|---|
| 294 | quickbook::detail::outerr("",0) << "Error: " << e.what() << "\n"; |
|---|
| 295 | return 1; |
|---|
| 296 | } |
|---|
| 297 | |
|---|
| 298 | catch(...) |
|---|
| 299 | { |
|---|
| 300 | quickbook::detail::outerr("",0) << "Error: Exception of unknown type caught\n"; |
|---|
| 301 | } |
|---|
| 302 | |
|---|
| 303 | return 0; |
|---|
| 304 | } |
|---|