]> git.lyx.org Git - lyx.git/blobdiff - src/LaTeX.cpp
Backport 641ae5c7add025.
[lyx.git] / src / LaTeX.cpp
index 7ce355b39861a60119f8c85f4b5feba03d05c390..df20f7b285bb999fc579674b6c03390ee3ea4cce 100644 (file)
@@ -18,6 +18,7 @@
 #include "BufferList.h"
 #include "LaTeX.h"
 #include "LyXRC.h"
+#include "LyX.h"
 #include "DepTable.h"
 
 #include "support/debug.h"
@@ -57,7 +58,7 @@ docstring runMessage(unsigned int count)
        return bformat(_("Waiting for LaTeX run number %1$d"), count);
 }
 
-} // anon namespace
+} // namespace
 
 /*
  * CLASS TEXERRORS
@@ -97,7 +98,13 @@ LaTeX::LaTeX(string const & latex, OutputParams const & rp,
        : cmd(latex), file(f), path(p), lpath(lp), runparams(rp), biber(false)
 {
        num_errors = 0;
-       if (prefixIs(cmd, "pdf")) { // Do we use pdflatex ?
+       // lualatex can still produce a DVI with --output-format=dvi. However,
+       // we do not use that internally (we use the "dvilualatex" command) so
+       // it would only happen from a custom converter. Thus, it is better to
+       // guess that lualatex produces a PDF than to guess a DVI.
+       // FIXME we should base the extension on the output format, which we should
+       // get in a robust way, e.g. from the converter.
+       if (prefixIs(cmd, "pdf") || prefixIs(cmd, "lualatex") || prefixIs(cmd, "xelatex")) {
                depfile = FileName(file.absFileName() + ".dep-pdf");
                output_file =
                        FileName(changeExtension(file.absFileName(), ".pdf"));
@@ -230,7 +237,7 @@ int LaTeX::run(TeXErrors & terr)
        /// in which case we will not need to run bibtex again.
        vector<AuxInfo> bibtex_info_old;
        if (!run_bibtex)
-               bibtex_info_old = scanAuxFiles(aux_file);
+               bibtex_info_old = scanAuxFiles(aux_file, runparams.only_childbibs);
 
        ++count;
        LYXERR(Debug::LATEX, "Run #" << count);
@@ -246,7 +253,7 @@ int LaTeX::run(TeXErrors & terr)
                scanres = scanLogFile(terr);
        }
 
-       vector<AuxInfo> const bibtex_info = scanAuxFiles(aux_file);
+       vector<AuxInfo> const bibtex_info = scanAuxFiles(aux_file, runparams.only_childbibs);
        if (!run_bibtex && bibtex_info_old != bibtex_info)
                run_bibtex = true;
 
@@ -341,7 +348,7 @@ int LaTeX::run(TeXErrors & terr)
        } else {
                LYXERR(Debug::DEPEND, "Dep. file has NOT changed");
        }
-       
+
        // 3
        // rerun bibtex?
        // Complex bibliography packages such as Biblatex require
@@ -444,7 +451,7 @@ bool LaTeX::runMakeIndex(string const & f, OutputParams const & runparams,
 {
        string tmp = runparams.use_japanese ?
                lyxrc.jindex_command : lyxrc.index_command;
-       
+
        if (!runparams.index_command.empty())
                tmp = runparams.index_command;
 
@@ -484,12 +491,26 @@ bool LaTeX::runMakeIndexNomencl(FileName const & file,
 
 
 vector<AuxInfo> const
-LaTeX::scanAuxFiles(FileName const & file)
+LaTeX::scanAuxFiles(FileName const & file, bool const only_childbibs)
 {
        vector<AuxInfo> result;
 
+       // With chapterbib, we have to bibtex all children's aux files
+       // but _not_ the master's!
+       if (only_childbibs) {
+               for (string const &s: children) {
+                       FileName fn =
+                               makeAbsPath(s, file.onlyPath().realPath());
+                       fn.changeExtension("aux");
+                       if (fn.exists())
+                               result.push_back(scanAuxFile(fn));
+               }
+               return result;
+       }
+
        result.push_back(scanAuxFile(file));
 
+       // This is for bibtopic
        string const basename = removeExtension(file.absFileName());
        for (int i = 1; i < 1000; ++i) {
                FileName const file2(basename
@@ -608,11 +629,7 @@ bool LaTeX::runBibTeX(vector<AuxInfo> const & bibtex_info,
                        continue;
                result = true;
 
-               string tmp = runparams.use_japanese ?
-                       lyxrc.jbibtex_command : lyxrc.bibtex_command;
-
-               if (!runparams.bibtex_command.empty())
-                       tmp = runparams.bibtex_command;
+               string tmp = runparams.bibtex_command;
                tmp += " ";
                // onlyFileName() is needed for cygwin
                tmp += quoteName(onlyFileName(removeExtension(
@@ -634,16 +651,20 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                onlyFileName(changeExtension(file.absFileName(), ".log"));
        LYXERR(Debug::LATEX, "Log file: " << tmp);
        FileName const fn = FileName(makeAbsPath(tmp));
+       // FIXME we should use an ifdocstream here and a docstring for token
+       // below. The encoding of the log file depends on the _output_ (font)
+       // encoding of the TeX file (T1, TU etc.). See #10728.
        ifstream ifs(fn.toFilesystemEncoding().c_str());
        bool fle_style = false;
        static regex const file_line_error(".+\\.\\D+:[0-9]+: (.+)");
-       static regex const child_file(".*([0-9]+[A-Za-z]*_.+\\.tex).*");
+       static regex const child_file("[^0-9]*([0-9]+[A-Za-z]*_.+\\.tex).*");
        // Flag for 'File ended while scanning' message.
        // We need to wait for subsequent processing.
        string wait_for_error;
        string child_name;
        int pnest = 0;
        stack <pair<string, int> > child;
+       children.clear();
 
        string token;
        while (getline(ifs, token)) {
@@ -671,7 +692,12 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                                string const substr = token.substr(i + 1, len);
                                if (regex_match(substr, sub, child_file)) {
                                        string const name = sub.str(1);
-                                       child.push(make_pair(name, pnest));
+                                       // Sometimes also masters have a name that matches
+                                       // (if their name starts with a number and _)
+                                       if (name != file.onlyFileName()) {
+                                               child.push(make_pair(name, pnest));
+                                               children.push_back(name);
+                                       }
                                        i += len;
                                }
                        } else if (token[i] == ')') {
@@ -766,7 +792,7 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                        if (contains(token, "LaTeX Error:"))
                                retval |= LATEX_ERROR;
 
-                       if (prefixIs(token, "! File ended while scanning")){
+                       if (prefixIs(token, "! File ended while scanning")) {
                                if (prefixIs(token, "! File ended while scanning use of \\Hy@setref@link.")){
                                        // bug 7344. We must rerun LaTeX if hyperref has been toggled.
                                        retval |= ERROR_RERUN;
@@ -778,6 +804,12 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                                }
                        }
 
+                       if (prefixIs(token, "! Incomplete \\if")) {
+                               // bug 10666. At this point its not clear we finish with error.
+                               wait_for_error = desc;
+                               continue;
+                       }
+
                        if (prefixIs(token, "! Paragraph ended before \\Hy@setref@link was complete.")){
                                        // bug 7344. We must rerun LaTeX if hyperref has been toggled.
                                        retval |= ERROR_RERUN;
@@ -789,6 +821,7 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                                string errstr;
                                int count = 0;
                                errstr = wait_for_error;
+                               wait_for_error.clear();
                                do {
                                        if (!getline(ifs, tmp))
                                                break;
@@ -799,7 +832,7 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                                } while (!contains(tmp, "(job aborted"));
 
                                terr.insertError(0,
-                                                from_local8bit("Emergency stop"),
+                                                from_ascii("Emergency stop"),
                                                 from_local8bit(errstr),
                                                 child_name);
                        }
@@ -878,28 +911,50 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                        } else if (contains(token, "Rerun to get citations")) {
                                // Natbib seems to use this.
                                retval |= UNDEF_CIT;
-                       } else if (contains(token, "No pages of output")) {
+                       } else if (contains(token, "No pages of output")
+                               || contains(token, "no pages of output")) {
                                // No output file (e.g. the DVI or PDF) was created
                                retval |= NO_OUTPUT;
+                       } else if (contains(token, "Error 256 (driver return code)")) {
+                               // This is a xdvipdfmx driver error reported by XeTeX.
+                               // We have to check whether an output PDF file was created.
+                               FileName pdffile = file;
+                               pdffile.changeExtension("pdf");
+                               if (!pdffile.exists())
+                                       // No output PDF file was created (see #10076)
+                                       retval |= NO_OUTPUT;
                        } else if (contains(token, "That makes 100 errors")) {
-                               // More than 100 errors were reprted
+                               // More than 100 errors were reported
                                retval |= TOO_MANY_ERRORS;
                        } else if (prefixIs(token, "!pdfTeX error:")) {
                                // otherwise we dont catch e.g.:
                                // !pdfTeX error: pdflatex (file feyn10): Font feyn10 at 600 not found
                                retval |= ERRORS;
                                terr.insertError(0,
-                                                from_local8bit("pdfTeX Error"),
-                                                from_local8bit(token),
-                                                child_name);
-                       } else if (prefixIs(token, "Missing character: There is no ")) {
-                               // XeTeX/LuaTeX error about missing glyph in selected font
-                               // (bug 9610)
-                               retval |= LATEX_ERROR;
-                               terr.insertError(0,
-                                                from_local8bit("Missing glyphs!"),
+                                                from_ascii("pdfTeX Error"),
                                                 from_local8bit(token),
                                                 child_name);
+                       } else if (!ignore_missing_glyphs
+                                  && prefixIs(token, "Missing character: There is no ")
+                                  && !contains(token, "nullfont")) {
+                               // Warning about missing glyph in selected font
+                               // may be dataloss (bug 9610)
+                               // but can be ignored for 'nullfont' (bug 10394).
+                               // as well as for ZERO WIDTH NON-JOINER (0x200C) which is
+                               // missing in many fonts and output for ligature break (bug 10727).
+                               // Since this error only occurs with utf8 output, we can safely assume
+                               // that the log file is utf8-encoded
+                               docstring const utoken = from_utf8(token);
+                               if (!contains(utoken, 0x200C)) {
+                                       retval |= LATEX_ERROR;
+                                       terr.insertError(0,
+                                                        from_ascii("Missing glyphs!"),
+                                                        utoken,
+                                                        child_name);
+                               }
+                       } else if (!wait_for_error.empty()) {
+                               // We collect information until we know we have an error.
+                               wait_for_error += token + '\n';
                        }
                }
        }
@@ -1026,8 +1081,8 @@ bool completeFilename(string const & ff, DepTable & head)
 }
 
 
-int iterateLine(string const & token, regex const & reg, string const & closing,
-               int fragment_pos, DepTable & head)
+int iterateLine(string const & token, regex const & reg, string const & opening,
+               string const & closing, int fragment_pos, DepTable & head)
 {
        smatch what;
        string::const_iterator first = token.begin();
@@ -1044,6 +1099,15 @@ int iterateLine(string const & token, regex const & reg, string const & closing,
                                // since we had a closing bracket,
                                // do not investigate further
                                fragment = false;
+                       } else if (what.str(2) == opening) {
+                               // if we have another opening bracket,
+                               // we might have a nested file chain
+                               // as is (file.ext (subfile.ext))
+                               fragment = !handleFoundFile(rtrim(what.str(1)), head);
+                               // decrease first position by one in order to
+                               // consider the opening delimiter on next iteration
+                               if (first > token.begin())
+                                       --first;
                        } else
                                // if we have no closing bracket,
                                // try to handle as file nevertheless
@@ -1088,7 +1152,7 @@ int iterateLine(string const & token, regex const & reg, string const & closing,
        return result;
 }
 
-} // anon namespace
+} // namespace
 
 
 void LaTeX::deplog(DepTable & head)
@@ -1227,7 +1291,7 @@ void LaTeX::deplog(DepTable & head)
                if (regex_match(token, sub, reg5)) {
                        // search for strings in <...>
                        static regex const reg5_1("<([^>]+)(.)");
-                       fragment_pos = iterateLine(token, reg5_1, ">",
+                       fragment_pos = iterateLine(token, reg5_1, "<", ">",
                                                   fragment_pos, head);
                        fragment = (fragment_pos != -1);
                }
@@ -1240,7 +1304,7 @@ void LaTeX::deplog(DepTable & head)
                if (regex_match(token, sub, reg6)) {
                        // search for strings in (...)
                        static regex const reg6_1("\\(([^()]+)(.)");
-                       fragment_pos = iterateLine(token, reg6_1, ")",
+                       fragment_pos = iterateLine(token, reg6_1, "(", ")",
                                                   fragment_pos, head);
                        fragment = (fragment_pos != -1);
                }