]> git.lyx.org Git - lyx.git/blobdiff - src/LaTeX.cpp
Cmake build: Don't use temporary doc-files as source for installation
[lyx.git] / src / LaTeX.cpp
index 5623bd2c1304065d1e2e0b169911fe2e1efbf70f..10540642b1ac652f487393cac17720e219dfd7cb 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"
@@ -49,7 +50,6 @@ namespace os = support::os;
 //   different way.
 // - the makeindex style files should be taken care of with
 //   the dependency mechanism.
-// - we should perhaps also scan the bibtex log file
 
 namespace {
 
@@ -58,7 +58,7 @@ docstring runMessage(unsigned int count)
        return bformat(_("Waiting for LaTeX run number %1$d"), count);
 }
 
-} // anon namespace
+} // namespace
 
 /*
  * CLASS TEXERRORS
@@ -93,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)
-       : cmd(latex), file(f), path(p), runparams(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)
 {
        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"));
@@ -106,12 +113,16 @@ LaTeX::LaTeX(string const & latex, OutputParams const & rp,
                output_file =
                        FileName(changeExtension(file.absFileName(), ".dvi"));
        }
+       if (clean_start)
+               removeAuxiliaryFiles();
 }
 
 
-void LaTeX::deleteFilesOnError() const
+void LaTeX::removeAuxiliaryFiles() const
 {
-       // currently just a dummy function.
+       // Note that we do not always call this function when there is an error.
+       // For example, if there is an error but an output file is produced we
+       // still would like to output (export/view) the file.
 
        // What files do we have to delete?
 
@@ -143,6 +154,13 @@ void LaTeX::deleteFilesOnError() const
        // Also remove the aux file
        FileName const aux(changeExtension(file.absFileName(), ".aux"));
        aux.removeFile();
+
+       // Also remove the .out file (e.g. hyperref bookmarks) (#9963)
+       FileName const out(changeExtension(file.absFileName(), ".out"));
+       out.removeFile();
+
+       // Remove the output file, which is often generated even if error
+       output_file.removeFile();
 }
 
 
@@ -154,6 +172,7 @@ int LaTeX::run(TeXErrors & terr)
        // each time the .tex file changes.
 {
        int scanres = NO_ERRORS;
+       int bscanres = NO_ERRORS;
        unsigned int count = 0; // number of times run
        num_errors = 0; // just to make sure.
        unsigned int const MAX_RUN = 6;
@@ -164,8 +183,6 @@ int LaTeX::run(TeXErrors & terr)
        theBufferList().updateIncludedTeXfiles(FileName::getcwd().absFileName(),
                runparams);
 
-       // Never write the depfile if an error was encountered.
-
        // 0
        // first check if the file dependencies exist:
        //     ->If it does exist
@@ -184,7 +201,7 @@ int LaTeX::run(TeXErrors & terr)
 
        bool had_depfile = depfile.exists();
        bool run_bibtex = false;
-       FileName const aux_file(changeExtension(file.absFileName(), "aux"));
+       FileName const aux_file(changeExtension(file.absFileName(), ".aux"));
 
        if (had_depfile) {
                LYXERR(Debug::DEPEND, "Dependency file exists");
@@ -220,26 +237,23 @@ 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));
 
-       startscript();
+       int exit_code = startscript();
+
        scanres = scanLogFile(terr);
        if (scanres & ERROR_RERUN) {
                LYXERR(Debug::LATEX, "Rerunning LaTeX");
-               startscript();
+               terr.clearErrors();
+               exit_code = startscript();
                scanres = scanLogFile(terr);
        }
 
-       if (scanres & ERRORS) {
-               deleteFilesOnError();
-               return scanres; // return on error
-       }
-
-       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;
 
@@ -269,7 +283,12 @@ int LaTeX::run(TeXErrors & terr)
                                runparams);
        }
        FileName const nlofile(changeExtension(file.absFileName(), ".nlo"));
-       if (head.haschanged(nlofile))
+       // 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");
        FileName const glofile(changeExtension(file.absFileName(), ".glo"));
        if (head.haschanged(glofile))
@@ -292,14 +311,9 @@ int LaTeX::run(TeXErrors & terr)
                message(_("Running BibTeX."));
                updateBibtexDependencies(head, bibtex_info);
                rerun |= runBibTeX(bibtex_info, runparams);
-               if (biber) {
-                       // since biber writes no info to the aux file, we have
-                       // to parse the blg file (which only exists after biber
-                       // was first issued)
-                       FileName const blgfile(changeExtension(file.absFileName(), ".blg"));
-                       if (blgfile.exists())
-                               scanBlgFile(head);
-               }
+               FileName const blgfile(changeExtension(file.absFileName(), ".blg"));
+               if (blgfile.exists())
+                       bscanres = scanBlgFile(head, terr);
        } else if (!had_depfile) {
                /// If we run pdflatex on the file after running latex on it,
                /// then we do not need to run bibtex, but we do need to
@@ -327,10 +341,6 @@ int LaTeX::run(TeXErrors & terr)
                message(runMessage(count));
                startscript();
                scanres = scanLogFile(terr);
-               if (scanres & ERRORS) {
-                       deleteFilesOnError();
-                       return scanres; // return on error
-               }
 
                // update the depedencies
                deplog(head); // reads the latex log
@@ -338,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
@@ -352,6 +362,9 @@ int LaTeX::run(TeXErrors & terr)
                message(_("Running BibTeX."));
                updateBibtexDependencies(head, bibtex_info);
                rerun |= runBibTeX(bibtex_info, runparams);
+               FileName const blgfile(changeExtension(file.absFileName(), ".blg"));
+               if (blgfile.exists())
+                       bscanres = scanBlgFile(head, terr);
        }
 
        // 4
@@ -398,10 +411,6 @@ int LaTeX::run(TeXErrors & terr)
                message(runMessage(count));
                startscript();
                scanres = scanLogFile(terr);
-               if (scanres & ERRORS) {
-                       deleteFilesOnError();
-                       return scanres; // return on error
-               }
 
                // keep this updated
                head.update();
@@ -409,7 +418,19 @@ int LaTeX::run(TeXErrors & terr)
 
        // Write the dependencies to file.
        head.write(depfile);
+
+       if (exit_code) {
+               // add flag here, just before return, instead of when exit_code
+               // is defined because scanres is sometimes overwritten above
+               // (e.g. rerun)
+               scanres |= NONZERO_ERROR;
+       }
+
        LYXERR(Debug::LATEX, "Done.");
+
+       if (bscanres & ERRORS)
+               return bscanres; // return on error
+
        return scanres;
 }
 
@@ -421,7 +442,7 @@ int LaTeX::startscript()
                     + quoteName(onlyFileName(file.toFilesystemEncoding()))
                     + " > " + os::nulldev();
        Systemcall one;
-       return one.startscript(Systemcall::Wait, tmp, path);
+       return one.startscript(Systemcall::Wait, tmp, path, lpath);
 }
 
 
@@ -430,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;
 
@@ -448,7 +469,7 @@ bool LaTeX::runMakeIndex(string const & f, OutputParams const & runparams,
        tmp += quoteName(f);
        tmp += params;
        Systemcall one;
-       one.startscript(Systemcall::Wait, tmp, path);
+       one.startscript(Systemcall::Wait, tmp, path, lpath);
        return true;
 }
 
@@ -464,18 +485,32 @@ bool LaTeX::runMakeIndexNomencl(FileName const & file,
        tmp += " -o "
                + onlyFileName(changeExtension(file.toFilesystemEncoding(), nls));
        Systemcall one;
-       one.startscript(Systemcall::Wait, tmp, path);
+       one.startscript(Systemcall::Wait, tmp, path, lpath);
        return true;
 }
 
 
 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
@@ -578,7 +613,8 @@ void LaTeX::updateBibtexDependencies(DepTable & dep,
        // biber writes nothing into the aux file.
        // Instead, we have to scan the blg file
        if (biber) {
-               scanBlgFile(dep);
+               TeXErrors terr;
+               scanBlgFile(dep, terr);
        }
 }
 
@@ -593,17 +629,13 @@ 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(
                                it->aux_file.absFileName())));
                Systemcall one;
-               one.startscript(Systemcall::Wait, tmp, path);
+               one.startscript(Systemcall::Wait, tmp, path, lpath);
        }
        // Return whether bibtex was run
        return result;
@@ -619,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 file_line_error(".+\\.\\D+:[0-9]+: (.+)");
-       static regex child_file(".*([0-9]+[A-Za-z]*_.+\\.tex).*");
+       static regex const file_line_error(".+\\.\\D+:[0-9]+: (.+)");
+       static regex const child_file(".*([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)) {
@@ -653,10 +689,11 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                                size_t len = j == string::npos
                                                ? token.substr(i + 1).length()
                                                : j - i - 1;
-                               if (regex_match(token.substr(i + 1, len),
-                                                       sub, child_file)) {
+                               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));
+                                       children.push_back(name);
                                        i += len;
                                }
                        } else if (token[i] == ')') {
@@ -694,6 +731,10 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                                   && contains(token, "on page")
                                   && contains(token, "undefined")) {
                                retval |= UNDEF_CIT;
+                       } else if (contains(token, "Citation")
+                                  && contains(token, "on input line")
+                                  && contains(token, "undefined")) {
+                               retval |= UNDEF_CIT;
                        }
                } else if (prefixIs(token, "Package")) {
                        // Package warnings
@@ -747,7 +788,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;
@@ -759,6 +800,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;
@@ -770,6 +817,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;
@@ -780,7 +828,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);
                        }
@@ -792,16 +840,21 @@ int LaTeX::scanLogFile(TeXErrors & terr)
                                if (!getline(ifs, tmp))
                                        break;
                                tmp = rtrim(tmp, "\r");
-                               if (++count > 10)
+                               // 15 is somewhat arbitrarily chosen, based on practice.
+                               // We used 10 for 14 years and increased it to 15 when we
+                               // saw one case.
+                               if (++count > 15)
                                        break;
                        } while (!prefixIs(tmp, "l."));
                        if (prefixIs(tmp, "l.")) {
                                // we have a latex error
                                retval |=  TEX_ERROR;
                                if (contains(desc,
-                                   "Package babel Error: You haven't defined the language") ||
-                                   contains(desc,
-                                   "Package babel Error: You haven't loaded the option"))
+                                       "Package babel Error: You haven't defined the language")
+                                   || contains(desc,
+                                       "Package babel Error: You haven't loaded the option")
+                                   || contains(desc,
+                                       "Package babel Error: Unknown language"))
                                        retval |= ERROR_RERUN;
                                // get the line number:
                                int line = 0;
@@ -854,20 +907,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")) {
-                               // A dvi file was not created
+                       } 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:")){
+                       } 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_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_local8bit("pdfTeX Error"),
-                                                        from_local8bit(token),
+                                                        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';
                        }
                }
        }
@@ -923,8 +1006,7 @@ bool handleFoundFile(string const & ff, DepTable & head)
                                        return true;
                                // strip off part after last space and try again
                                string tmp = strippedfile;
-                               string const stripoff =
-                                       rsplit(tmp, strippedfile, ' ');
+                               rsplit(tmp, strippedfile, ' ');
                                absname.set(strippedfile);
                                if (insertIfExists(absname, head))
                                        return true;
@@ -949,8 +1031,7 @@ bool handleFoundFile(string const & ff, DepTable & head)
                                break;
                        // strip off part after last space and try again
                        string strippedfile;
-                       string const stripoff =
-                               rsplit(foundfile, strippedfile, ' ');
+                       rsplit(foundfile, strippedfile, ' ');
                        foundfile = strippedfile;
                        onlyfile = onlyFileName(strippedfile);
                        absname = makeAbsPath(onlyfile);
@@ -996,7 +1077,7 @@ bool completeFilename(string const & ff, DepTable & head)
 }
 
 
-int iterateLine(string const token, regex const reg, string const closing,
+int iterateLine(string const & token, regex const & reg, string const & closing,
                int fragment_pos, DepTable & head)
 {
        smatch what;
@@ -1058,7 +1139,7 @@ int iterateLine(string const token, regex const reg, string const closing,
        return result;
 }
 
-} // anon namespace
+} // namespace
 
 
 void LaTeX::deplog(DepTable & head)
@@ -1196,7 +1277,7 @@ void LaTeX::deplog(DepTable & head)
                // (and in addition to those above)
                if (regex_match(token, sub, reg5)) {
                        // search for strings in <...>
-                       static regex reg5_1("<([^>]+)(.)");
+                       static regex const reg5_1("<([^>]+)(.)");
                        fragment_pos = iterateLine(token, reg5_1, ">",
                                                   fragment_pos, head);
                        fragment = (fragment_pos != -1);
@@ -1209,7 +1290,7 @@ void LaTeX::deplog(DepTable & head)
                // where "File: file.ext" would be skipped
                if (regex_match(token, sub, reg6)) {
                        // search for strings in (...)
-                       static regex reg6_1("\\(([^()]+)(.)");
+                       static regex const reg6_1("\\(([^()]+)(.)");
                        fragment_pos = iterateLine(token, reg6_1, ")",
                                                   fragment_pos, head);
                        fragment = (fragment_pos != -1);
@@ -1229,7 +1310,7 @@ void LaTeX::deplog(DepTable & head)
 }
 
 
-void LaTeX::scanBlgFile(DepTable & dep)
+int LaTeX::scanBlgFile(DepTable & dep, TeXErrors & terr)
 {
        FileName const blg_file(changeExtension(file.absFileName(), "blg"));
        LYXERR(Debug::LATEX, "Scanning blg file: " << blg_file);
@@ -1237,7 +1318,15 @@ void LaTeX::scanBlgFile(DepTable & dep)
        ifstream ifs(blg_file.toFilesystemEncoding().c_str());
        string token;
        static regex const reg1(".*Found (bibtex|BibTeX) data (file|source) '([^']+).*");
+       static regex const bibtexError("^(.*---line [0-9]+ of file).*$");
+       static regex const bibtexError2("^(.*---while reading file).*$");
+       static regex const bibtexError3("(A bad cross reference---).*");
+       static regex const bibtexError4("(Sorry---you've exceeded BibTeX's).*");
+       static regex const bibtexError5("\\*Please notify the BibTeX maintainer\\*");
+       static regex const biberError("^.*> (FATAL|ERROR) - (.*)$");
+       int retval = NO_ERRORS;
 
+       string prevtoken;
        while (getline(ifs, token)) {
                token = rtrim(token, "\r");
                smatch sub;
@@ -1251,7 +1340,42 @@ void LaTeX::scanBlgFile(DepTable & dep)
                                handleFoundFile(data, dep);
                        }
                }
-       } 
+               else if (regex_match(token, sub, bibtexError)
+                        || regex_match(token, sub, bibtexError2)
+                        || regex_match(token, sub, bibtexError4)
+                        || regex_match(token, sub, bibtexError5)) {
+                       retval |= BIBTEX_ERROR;
+                       string errstr = N_("BibTeX error: ") + token;
+                       string message;
+                       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';
+                       }
+                       message += token;
+                       terr.insertError(0,
+                                        from_local8bit(errstr),
+                                        from_local8bit(message));
+               } else if (regex_match(prevtoken, sub, bibtexError3)) {
+                       retval |= BIBTEX_ERROR;
+                       string errstr = N_("BibTeX error: ") + prevtoken;
+                       string message = prevtoken + '\n' + token;
+                       terr.insertError(0,
+                                        from_local8bit(errstr),
+                                        from_local8bit(message));
+               } else if (regex_match(token, sub, biberError)) {
+                       retval |= BIBTEX_ERROR;
+                       string errstr = N_("Biber error: ") + sub.str(2);
+                       string message = token;
+                       terr.insertError(0,
+                                        from_local8bit(errstr),
+                                        from_local8bit(message));
+               }
+               prevtoken = token;
+       }
+       return retval;
 }