/* Ousía Copyright (C) 2014 Benjamin Paaßen, Andreas Stöckel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * @file Main.cpp * * This file provides the command line interface for Ousia. More information * on command line options can be found in the test. * * @author Benjamin Paaßen (bpaassen@techfak.uni-bielefeld.de) */ #include // Non-portable, needed for isatty #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const size_t ERROR_IN_COMMAND_LINE = 1; const size_t SUCCESS = 0; const size_t ERROR_UNHANDLED_EXCEPTION = 2; const size_t ERROR_IN_DOCUMENT = 3; using namespace ousia; namespace po = boost::program_options; namespace fs = boost::filesystem; const char *MSG_COPYING = "Ousía\n" "Semantic Document Markup\n" "Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel\n" "\n" "This program is free software: you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation, either version 3 of the License, or\n" "(at your option) any later version.\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n"; const std::set formats{"html", "xml"}; static void createOutput(Handle doc, std::ostream &out, const std::string &format, Logger &logger, ResourceManager &resMgr) { if (format == "html") { html::DemoHTMLTransformer transform; transform.writeHTML(doc, out, logger, true); } else if (format == "xml") { xml::XmlTransformer transform; transform.writeXml(doc, out, logger, resMgr, true); } } int main(int argc, char **argv) { // Initialize terminal logger. Only use color if writing to a terminal (tty) bool useColor = isatty(STDERR_FILENO); TerminalLogger logger{std::cerr, useColor}; // Program options po::options_description desc( "Program usage\n./ousia [optional options] <-F format> \nProgram options"); std::string inputPath; std::string outputPath; std::string format; #ifdef MANAGER_GRAPHVIZ_EXPORT std::string graphvizPath; #endif /* * This is a rather strange access mechanism: add_options() returns an * easy_init object that has overloaded the () operator to accept new * initializations. Again: Rather strange syntax, but it works. */ desc.add_options()("help", "Program help")( "input,i", po::value(&inputPath)->required(), "The input document file name")( "include,I", po::value>(), "Include paths, where resources like the input document " "or additional ontologies, typesystems, etc. might be " "found.")( "output,o", po::value(&outputPath), "The output file name. Per default the input file name will be used.")( "format,F", po::value(&format)->required(), "The output format that shall be produced." #ifdef MANAGER_GRAPHVIZ_EXPORT )( "graphviz,G", po::value(&graphvizPath), "If set, dumps the internal object graph to the given graphviz dot file" #endif ); // "input" should be a positional option, such that we can write: // ./ousia [some options] // without having to use -i or I po::positional_options_description positional; positional.add("input", 1); po::variables_map vm; try { // try to read the values for each option to the variable map. po::store(po::command_line_parser(argc, argv) .options(desc) .positional(positional) .run(), vm); // first check the help option. if (vm.count("help")) { // write the program options description as generated by boost std::cout << MSG_COPYING << std::endl << std::endl << desc << std::endl << std::endl; return SUCCESS; } // only if the user did not want help to we use notify, which checks // if all required options were given. po::notify(vm); } catch (po::error &e) { logger.error(e.what()); std::cerr << desc << std::endl; return ERROR_IN_COMMAND_LINE; } // To comply with standard UNIX conventions the following should be changed: // TODO: Allow "-" for input and output files for reading from stdin and // writing to stdout if (inputPath == "-") { logger.error("Currently no reading from std::in is supported!"); return ERROR_IN_COMMAND_LINE; } else { if (!fs::exists(inputPath)) { logger.error("Input file \"" + inputPath + "\" does not exist"); return ERROR_IN_COMMAND_LINE; } if (!fs::is_regular_file(inputPath)) { logger.error("Input file \"" + inputPath + "\" is not a regular file"); return ERROR_IN_COMMAND_LINE; } inputPath = fs::canonical(inputPath).string(); } // prepare output path if (!vm.count("output")) { // get the input filename. fs::path in{inputPath}; // construct a working directory output path. fs::path outP = fs::canonical("."); outP /= (in.stem().string() + "." + format); outputPath = outP.string(); logger.note(std::string("Using ") + outputPath + std::string(" as output path.")); } // check format. if (!formats.count(format)) { logger.error("Format must be one of: "); for (auto &f : formats) { logger.error(f); } } // initialize global instances. Manager manager; Registry registry; ResourceManager resourceManager; ParserScope scope; Rooted project{new Project(manager)}; FileLocator fileLocator; ParserContext context{registry, resourceManager, scope, project, logger}; // Connect the Source Context Callback of the logger to provide the user // with context information (line, column, filename, text) for log messages logger.setSourceContextCallback(resourceManager.getSourceContextCallback()); // fill registry registry.registerDefaultExtensions(); OsmlParser osmlParser; OsxmlParser osxmlParser; registry.registerParser( {"text/vnd.ousia.osml"}, {&RttiTypes::Document, &RttiTypes::Ontology, &RttiTypes::Typesystem}, &osmlParser); registry.registerParser( {"text/vnd.ousia.osml+xml"}, {&RttiTypes::Document, &RttiTypes::Ontology, &RttiTypes::Typesystem}, &osxmlParser); registry.registerResourceLocator(&fileLocator); // register search paths fileLocator.addDefaultSearchPaths(); // in user includes we allow every kind of resource. if (vm.count("include")) { std::vector includes = vm["include"].as>(); for (auto &i : includes) { // Adding the search path as "UNKNOWN" suffices, as this search path // is automatically searched for all files. fileLocator.addSearchPath(i, ResourceType::UNKNOWN); } } // now all preparation is done and we can parse the input document. Rooted docNode = context.import(inputPath, "", "", {&RttiTypes::Document}); #ifdef MANAGER_GRAPHVIZ_EXPORT if (!graphvizPath.empty()) { try { manager.exportGraphviz(graphvizPath.c_str()); } catch (LoggableException ex){ logger.log(ex); } } #endif if (logger.hasError() || docNode == nullptr) { logger.fatalError("Errors occured while parsing the document"); return ERROR_IN_DOCUMENT; } Rooted doc = docNode.cast(); // write output if (outputPath != "-") { std::ofstream out{outputPath}; createOutput(doc, out, format, logger, resourceManager); } else { createOutput(doc, std::cout, format, logger, resourceManager); } return SUCCESS; }