../../src/source-highlight.cc

/*
 * Copyright (C) 1999-2009 Lorenzo Bettini <http://www.lorenzobettini.it>
 *
 * 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
// this is glib related so don't use it when compiling with qmake
#include "progname.h"
#else
#define set_program_name(x) ;
#endif
#include <cstdlib>
#include <iostream>
#include <exception>
#include <boost/shared_ptr.hpp>
#include "srchilite/sourcehighlight.h"
#include "srchilite/fileutil.h"
#include "srchilite/verbosity.h"
#include "srchilite/langmap.h"
#include "srchilite/languageinfer.h"
#include "srchilite/utils.h"
#include "srchilite/highlightbuilderexception.h"
#include "srchilite/parserexception.h"
#include "srchilite/debuglistener.h"
#include "srchilite/ctagsmanager.h"
#include "srchilite/stopwatch.h"
#include "srchilite/lineranges.h"
#include "srchilite/regexranges.h"
#include "srchilite/versions.h"
#include "srchilite/settings.h"
#include "cmdline.h"
using namespace std;
using namespace srchilite;
/**
 * how language inference should be made: NOINFERENCE = no inference at all,
 * INFERFIRST = inference has priority (e.g., if --infer-lang is specified),
 * INFERATLAST = try inference as the last chance
 */
enum InferPolicy {
 NOINFERENCE = 0, INFERFIRST, INFERATLAST
};
static void print_copyright();
static void print_reportbugs();
static void print_version();
static string inferLang(const string &inputFileName);
static string getMappedLang(const string &s, LangMap &langmap);
static void printError(const string &s);
static void print_cgi_header();
/**
 * Exits for a generic error (i.e., file not found)
 */
static void exitError(const string &s);
static string getLangFileName(InferPolicy infer, const string &inputFileName,
 const string &langFileName, const string &langName, LangMap &langMap);
static bool verbose = false;
static bool failsafe = false;
#ifdef BUILD_AS_CGI
#include "envmapper.h"
#endif // BUILD_AS_CGI
/**
 * Print progress status information (provided --quiet is not specified)
 * @param message
 */
#define PROGRESSINFO(message) if (!args_info.quiet_given) cerr << message;
int main(int argc, char * argv[]) {
 set_program_name(argv[0]);
 gengetopt_args_info args_info; // command line structure
 bool is_cgi = false;
#ifdef BUILD_AS_CGI
 // map environment to parameters if used as CGI
 char **temp_argv;
 temp_argv = map_environment(&argc, argv);
 is_cgi = temp_argv != argv;
 argv = temp_argv;
#endif // BUILD_AS_CGI
 if (cmdline_parser(argc, argv, &args_info) != 0) {
 // calls cmdline parser. The user gived bag args if it doesn't return -1
 return EXIT_FAILURE;
 }
 if (args_info.version_given) {
 print_version();
 print_copyright();
 cmdline_parser_free(&args_info);
 return EXIT_SUCCESS;
 }
 if (args_info.help_given || args_info.detailed_help_given) {
 cout << "GNU ";
 if (args_info.detailed_help_given) {
 cmdline_parser_print_detailed_help();
 } else {
 cmdline_parser_print_help();
 }
 print_reportbugs();
 cmdline_parser_free(&args_info);
 return EXIT_SUCCESS;
 }
 // set possible line ranges
 boost::shared_ptr<LineRanges> lineRanges;
 if (args_info.line_range_given) {
 lineRanges = boost::shared_ptr<LineRanges>(new LineRanges);
 for (unsigned int i = 0; i < args_info.line_range_given; ++i) {
 if (lineRanges->addRange(args_info.line_range_arg[i]) != NO_ERROR) {
 string invalid_range = args_info.line_range_arg[i];
 cmdline_parser_free(&args_info);
 exitError("invalid line range: " + invalid_range);
 }
 }
 }
 // set possible regex ranges
 boost::shared_ptr<RegexRanges> regexRanges;
 if (args_info.regex_range_given) {
 regexRanges = boost::shared_ptr<RegexRanges>(new RegexRanges);
 for (unsigned int i = 0; i < args_info.regex_range_given; ++i) {
 if (!regexRanges->addRegexRange(args_info.regex_range_arg[i])) {
 string invalid_range = args_info.regex_range_arg[i];
 cmdline_parser_free(&args_info);
 exitError("invalid regex range: " + invalid_range);
 }
 }
 }
 if (args_info.range_context_given) {
 lineRanges->setContextLines(args_info.range_context_arg);
 }
 // if a stopwatch is created, when it is deleted (automatically
 // since we're using a shared pointer, it will print the
 // elapsed seconds.
 boost::shared_ptr<StopWatch> stopwatch;
 if (args_info.statistics_given) {
 stopwatch = boost::shared_ptr<StopWatch>(new StopWatch);
 }
 verbose = args_info.verbose_given;
 failsafe = args_info.failsafe_given;
 string inputFile;
 string outputFile;
 string srcLang;
 string outputFormat;
 string langFile;
 string outLangFile;
 string dataDir;
 string outputDir;
 Verbosity::setVerbosity(verbose);
 if (args_info.input_given)
 inputFile = args_info.input_arg;
 if (args_info.output_given)
 outputFile = args_info.output_arg;
 // for cgi force output to standard output
 if (is_cgi)
 outputFile = "STDOUT";
 if (args_info.src_lang_given)
 srcLang = args_info.src_lang_arg;
 if (args_info.lang_def_given)
 langFile = args_info.lang_def_arg;
 if (args_info.outlang_def_given)
 outLangFile = args_info.outlang_def_arg;
 // the default for output format is html
 outputFormat = args_info.out_format_arg;
 if (args_info.data_dir_given) {
 dataDir = args_info.data_dir_arg;
 }
 if (args_info.output_dir_given) {
 outputDir = args_info.output_dir_arg;
 }
 /*
 the starting default path to search for files is computed at
 run-time: it is
 the path of the binary + ".." + RELATIVEDATADIR
 this should make the package relocable (i.e., not stuck
 with a fixed installation directory).
 Of course, the GNU standards for installation directories
 should be followed, but this is not a problem if you use
 configure and make install features.
 If no path is specified in the running program we go back to
 the absolute datadir.
 */
 // this is defined in fileutil.cc
 string prefix_dir = get_file_path(argv[0]);
 if (prefix_dir.size())
 start_path = get_file_path(argv[0]) + RELATIVEDATADIR;
 else
 start_path = Settings::retrieveDataDir();
 // if datadir is not specified, we rely on start_path?
 try {
 // initialize map files
 LangMap langmap(dataDir, args_info.lang_map_arg);
 // just print the input language list and associations
 if (args_info.lang_list_given) {
 langmap.open();
 langmap.print();
 return (EXIT_SUCCESS);
 }
 LangMap outlangmap(dataDir, args_info.outlang_map_arg);
 // just print the input language list and associations
 if (args_info.outlang_list_given) {
 outlangmap.open();
 outlangmap.print();
 return (EXIT_SUCCESS);
 }
 string cssUrl;
 if (args_info.css_given)
 cssUrl = args_info.css_arg;
 string docTitle;
 if (args_info.title_given)
 docTitle = args_info.title_arg;
 if (args_info.check_lang_given || args_info.check_outlang_given
 || args_info.show_regex_given
 || args_info.show_lang_elements_given
 || args_info.lang_list_given || args_info.outlang_list_given) {
 // FIXME just a bogus outlang file specification not to search in outlang.map
 outLangFile = "html.outlang";
 }
 // initialize the main source highlight object
 SourceHighlight sourcehighlight(getLangFileName(NOINFERENCE,
 outputFile, outLangFile, outputFormat
 + (cssUrl.size() ? +"-css" : ""), outlangmap));
 // and sets all its properties according to the command line args
 sourcehighlight.setDataDir(dataDir);
 boost::shared_ptr<DebugListener> debugListener;
 // if a simple check is required...
 if (args_info.check_lang_given) {
 cout << "checking " << args_info.check_lang_arg << "... ";
 sourcehighlight.checkLangDef(args_info.check_lang_arg);
 // if we're here, everything went fine!
 cout << "OK" << endl;
 // otherwise an exception has already been thrown
 return (EXIT_SUCCESS);
 }
 if (args_info.check_outlang_given) {
 cout << "checking " << args_info.check_outlang_arg << "... ";
 sourcehighlight.checkOutLangDef(args_info.check_outlang_arg);
 // if we're here, everything went fine!
 cout << "OK" << endl;
 // otherwise an exception has already been thrown
 return (EXIT_SUCCESS);
 }
 // just print the highlightstate if show-regex is specified
 if (args_info.show_regex_given) {
 sourcehighlight.printHighlightState(args_info.show_regex_arg, cout);
 return (EXIT_SUCCESS);
 }
 // just print the lang elems if show-lang-elements is specified
 if (args_info.show_lang_elements_given) {
 sourcehighlight.printLangElems(args_info.show_lang_elements_arg,
 cout);
 return (EXIT_SUCCESS);
 }
 sourcehighlight.setGenerateEntireDoc((!args_info.no_doc_given)
 && (args_info.doc_given || (docTitle.size()) || cssUrl.size()));
 sourcehighlight.setStyleDefaultFile(args_info.style_defaults_arg);
 sourcehighlight.setStyleFile(args_info.style_file_arg);
 if (args_info.style_css_file_given)
 sourcehighlight.setStyleCssFile(args_info.style_css_file_arg);
 sourcehighlight.setGenerateVersion(args_info.gen_version_flag != 0);
 sourcehighlight.setCss(cssUrl);
 sourcehighlight.setTitle(docTitle);
 sourcehighlight.setOutputDir(outputDir);
 sourcehighlight.setLineRanges(lineRanges.get());
 sourcehighlight.setRegexRanges(regexRanges.get());
 sourcehighlight.setBinaryOutput(args_info.binary_output_given);
 if (args_info.debug_langdef_given) {
 debugListener = boost::shared_ptr<DebugListener>(new DebugListener);
 const string debugType = args_info.debug_langdef_arg;
 if (debugType == "interactive")
 debugListener->setInteractive(true);
 sourcehighlight.setHighlightEventListener(debugListener.get());
 // during debugging disable optimizations
 sourcehighlight.setOptimize(false);
 }
 int tabs = 0;
 if (args_info.tab_given) {
 tabs = args_info.tab_arg;
 }
 if (args_info.header_given)
 sourcehighlight.setHeaderFileName(args_info.header_arg);
 if (args_info.footer_given)
 sourcehighlight.setFooterFileName(args_info.footer_arg);
 bool generateLineNumbers = args_info.line_number_given
 || args_info.line_number_ref_given;
 sourcehighlight.setGenerateLineNumbers(generateLineNumbers);
 if (args_info.line_number_given) {
 // set the line number padding char (default is always '0')
 sourcehighlight.setLineNumberPad(args_info.line_number_arg[0]);
 }
 if (args_info.line_number_ref_given) {
 sourcehighlight.setGenerateLineNumberRefs(true);
 sourcehighlight.setLineNumberAnchorPrefix(
 args_info.line_number_ref_arg);
 }
 if (generateLineNumbers) {
 // when generating line numbers tabs must be translated
 // otherwise the output line will not be aligned
 // due to the presence of line numbers
 sourcehighlight.setTabSpaces(tabs ? tabs : 8);
 }
 if (tabs) {
 sourcehighlight.setTabSpaces(tabs);
 }
 // whether to give precedence to language inference
 InferPolicy inferPolicy = (args_info.infer_lang_given ? INFERFIRST
 : INFERATLAST);
 RefPosition refposition = INLINE;
 string gen_references_arg = args_info.gen_references_arg;
 if (gen_references_arg == "inline")
 refposition = INLINE;
 else if (gen_references_arg == "postline")
 refposition = POSTLINE;
 else if (gen_references_arg == "postdoc")
 refposition = POSTDOC;
 boost::shared_ptr<CTagsManager> ctagsManager;
 if (args_info.gen_references_given) {
 string ctags = args_info.ctags_arg;
 string ctags_file = args_info.ctags_file_arg;
 // build the additional arguments for the ctags command
 if (args_info.gen_references_given && ctags != "") {
 if (inputFile.size()) {
 ctags += " ";
 ctags += inputFile;
 } else if (args_info.inputs_num) {
 for (unsigned int i = 0; i < (args_info.inputs_num); ++i) {
 ctags += " ";
 ctags += args_info.inputs[i];
 }
 }
 }
 // the ctags command must be executed if --ctags is specified with an empty string
 ctagsManager = boost::shared_ptr<CTagsManager>(new CTagsManager(
 ctags_file, ctags, ctags != "", refposition));
 sourcehighlight.setCTagsManager(ctagsManager.get());
 }
 if (args_info.range_separator_given) {
 sourcehighlight.setRangeSeparator(args_info.range_separator_arg);
 }
 // OK, let's highlight!
 if (args_info.inputs_num && (args_info.input_given
 || args_info.output_given)) {
 // do not mix command line invocation modes
 cerr << "Please, use one of the two syntaxes for invocation: "
 << endl;
 cerr
 << "source-highlight [OPTIONS]... -i input_file -o output_file"
 << endl;
 cerr << "source-highlight [OPTIONS]... [FILES]..." << endl;
 exit(EXIT_FAILURE);
 }
 // for cgi we can only process one file specified with --input
 if (args_info.inputs_num && !is_cgi) {
 // the output file name is empty, but we don't want to generate to stdout
 sourcehighlight.setCanUseStdOut(false);
 // in case multiple input files were specified (without --input)
 for (unsigned int i = 0; i < (args_info.inputs_num); ++i) {
 PROGRESSINFO(string("Processing ")+ args_info.inputs[i] + " ... ");
 sourcehighlight.highlight(args_info.inputs[i], "",
 getLangFileName(inferPolicy, args_info.inputs[i],
 langFile, srcLang, langmap));
 PROGRESSINFO("created " + sourcehighlight.createOutputFileName(args_info.inputs[i]) + "\n");
 }
 } else {
 if (is_cgi)
 print_cgi_header();
 // in case only one input file was specified with --input
 sourcehighlight.highlight(inputFile, outputFile, getLangFileName(
 inferPolicy, inputFile, langFile, srcLang, langmap));
 }
 } catch (const HighlightBuilderException &e) {
 cerr << e << endl;
 exit(EXIT_FAILURE);
 } catch (const ParserException &e) {
 cerr << e << endl;
 exit(EXIT_FAILURE);
 } catch (const exception &e) {
 exitError(e.what());
 }
 cmdline_parser_free(&args_info);
 return 0;
}
void print_copyright() {
 int i;
 int copyright_text_length = 5;
 const char *copyright_text[] = {
 "copyright.text",
 "Copyright (C) 1999-2008 Lorenzo Bettini <http://www.lorenzobettini.it>",
 "This program comes with ABSOLUTELY NO WARRANTY.",
 "This is free software; you may redistribute copies of the program",
 "under the terms of the GNU General Public License.",
 "For more information about these matters, see the file named COPYING.",
 0 };
 for (i = 1; i <= copyright_text_length; ++i)
 cout << copyright_text[i] << endl;;
}
void print_reportbugs() {
 int i;
 int reportbugs_text_length = 3;
 const char *reportbugs_text[] = {
 "reportbugs.text",
 "",
 "Maintained by Lorenzo Bettini <http://www.lorenzobettini.it>",
 "Report bugs to <bug-source-highlight at gnu.org>",
 0 };
 for (i = 1; i <= reportbugs_text_length; ++i)
 cout << reportbugs_text[i] << endl;
}
void print_version() {
 cout << Versions::getCompleteVersion() << endl;
}
string inferLang(const string &inputFileName) {
 VERBOSELN("inferring input language...");
 if (!inputFileName.size()) {
 printError("missing feature: language inference requires input file");
 return "";
 }
 LanguageInfer languageInfer;
 const string &result = languageInfer.infer(inputFileName);
 if (result.size()) {
 VERBOSELN("inferred input language: " + result);
 } else {
 VERBOSELN("couldn't infer input language");
 }
 return result;
}
string getMappedLang(const string &s, LangMap &langmap) {
 // OK now map it into a .lang file
 string mapped_lang = langmap.getMappedFileName(s);
 if (!mapped_lang.size()) {
 // try the lower version
 mapped_lang = langmap.getFileName(Utils::tolower(s));
 }
 return mapped_lang;
}
string getLangFileName(InferPolicy infer, const string &inputFileName,
 const string &langFileName, const string &langName, LangMap &langMap) {
 string langFile;
 // language inference has the precedence
 if (infer == INFERFIRST) {
 langFile = inferLang(inputFileName);
 langFile = getMappedLang(langFile, langMap);
 if (langFile.size())
 return langFile;
 }
 // then if a lang file name was specified we're done
 if (langFileName.size())
 return langFileName;
 // now try with the langName
 langFile = getMappedLang(langName, langMap);
 if (langFile.size())
 return langFile;
 // otherwise try with the inputFileName (its file extension
 // and its name)
 langFile = langMap.getMappedFileNameFromFileName(inputFileName);
 if (langFile.size())
 return langFile;
 // OK, as a last chance let's try with language infer
 if (infer == INFERATLAST) {
 langFile = getMappedLang(inferLang(inputFileName), langMap);
 if (langFile.size())
 return langFile;
 }
 // if we're here we failed all checks
 // if failsafe is specified we default to default.lang
 if (failsafe)
 langFile = "default.lang";
 if (!langFile.size()) {
 // if we're here we must exit with failure
 if (langName.size())
 exitError("could not find a language definition for " + langName);
 else
 exitError("could not find a language definition for input file "
 + inputFileName);
 }
 return langFile;
}
void printError(const string &s) {
 cerr << PACKAGE << ": " << s << endl;
}
void exitError(const string &s) {
 printError(s);
 exit(EXIT_FAILURE);
}
void print_cgi_header() {
 cout << "Content-type: text/html\n";
 cout << "\n";
}

AltStyle によって変換されたページ (->オリジナル) /