/*
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
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";
int main(int argc, char **argv)
{
// Initialize terminal logger. Only use color if writing to a terminal (tty)
bool useColor = isatty(STDOUT_FILENO) && 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;
/*
* 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 domains, 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.");
// "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{
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();
std::cout << "Using " << outputPath << " as output path." << std::endl;
}
// TODO: REMOVE diagnostic code.
std::cout << "input : " << vm["input"].as() << std::endl;
std::cout << "output : " << outputPath << std::endl;
std::cout << "format : " << vm["format"].as() << std::endl;
if (vm.count("include")) {
std::vector includes =
vm["include"].as>();
std::cout << "includes : ";
for (auto &i : includes) {
std::cout << i << ", ";
}
std::cout << std::endl;
}
// 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();
XmlParser xmlParser;
registry.registerParser(
{"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"},
{&RttiTypes::Document, &RttiTypes::Domain, &RttiTypes::Typesystem},
&xmlParser);
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 doc = context.import(inputPath, "text/vnd.ousia.oxd", "",
{&RttiTypes::Document});
if (logger.hasError() || doc == nullptr) {
logger.fatalError("Errors occured while parsing the document");
return ERROR_IN_DOCUMENT;
}
// write output.
html::DemoHTMLTransformer outTransformer;
if (outputPath == "-") {
outTransformer.writeHTML(doc.cast(), std::cout);
} else {
std::fstream out {outputPath};
outTransformer.writeHTML(doc.cast(), out);
}
return SUCCESS;
}