From 0323fb0ba638519ff406e0ca0969a895d8959394 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Wed, 10 May 2017 20:49:40 +0100 Subject: [PATCH] Add source-highlight to docs. It's useful as reference. --- docs/source-highlight.cc | 594 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 594 insertions(+) create mode 100644 docs/source-highlight.cc diff --git a/docs/source-highlight.cc b/docs/source-highlight.cc new file mode 100644 index 0000000..8f0fe38 --- /dev/null +++ b/docs/source-highlight.cc @@ -0,0 +1,594 @@ +/* + * Copyright (C) 1999-2009 Lorenzo Bettini + * + * 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 +#include +#include +#include + +#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; + if (args_info.line_range_given) { + lineRanges = boost::shared_ptr(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; + if (args_info.regex_range_given) { + regexRanges = boost::shared_ptr(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; + if (args_info.statistics_given) { + stopwatch = boost::shared_ptr(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; + + // 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(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; + 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(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 ", + "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 ", + "Report bugs to ", + 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"; +} + +