]> git.lyx.org Git - lyx.git/blobdiff - src/LaTeX.cpp
Fix text direction issue for InsetInfo in RTL context
[lyx.git] / src / LaTeX.cpp
index e8a6f9f80c3b3149347d0198e4fce08eaae15c76..0144e1cf166cdab2e6890d6ca82652724accf989 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
@@ -92,11 +93,18 @@ bool operator!=(AuxInfo const & a, AuxInfo const & o)
  */
 
 LaTeX::LaTeX(string const & latex, OutputParams const & rp,
-            FileName const & f, string const & p, string const & lp,
-            bool const clean_start)
-       : cmd(latex), file(f), path(p), lpath(lp), runparams(rp), biber(false)
+            FileName const & f, string const & p, string const & lp, 
+            bool allow_cancellation, bool const clean_start)
+       : cmd(latex), file(f), path(p), lpath(lp), runparams(rp), biber(false),
+       allow_cancel(allow_cancellation)
 {
        num_errors = 0;
+       // 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 =
@@ -230,23 +238,27 @@ 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);
        message(runMessage(count));
 
        int exit_code = startscript();
+       if (exit_code == Systemcall::KILLED)
+               return Systemcall::KILLED;
 
        scanres = scanLogFile(terr);
        if (scanres & ERROR_RERUN) {
                LYXERR(Debug::LATEX, "Rerunning LaTeX");
                terr.clearErrors();
                exit_code = startscript();
+               if (exit_code == Systemcall::KILLED)
+                       return Systemcall::KILLED;
                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;
 
@@ -272,20 +284,34 @@ int LaTeX::run(TeXErrors & terr)
                LYXERR(Debug::LATEX, "Running MakeIndex.");
                message(_("Running Index Processor."));
                // onlyFileName() is needed for cygwin
-               rerun |= runMakeIndex(onlyFileName(idxfile.absFileName()),
-                               runparams);
+               int const ret = 
+                               runMakeIndex(onlyFileName(idxfile.absFileName()), runparams);
+               if (ret == Systemcall::KILLED)
+                       return Systemcall::KILLED;
+               rerun = true;
        }
+
        FileName const nlofile(changeExtension(file.absFileName(), ".nlo"));
        // If all nomencl entries are removed, nomencl writes an empty nlo file.
        // DepTable::hasChanged() returns false in this case, since it does not
        // distinguish empty files from non-existing files. This is why we need
        // the extra checks here (to trigger a rerun). Cf. discussions in #8905.
        // FIXME: Sort out the real problem in DepTable.
-       if (head.haschanged(nlofile) || (nlofile.exists() && nlofile.isFileEmpty()))
-               rerun |= runMakeIndexNomencl(file, ".nlo", ".nls");
+       if (head.haschanged(nlofile) || (nlofile.exists() && nlofile.isFileEmpty())) {
+               int const ret = runMakeIndexNomencl(file, ".nlo", ".nls");
+               if (ret == Systemcall::KILLED)
+                       return Systemcall::KILLED;
+               rerun = true;
+       }
+
        FileName const glofile(changeExtension(file.absFileName(), ".glo"));
-       if (head.haschanged(glofile))
-               rerun |= runMakeIndexNomencl(file, ".glo", ".gls");
+       if (head.haschanged(glofile)) {
+               int const ret = runMakeIndexNomencl(file, ".glo", ".gls");
+               if (ret)
+                       return ret;
+               rerun = true;
+       }
+
 
        // check if we're using biber instead of bibtex
        // biber writes no info to the aux file, so we just check
@@ -303,7 +329,10 @@ int LaTeX::run(TeXErrors & terr)
                LYXERR(Debug::LATEX, "Running BibTeX.");
                message(_("Running BibTeX."));
                updateBibtexDependencies(head, bibtex_info);
-               rerun |= runBibTeX(bibtex_info, runparams);
+               int exit_code;
+               rerun |= runBibTeX(bibtex_info, runparams, exit_code);
+               if (exit_code == Systemcall::KILLED)
+                       return Systemcall::KILLED;
                FileName const blgfile(changeExtension(file.absFileName(), ".blg"));
                if (blgfile.exists())
                        bscanres = scanBlgFile(head, terr);
@@ -332,7 +361,9 @@ int LaTeX::run(TeXErrors & terr)
                LYXERR(Debug::DEPEND, "Dep. file has changed or rerun requested");
                LYXERR(Debug::LATEX, "Run #" << count);
                message(runMessage(count));
-               startscript();
+               int exit_code = startscript();
+               if (exit_code == Systemcall::KILLED)
+                       return Systemcall::KILLED;
                scanres = scanLogFile(terr);
 
                // update the depedencies
@@ -341,7 +372,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
@@ -354,7 +385,10 @@ int LaTeX::run(TeXErrors & terr)
                LYXERR(Debug::LATEX, "Running BibTeX.");
                message(_("Running BibTeX."));
                updateBibtexDependencies(head, bibtex_info);
-               rerun |= runBibTeX(bibtex_info, runparams);
+               int exit_code;
+               rerun |= runBibTeX(bibtex_info, runparams, exit_code);
+               if (exit_code == Systemcall::KILLED)
+                       return Systemcall::KILLED;
                FileName const blgfile(changeExtension(file.absFileName(), ".blg"));
                if (blgfile.exists())
                        bscanres = scanBlgFile(head, terr);
@@ -374,15 +408,18 @@ int LaTeX::run(TeXErrors & terr)
                LYXERR(Debug::LATEX, "Running MakeIndex.");
                message(_("Running Index Processor."));
                // onlyFileName() is needed for cygwin
-               rerun = runMakeIndex(onlyFileName(changeExtension(
+               int const ret = runMakeIndex(onlyFileName(changeExtension(
                                file.absFileName(), ".idx")), runparams);
+               if (ret == Systemcall::KILLED)
+                       return Systemcall::KILLED;
+               rerun = true;
        }
 
-       // I am not pretty sure if need this twice.
+       // MSVC complains that bool |= int is unsafe. Not sure why.
        if (head.haschanged(nlofile))
-               rerun |= runMakeIndexNomencl(file, ".nlo", ".nls");
+               rerun |= (runMakeIndexNomencl(file, ".nlo", ".nls") != 0);
        if (head.haschanged(glofile))
-               rerun |= runMakeIndexNomencl(file, ".glo", ".gls");
+               rerun |= (runMakeIndexNomencl(file, ".glo", ".gls") != 0);
 
        // 5
        // we will only run latex more if the log file asks for it.
@@ -435,25 +472,27 @@ int LaTeX::startscript()
                     + quoteName(onlyFileName(file.toFilesystemEncoding()))
                     + " > " + os::nulldev();
        Systemcall one;
-       return one.startscript(Systemcall::Wait, tmp, path, lpath);
+       Systemcall::Starttype const starttype = 
+               allow_cancel ? Systemcall::WaitLoop : Systemcall::Wait;
+       return one.startscript(starttype, tmp, path, lpath, true);
 }
 
 
-bool LaTeX::runMakeIndex(string const & f, OutputParams const & runparams,
+int LaTeX::runMakeIndex(string const & f, OutputParams const & rp,
                         string const & params)
 {
-       string tmp = runparams.use_japanese ?
+       string tmp = rp.use_japanese ?
                lyxrc.jindex_command : lyxrc.index_command;
-       
-       if (!runparams.index_command.empty())
-               tmp = runparams.index_command;
+
+       if (!rp.index_command.empty())
+               tmp = rp.index_command;
 
        LYXERR(Debug::LATEX,
                "idx file has been made, running index processor ("
                << tmp << ") on file " << f);
 
-       tmp = subst(tmp, "$$lang", runparams.document_language);
-       if (runparams.use_indices) {
+       tmp = subst(tmp, "$$lang", rp.document_language);
+       if (rp.use_indices) {
                tmp = lyxrc.splitindex_command + " -m " + quoteName(tmp);
                LYXERR(Debug::LATEX,
                "Multiple indices. Using splitindex command: " << tmp);
@@ -462,35 +501,51 @@ bool LaTeX::runMakeIndex(string const & f, OutputParams const & runparams,
        tmp += quoteName(f);
        tmp += params;
        Systemcall one;
-       one.startscript(Systemcall::Wait, tmp, path, lpath);
-       return true;
+       Systemcall::Starttype const starttype = 
+               allow_cancel ? Systemcall::WaitLoop : Systemcall::Wait;
+       return one.startscript(starttype, tmp, path, lpath, true);
 }
 
 
-bool LaTeX::runMakeIndexNomencl(FileName const & file,
+int LaTeX::runMakeIndexNomencl(FileName const & fname,
                string const & nlo, string const & nls)
 {
        LYXERR(Debug::LATEX, "Running MakeIndex for nomencl.");
        message(_("Running MakeIndex for nomencl."));
        string tmp = lyxrc.nomencl_command + ' ';
        // onlyFileName() is needed for cygwin
-       tmp += quoteName(onlyFileName(changeExtension(file.absFileName(), nlo)));
+       tmp += quoteName(onlyFileName(changeExtension(fname.absFileName(), nlo)));
        tmp += " -o "
-               + onlyFileName(changeExtension(file.toFilesystemEncoding(), nls));
+               + onlyFileName(changeExtension(fname.toFilesystemEncoding(), nls));
        Systemcall one;
-       one.startscript(Systemcall::Wait, tmp, path, lpath);
-       return true;
+       Systemcall::Starttype const starttype = 
+               allow_cancel ? Systemcall::WaitLoop : Systemcall::Wait;
+       return one.startscript(starttype, tmp, path, lpath, true);
 }
 
 
 vector<AuxInfo> const
-LaTeX::scanAuxFiles(FileName const & file)
+LaTeX::scanAuxFiles(FileName const & fname, bool const only_childbibs)
 {
        vector<AuxInfo> result;
 
-       result.push_back(scanAuxFile(file));
+       // 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, fname.onlyPath().realPath());
+                       fn.changeExtension("aux");
+                       if (fn.exists())
+                               result.push_back(scanAuxFile(fn));
+               }
+               return result;
+       }
+
+       result.push_back(scanAuxFile(fname));
 
-       string const basename = removeExtension(file.absFileName());
+       // This is for bibtopic
+       string const basename = removeExtension(fname.absFileName());
        for (int i = 1; i < 1000; ++i) {
                FileName const file2(basename
                        + '.' + convert<string>(i)
@@ -503,20 +558,20 @@ LaTeX::scanAuxFiles(FileName const & file)
 }
 
 
-AuxInfo const LaTeX::scanAuxFile(FileName const & file)
+AuxInfo const LaTeX::scanAuxFile(FileName const & fname)
 {
        AuxInfo result;
-       result.aux_file = file;
-       scanAuxFile(file, result);
+       result.aux_file = fname;
+       scanAuxFile(fname, result);
        return result;
 }
 
 
-void LaTeX::scanAuxFile(FileName const & file, AuxInfo & aux_info)
+void LaTeX::scanAuxFile(FileName const & fname, AuxInfo & aux_info)
 {
-       LYXERR(Debug::LATEX, "Scanning aux file: " << file);
+       LYXERR(Debug::LATEX, "Scanning aux file: " << fname);
 
-       ifstream ifs(file.toFilesystemEncoding().c_str());
+       ifstream ifs(fname.toFilesystemEncoding().c_str());
        string token;
        static regex const reg1("\\\\citation\\{([^}]+)\\}");
        static regex const reg2("\\\\bibdata\\{([^}]+)\\}");
@@ -599,26 +654,28 @@ void LaTeX::updateBibtexDependencies(DepTable & dep,
 
 
 bool LaTeX::runBibTeX(vector<AuxInfo> const & bibtex_info,
-                     OutputParams const & runparams)
+                     OutputParams const & rp, int & exit_code)
 {
        bool result = false;
+       exit_code = 0;
        for (vector<AuxInfo>::const_iterator it = bibtex_info.begin();
             it != bibtex_info.end(); ++it) {
                if (!biber && it->databases.empty())
                        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 = rp.bibtex_command;
                tmp += " ";
                // onlyFileName() is needed for cygwin
                tmp += quoteName(onlyFileName(removeExtension(
                                it->aux_file.absFileName())));
                Systemcall one;
-               one.startscript(Systemcall::Wait, tmp, path, lpath);
+               Systemcall::Starttype const starttype = 
+               allow_cancel ? Systemcall::WaitLoop : Systemcall::Wait;
+               exit_code = one.startscript(starttype, tmp, path, lpath, true);
+               if (exit_code) {
+                       return result;
+               }
        }
        // Return whether bibtex was run
        return result;
@@ -634,16 +691,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 +732,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 +832,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 +844,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 +861,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,13 +872,12 @@ 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);
                        }
 
                        // get the next line
-                       string tmp;
                        int count = 0;
                        do {
                                if (!getline(ifs, tmp))
@@ -882,25 +954,46 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                                || 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';
                        }
                }
        }
@@ -1027,8 +1120,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();
@@ -1045,6 +1138,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
@@ -1089,7 +1191,7 @@ int iterateLine(string const & token, regex const & reg, string const & closing,
        return result;
 }
 
-} // anon namespace
+} // namespace
 
 
 void LaTeX::deplog(DepTable & head)
@@ -1228,7 +1330,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);
                }
@@ -1241,7 +1343,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);
                }
@@ -1296,32 +1398,32 @@ int LaTeX::scanBlgFile(DepTable & dep, TeXErrors & terr)
                         || regex_match(token, sub, bibtexError5)) {
                        retval |= BIBTEX_ERROR;
                        string errstr = N_("BibTeX error: ") + token;
-                       string message;
+                       string msg;
                        if ((prefixIs(token, "while executing---line")
                             || prefixIs(token, "---line ")
                             || prefixIs(token, "*Please notify the BibTeX"))
                            && !prevtoken.empty()) {
                                errstr = N_("BibTeX error: ") + prevtoken;
-                               message = prevtoken + '\n';
+                               msg = prevtoken + '\n';
                        }
-                       message += token;
+                       msg += token;
                        terr.insertError(0,
                                         from_local8bit(errstr),
-                                        from_local8bit(message));
+                                        from_local8bit(msg));
                } else if (regex_match(prevtoken, sub, bibtexError3)) {
                        retval |= BIBTEX_ERROR;
                        string errstr = N_("BibTeX error: ") + prevtoken;
-                       string message = prevtoken + '\n' + token;
+                       string msg = prevtoken + '\n' + token;
                        terr.insertError(0,
                                         from_local8bit(errstr),
-                                        from_local8bit(message));
+                                        from_local8bit(msg));
                } else if (regex_match(token, sub, biberError)) {
                        retval |= BIBTEX_ERROR;
                        string errstr = N_("Biber error: ") + sub.str(2);
-                       string message = token;
+                       string msg = token;
                        terr.insertError(0,
                                         from_local8bit(errstr),
-                                        from_local8bit(message));
+                                        from_local8bit(msg));
                }
                prevtoken = token;
        }