diff --git a/src/tex2lyx/ChangeLog b/src/tex2lyx/ChangeLog index d054b82d63..4c3903e79e 100644 --- a/src/tex2lyx/ChangeLog +++ b/src/tex2lyx/ChangeLog @@ -1,3 +1,16 @@ +2003-11-18 Georg Baum + + * tex2lyx.C: + * tex2lyx.h: + * text.C: Read a list of commands and their arguments from a reLyX + compatible syntax file in order to parse optional argumnts correctly. + * preamble.C: + * table.C: + * text.C: + * tex2lyx.C: + * texparser.C + * math.C: change size() to !empty() where it was used as bool + 2003-11-03 Georg Baum * math.C: diff --git a/src/tex2lyx/math.C b/src/tex2lyx/math.C index a170da7db9..ea3cc9d4e0 100644 --- a/src/tex2lyx/math.C +++ b/src/tex2lyx/math.C @@ -121,7 +121,7 @@ void parse_math(Parser & p, ostream & os, unsigned flags, const mode_type mode) } else if (t.cat() == catComment) { - if (t.cs().size()) + if (!t.cs().empty()) cerr << "Ignoring comment: " << t.asInput(); else // "%\n" combination diff --git a/src/tex2lyx/preamble.C b/src/tex2lyx/preamble.C index 708fdd90e1..78f9f39b5b 100644 --- a/src/tex2lyx/preamble.C +++ b/src/tex2lyx/preamble.C @@ -131,7 +131,7 @@ void handle_package(string const & name, string const & options) h_language = name; h_quotes_language = name; } else { - if (options.size()) + if (!options.empty()) h_preamble << "\\usepackage[" << options << "]{" << name << "}\n"; else h_preamble << "\\usepackage{" << name << "}\n"; @@ -146,7 +146,7 @@ void end_preamble(ostream & os, LyXTextClass const & /*textclass*/) << "\\lyxformat 225\n" << "\\textclass " << h_textclass << "\n" << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n"; - if (h_options.size()) + if (!h_options.empty()) os << "\\options " << h_options << "\n"; os << "\\language " << h_language << "\n" << "\\inputencoding " << h_inputencoding << "\n" @@ -319,7 +319,7 @@ LyXTextClass const parse_preamble(Parser & p, ostream & os, string const & force trim(name); int nargs = 0; string opts = p.getOpt(); - if (opts.size()) { + if (!opts.empty()) { istringstream is(string(opts, 1)); //cerr << "opt: " << is.str() << "\n"; is >> nargs; @@ -361,13 +361,13 @@ LyXTextClass const parse_preamble(Parser & p, ostream & os, string const & force h_preamble << "\\begin{" << name << "}"; } - else if (t.cs().size()) + else if (!t.cs().empty()) h_preamble << '\\' << t.cs(); } p.skip_spaces(); // Force textclass if the user wanted it - if (forceclass.size()) { + if (!forceclass.empty()) { h_textclass = forceclass; } string layoutfilename = LibFileSearch("layouts", h_textclass, "layout"); @@ -377,7 +377,7 @@ LyXTextClass const parse_preamble(Parser & p, ostream & os, string const & force } LyXTextClass textclass; textclass.Read(layoutfilename); - if (! h_papersides.size()) { + if (h_papersides.empty()) { ostringstream ss; ss << textclass.sides(); h_papersides = ss.str(); diff --git a/src/tex2lyx/table.C b/src/tex2lyx/table.C index 2f6e18b2c4..18579870d5 100644 --- a/src/tex2lyx/table.C +++ b/src/tex2lyx/table.C @@ -299,7 +299,7 @@ void handle_tabular(Parser & p, ostream & os, Context & context) { string posopts = p.getOpt(); - if (posopts.size()) + if (!posopts.empty()) cerr << "vertical tabular positioning '" << posopts << "' ignored\n"; vector colinfo; @@ -484,9 +484,9 @@ void handle_tabular(Parser & p, ostream & os, os << " leftline=\"true\""; if (colinfo[col].rightline) os << " rightline=\"true\""; - if (colinfo[col].width.size()) + if (!colinfo[col].width.empty()) os << " width=\"" << colinfo[col].width << "\""; - if (colinfo[col].special.size()) + if (!colinfo[col].special.empty()) os << " special=\"" << colinfo[col].special << "\""; os << ">\n"; } diff --git a/src/tex2lyx/tex2lyx.C b/src/tex2lyx/tex2lyx.C index 5dad8e63c8..5e20b9a671 100644 --- a/src/tex2lyx/tex2lyx.C +++ b/src/tex2lyx/tex2lyx.C @@ -43,6 +43,7 @@ using std::ostringstream; using std::stringstream; using std::string; using std::vector; +using std::map; using lyx::support::system_lyxdir; using lyx::support::user_lyxdir; @@ -114,7 +115,63 @@ string active_environment() } +map > known_commands; + + +namespace { + + +/*! + * Read a list of TeX commands from a reLyX compatible syntax file. + * Since this list is used after all commands that have a LyX counterpart + * are handled, it does not matter that the "syntax.default" file from reLyX + * has almost all of them listed. For the same reason the reLyX-specific + * reLyXre environment is ignored. + */ +void read_syntaxfile(string const & file_name) +{ + if (!IsFileReadable(file_name)) { + cerr << "Could not open syntax file \"" << file_name + << "\" for reading." << endl; + exit(2); + } + ifstream is(file_name.c_str()); + // We can use our TeX parser, since the syntax of the layout file is + // modeled after TeX. + // Unknown tokens are just silently ignored, this helps us to skip some + // reLyX specific things. + Parser p(is); + while (p.good()) { + Token const & t = p.get_token(); + if (t.cat() == catEscape) { + string command = t.asInput(); + if (p.next_token().asInput() == "*") { + p.get_token(); + command += '*'; + } + p.skip_spaces(); + vector arguments; + while (p.next_token().cat() == catBegin || + p.next_token().asInput() == "[") { + if (p.next_token().cat() == catBegin) { + string const arg = p.getArg('{', '}'); + if (arg == "translate") + arguments.push_back(required); + else + arguments.push_back(verbatim); + } else { + p.getArg('[', ']'); + arguments.push_back(optional); + } + } + known_commands[command] = arguments; + } + } +} + + string documentclass; +string syntaxfile; bool overwrite_files = false; @@ -130,7 +187,8 @@ int parse_help(string const &, string const &) "\t-f Force creation of .lyx files even if they exist already\n" "\t-userdir dir try to set user directory to dir\n" "\t-sysdir dir try to set system directory to dir\n" - "\t-c textclass declare the textclass" << endl; + "\t-c textclass declare the textclass\n" + "\t-s syntaxfile read additional syntax file" << endl; exit(0); } @@ -146,6 +204,17 @@ int parse_class(string const & arg, string const &) } +int parse_syntaxfile(string const & arg, string const &) +{ + if (arg.empty()) { + cerr << "Missing syntaxfile string after -s switch" << endl; + exit(1); + } + syntaxfile = arg; + return 1; +} + + int parse_sysdir(string const & arg, string const &) { if (arg.empty()) { @@ -177,10 +246,11 @@ int parse_force(string const &, string const &) void easyParse(int & argc, char * argv[]) { - std::map cmdmap; + map cmdmap; cmdmap["-c"] = parse_class; cmdmap["-f"] = parse_force; + cmdmap["-s"] = parse_syntaxfile; cmdmap["-help"] = parse_help; cmdmap["--help"] = parse_help; cmdmap["-sysdir"] = parse_sysdir; @@ -208,6 +278,8 @@ void easyParse(int & argc, char * argv[]) } } +} // anonymous namespace + void tex2lyx(std::istream &is, std::ostream &os) { @@ -267,6 +339,15 @@ int main(int argc, char * argv[]) lyx::support::os::init(&argc, &argv); lyx::support::setLyxPaths(); + string const system_syntaxfile = lyx::support::LibFileSearch("reLyX", "syntax.default"); + if (system_syntaxfile.empty()) { + cerr << "Error: Could not find syntax file \"syntax.default\"." << endl; + exit(1); + } + read_syntaxfile(system_syntaxfile); + if (!syntaxfile.empty()) + read_syntaxfile(syntaxfile); + if (!IsFileReadable(argv[1])) { cerr << "Could not open input file \"" << argv[1] << "\" for reading." << endl; diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index df6e0371d1..97b6f66dd7 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -19,6 +19,7 @@ #include #include #include +#include class Context; @@ -60,6 +61,15 @@ char const ** is_known(std::string const & str, char const ** what); extern std::vector active_environments; std::string active_environment(); +enum ArgumentType { + required, + verbatim, + optional +}; + +/// Known TeX commands with arguments that get parsed into ERT. +extern std::map > known_commands; + /*! Reads tex input from \a is and writes lyx output to \a os. * Uses some common settings for the preamble, so this should only * be used more than once for included documents. diff --git a/src/tex2lyx/texparser.C b/src/tex2lyx/texparser.C index bd51e3b122..46bb0d957e 100644 --- a/src/tex2lyx/texparser.C +++ b/src/tex2lyx/texparser.C @@ -250,7 +250,7 @@ string Parser::getArg(char left, char right) while ((c = getChar()) != right && good()) { // Ignore comments if (curr_token().cat() == catComment) { - if (curr_token().cs().size()) + if (!curr_token().cs().empty()) cerr << "Ignoring comment: " << curr_token().asInput(); } else if (curr_token().cat() == catSpace || curr_token().cat() == catNewline) diff --git a/src/tex2lyx/text.C b/src/tex2lyx/text.C index d23ebc72fb..ee65ecaa3f 100644 --- a/src/tex2lyx/text.C +++ b/src/tex2lyx/text.C @@ -294,6 +294,43 @@ void check_space(Parser const & p, ostream & os, Context & context) os << ' '; } + +/*! + * Check wether \param command is a known command. If yes, + * handle the command with all arguments. + * \return true if the command was parsed, false otherwise. + */ +bool parse_command(string const & command, Parser & p, ostream & os, + bool outer, Context & context) +{ + if (known_commands.find(command) != known_commands.end()) { + vector const & template_arguments = known_commands[command]; + string ert = command; + size_t no_arguments = template_arguments.size(); + for (size_t i = 0; i < no_arguments; ++i) { + switch (template_arguments[i]) { + case required: + // This argument contains regular LaTeX + handle_ert(os, ert + '{', context); + parse_text(p, os, FLAG_ITEM, outer, context); + ert = "}"; + break; + case verbatim: + // This argument may contain special characters + ert += '{' + p.verbatim_item() + '}'; + break; + case optional: + ert += p.getOpt(); + break; + } + } + handle_ert(os, ert, context); + return true; + } + return false; +} + + void parse_environment(Parser & p, ostream & os, bool outer, Context & parent_context) { @@ -377,11 +414,11 @@ void parse_environment(Parser & p, ostream & os, bool outer, // lyx can't handle length variables ostringstream ss; ss << "\\begin{minipage}"; - if (latex_position.size()) + if (!latex_position.empty()) ss << '[' << latex_position << ']'; - if (latex_height.size()) + if (!latex_height.empty()) ss << '[' << latex_height << ']'; - if (latex_inner_pos.size()) + if (!latex_inner_pos.empty()) ss << '[' << latex_inner_pos << ']'; ss << "{" << width << "}"; handle_ert(os, ss.str(), parent_context); @@ -628,7 +665,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, else if (t.cat() == catComment) { context.check_layout(os); - if (t.cs().size()) { + if (!t.cs().empty()) { handle_comment(os, '%' + t.cs(), context); if (p.next_token().cat() == catNewline) { // A newline after a comment line starts a new paragraph @@ -696,7 +733,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, handle_ert(os, "[", context); os << s; handle_ert(os, "]", context); - } else if (s.size()) { + } else if (!s.empty()) { // The space is needed to separate the item from the rest of the sentence. os << s << ' '; p.skip_spaces(); @@ -781,7 +818,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, if (opt.find('t') != string::npos) ss << "Top"; if (opt.find('b') != string::npos) ss << "Bottom"; if (opt.find('B') != string::npos) ss << "Baseline"; - if (ss.str().size()) + if (!ss.str().empty()) os << "\trotateOrigin " << ss.str() << '\n'; else cerr << "Warning: Ignoring unknown includegraphics origin argument '" << opt << "'\n"; @@ -838,7 +875,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, if (opts.find("command") != opts.end()) special << "command=" << opts["command"] << ','; string s_special = special.str(); - if (s_special.size()) { + if (!s_special.empty()) { // We had special arguments. Remove the trailing ','. os << "\tspecial " << s_special.substr(0, s_special.size() - 1) << '\n'; } @@ -1187,7 +1224,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, begin_inset(os, "LatexCommand "); os << "\\bibtex"; // Do we have a bibliographystyle set? - if (bibliographystyle.size()) { + if (!bibliographystyle.empty()) { os << '[' << bibliographystyle << ']'; } os << '{' << p.verbatim_item() << "}\n"; @@ -1223,16 +1260,6 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // next paragraph. } - else if (t.cs() == "psfrag") { - // psfrag{ps-text}[ps-pos][tex-pos]{tex-text} - // TODO: Generalize this! - string arguments = p.getArg('{', '}'); - arguments += '}'; - arguments += p.getOpt(); - arguments += p.getOpt(); - handle_ert(os, "\\psfrag{" + arguments, context); - } - else { //cerr << "#: " << t << " mode: " << mode << endl; // heuristic: read up to next non-nested space @@ -1247,14 +1274,14 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, cerr << "found ERT: " << s << endl; handle_ert(os, s + ' ', context); */ - context.check_layout(os); string name = t.asInput(); if (p.next_token().asInput() == "*") { // Starred commands like \vspace*{} p.get_token(); // Eat '*' name += '*'; } - handle_ert(os, name, context); + if (! parse_command(t.asInput(), p, os, outer, context)) + handle_ert(os, name, context); } if (flags & FLAG_LEAVE) {