diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp index 58e7397ab7..da09ec1faa 100644 --- a/src/tex2lyx/Preamble.cpp +++ b/src/tex2lyx/Preamble.cpp @@ -173,6 +173,11 @@ const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists", 0}; +/*! + * Known file extensions for TeX files as used by \\includeonly + */ +char const * const known_tex_extensions[] = {"tex", 0}; + /// packages that work only in xetex /// polyglossia is handled separately const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian", @@ -1252,6 +1257,12 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled os << *it << '\n'; os << "\\end_modules\n"; } + if (!h_includeonlys.empty()) { + os << "\\begin_includeonly\n"; + for (auto const & iofile : h_includeonlys) + os << iofile << '\n'; + os << "\\end_includeonly\n"; + } os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n" << "\\language " << h_language << "\n" << "\\language_package " << h_language_package << "\n" @@ -2040,6 +2051,33 @@ void Preamble::parse(Parser & p, string const & forceclass, } } + if (t.cs() == "includeonly") { + vector includeonlys = getVectorFromString(p.getArg('{', '}')); + for (auto & iofile : includeonlys) { + string filename(normalize_filename(iofile)); + string const path = getMasterFilePath(true); + // We want to preserve relative/absolute filenames, + // therefore path is only used for testing + if (!makeAbsPath(filename, path).exists()) { + // The file extension is probably missing. + // Now try to find it out. + string const tex_name = + find_file(filename, path, + known_tex_extensions); + if (!tex_name.empty()) + filename = tex_name; + } + string outname; + if (makeAbsPath(filename, path).exists()) + fix_child_filename(filename); + else + cerr << "Warning: Could not find included file '" + << filename << "'." << endl; + outname = changeExtension(filename, "lyx"); + h_includeonlys.push_back(outname); + } + } + else if (is_known(t.cs(), known_if_3arg_commands)) { // prevent misparsing of \usepackage if it is used // as an argument (see e.g. our own output of diff --git a/src/tex2lyx/Preamble.h b/src/tex2lyx/Preamble.h index 074a85c613..9c70dca62a 100644 --- a/src/tex2lyx/Preamble.h +++ b/src/tex2lyx/Preamble.h @@ -223,6 +223,7 @@ private: std::map h_use_packages; std::string h_use_default_options; std::string h_use_hyperref; + std::vector h_includeonlys; bool h_use_refstyle; bool h_use_minted; diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt index d50ce2138f..5fb90e3c2f 100644 --- a/src/tex2lyx/TODO.txt +++ b/src/tex2lyx/TODO.txt @@ -39,8 +39,6 @@ Format LaTeX feature LyX feature 363 horizontal longtable alignment InsetTabular 364 branch file name suffix \filename_suffix 371 automatic mhchem loading \use_mhchem -375 \includeonly \{begin,end}_includeonly -376 update .aux of unincluded children \maintain_unincluded_children 377 multirow.sty InsetTabular 378 revision info InsetInfo 380 ? InsetPreview diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index 4c12099bde..a8ee7dfb45 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -51,6 +51,13 @@ void parse_text(Parser & p, std::ostream & os, unsigned flags, bool outer, Context & context); void check_comment_bib(std::ostream & os, Context & context); +void fix_child_filename(std::string & name); + +std::string const normalize_filename(std::string const & name); + +std::string find_file(std::string const & name, std::string const & path, + char const * const * extensions); + /*! * Parses a subdocument, usually useful in insets (whence the name). * diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp index b9154b9135..cd7d0a92bd 100644 --- a/src/tex2lyx/text.cpp +++ b/src/tex2lyx/text.cpp @@ -482,22 +482,6 @@ void translate_box_len(string const & length, string & value, string & unit, str } -/*! - * Find a file with basename \p name in path \p path and an extension - * in \p extensions. - */ -string find_file(string const & name, string const & path, - char const * const * extensions) -{ - for (char const * const * what = extensions; *what; ++what) { - string const trial = addExtension(name, *what); - if (makeAbsPath(trial, path).exists()) - return trial; - } - return string(); -} - - void begin_inset(ostream & os, string const & name) { os << "\n\\begin_inset " << name; @@ -2194,96 +2178,6 @@ void get_cite_arguments(Parser & p, bool natbibOrder, } -/// Convert filenames with TeX macros and/or quotes to something LyX -/// can understand -string const normalize_filename(string const & name) -{ - Parser p(name); - ostringstream os; - while (p.good()) { - Token const & t = p.get_token(); - if (t.cat() != catEscape) - os << t.asInput(); - else if (t.cs() == "lyxdot") { - // This is used by LyX for simple dots in relative - // names - os << '.'; - p.skip_spaces(); - } else if (t.cs() == "space") { - os << ' '; - p.skip_spaces(); - } else if (t.cs() == "string") { - // Convert \string" to " and \string~ to ~ - Token const & n = p.next_token(); - if (n.asInput() != "\"" && n.asInput() != "~") - os << t.asInput(); - } else - os << t.asInput(); - } - // Strip quotes. This is a bit complicated (see latex_path()). - string full = os.str(); - if (!full.empty() && full[0] == '"') { - string base = removeExtension(full); - string ext = getExtension(full); - if (!base.empty() && base[base.length()-1] == '"') - // "a b" - // "a b".tex - return addExtension(trim(base, "\""), ext); - if (full[full.length()-1] == '"') - // "a b.c" - // "a b.c".tex - return trim(full, "\""); - } - return full; -} - - -/// Convert \p name from TeX convention (relative to master file) to LyX -/// convention (relative to .lyx file) if it is relative -void fix_child_filename(string & name) -{ - string const absMasterTeX = getMasterFilePath(true); - bool const isabs = FileName::isAbsolute(name); - // convert from "relative to .tex master" to absolute original path - if (!isabs) - name = makeAbsPath(name, absMasterTeX).absFileName(); - bool copyfile = copyFiles(); - string const absParentLyX = getParentFilePath(false); - string abs = name; - if (copyfile) { - // convert from absolute original path to "relative to master file" - string const rel = to_utf8(makeRelPath(from_utf8(name), - from_utf8(absMasterTeX))); - // re-interpret "relative to .tex file" as "relative to .lyx file" - // (is different if the master .lyx file resides in a - // different path than the master .tex file) - string const absMasterLyX = getMasterFilePath(false); - abs = makeAbsPath(rel, absMasterLyX).absFileName(); - // Do not copy if the new path is impossible to create. Example: - // absMasterTeX = "/foo/bar/" - // absMasterLyX = "/bar/" - // name = "/baz.eps" => new absolute name would be "/../baz.eps" - if (contains(name, "/../")) - copyfile = false; - } - if (copyfile) { - if (isabs) - name = abs; - else { - // convert from absolute original path to - // "relative to .lyx file" - name = to_utf8(makeRelPath(from_utf8(abs), - from_utf8(absParentLyX))); - } - } - else if (!isabs) { - // convert from absolute original path to "relative to .lyx file" - name = to_utf8(makeRelPath(from_utf8(name), - from_utf8(absParentLyX))); - } -} - - void copy_file(FileName const & src, string dstname) { if (!copyFiles()) @@ -2508,6 +2402,112 @@ void registerExternalTemplatePackages(string const & name) } // anonymous namespace +/*! + * Find a file with basename \p name in path \p path and an extension + * in \p extensions. + */ +string find_file(string const & name, string const & path, + char const * const * extensions) +{ + for (char const * const * what = extensions; *what; ++what) { + string const trial = addExtension(name, *what); + if (makeAbsPath(trial, path).exists()) + return trial; + } + return string(); +} + + +/// Convert filenames with TeX macros and/or quotes to something LyX +/// can understand +string const normalize_filename(string const & name) +{ + Parser p(name); + ostringstream os; + while (p.good()) { + Token const & t = p.get_token(); + if (t.cat() != catEscape) + os << t.asInput(); + else if (t.cs() == "lyxdot") { + // This is used by LyX for simple dots in relative + // names + os << '.'; + p.skip_spaces(); + } else if (t.cs() == "space") { + os << ' '; + p.skip_spaces(); + } else if (t.cs() == "string") { + // Convert \string" to " and \string~ to ~ + Token const & n = p.next_token(); + if (n.asInput() != "\"" && n.asInput() != "~") + os << t.asInput(); + } else + os << t.asInput(); + } + // Strip quotes. This is a bit complicated (see latex_path()). + string full = os.str(); + if (!full.empty() && full[0] == '"') { + string base = removeExtension(full); + string ext = getExtension(full); + if (!base.empty() && base[base.length()-1] == '"') + // "a b" + // "a b".tex + return addExtension(trim(base, "\""), ext); + if (full[full.length()-1] == '"') + // "a b.c" + // "a b.c".tex + return trim(full, "\""); + } + return full; +} + + +/// Convert \p name from TeX convention (relative to master file) to LyX +/// convention (relative to .lyx file) if it is relative +void fix_child_filename(string & name) +{ + string const absMasterTeX = getMasterFilePath(true); + bool const isabs = FileName::isAbsolute(name); + // convert from "relative to .tex master" to absolute original path + if (!isabs) + name = makeAbsPath(name, absMasterTeX).absFileName(); + bool copyfile = copyFiles(); + string const absParentLyX = getParentFilePath(false); + string abs = name; + if (copyfile) { + // convert from absolute original path to "relative to master file" + string const rel = to_utf8(makeRelPath(from_utf8(name), + from_utf8(absMasterTeX))); + // re-interpret "relative to .tex file" as "relative to .lyx file" + // (is different if the master .lyx file resides in a + // different path than the master .tex file) + string const absMasterLyX = getMasterFilePath(false); + abs = makeAbsPath(rel, absMasterLyX).absFileName(); + // Do not copy if the new path is impossible to create. Example: + // absMasterTeX = "/foo/bar/" + // absMasterLyX = "/bar/" + // name = "/baz.eps" => new absolute name would be "/../baz.eps" + if (contains(name, "/../")) + copyfile = false; + } + if (copyfile) { + if (isabs) + name = abs; + else { + // convert from absolute original path to + // "relative to .lyx file" + name = to_utf8(makeRelPath(from_utf8(abs), + from_utf8(absParentLyX))); + } + } + else if (!isabs) { + // convert from absolute original path to "relative to .lyx file" + name = to_utf8(makeRelPath(from_utf8(name), + from_utf8(absParentLyX))); + } +} + + void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, Context & context) {