X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FLaTeX.cpp;h=3b06c8e34c0ed936149331ba70ea71999486d777;hb=55a3dd7b346d29a52ba305a4558e9e380ef50f47;hp=49968ffdc8a6884995b4f53ff166c66fe09b7b05;hpb=b01b9b5e84365ab1d92d2af88d3a0ce9431ebed8;p=lyx.git diff --git a/src/LaTeX.cpp b/src/LaTeX.cpp index 49968ffdc8..3b06c8e34c 100644 --- a/src/LaTeX.cpp +++ b/src/LaTeX.cpp @@ -29,12 +29,11 @@ #include "support/Systemcall.h" #include "support/os.h" -#include +#include "support/regex.h" #include +#include -using boost::regex; -using boost::smatch; using namespace std; using namespace lyx::support; @@ -50,7 +49,6 @@ namespace os = support::os; // different way. // - the makeindex style files should be taken care of with // the dependency mechanism. -// - somewhere support viewing of bibtex and makeindex log files. // - we should perhaps also scan the bibtex log file namespace { @@ -67,9 +65,10 @@ docstring runMessage(unsigned int count) */ void TeXErrors::insertError(int line, docstring const & error_desc, - docstring const & error_text) + docstring const & error_text, + string const & child_name) { - Error newerr(line, error_desc, error_text); + Error newerr(line, error_desc, error_text, child_name); errors.push_back(newerr); } @@ -94,18 +93,18 @@ bool operator!=(AuxInfo const & a, AuxInfo const & o) */ LaTeX::LaTeX(string const & latex, OutputParams const & rp, - FileName const & f) - : cmd(latex), file(f), runparams(rp) + FileName const & f, string const & p) + : cmd(latex), file(f), path(p), runparams(rp) { num_errors = 0; if (prefixIs(cmd, "pdf")) { // Do we use pdflatex ? - depfile = FileName(file.absFilename() + ".dep-pdf"); + depfile = FileName(file.absFileName() + ".dep-pdf"); output_file = - FileName(changeExtension(file.absFilename(), ".pdf")); + FileName(changeExtension(file.absFileName(), ".pdf")); } else { - depfile = FileName(file.absFilename() + ".dep"); + depfile = FileName(file.absFileName() + ".dep"); output_file = - FileName(changeExtension(file.absFilename(), ".dvi")); + FileName(changeExtension(file.absFileName(), ".dvi")); } } @@ -122,23 +121,27 @@ void LaTeX::deleteFilesOnError() const // but the reason for the error might be in a generated file... // bibtex file - FileName const bbl(changeExtension(file.absFilename(), ".bbl")); + FileName const bbl(changeExtension(file.absFileName(), ".bbl")); bbl.removeFile(); + // biber file + FileName const bcf(changeExtension(file.absFileName(), ".bcf")); + bcf.removeFile(); + // makeindex file - FileName const ind(changeExtension(file.absFilename(), ".ind")); + FileName const ind(changeExtension(file.absFileName(), ".ind")); ind.removeFile(); // nomencl file - FileName const nls(changeExtension(file.absFilename(), ".nls")); + FileName const nls(changeExtension(file.absFileName(), ".nls")); nls.removeFile(); // nomencl file (old version of the package) - FileName const gls(changeExtension(file.absFilename(), ".gls")); + FileName const gls(changeExtension(file.absFileName(), ".gls")); gls.removeFile(); // Also remove the aux file - FileName const aux(changeExtension(file.absFilename(), ".aux")); + FileName const aux(changeExtension(file.absFileName(), ".aux")); aux.removeFile(); } @@ -147,7 +150,7 @@ int LaTeX::run(TeXErrors & terr) // We know that this function will only be run if the lyx buffer // has been changed. We also know that a newly written .tex file // is always different from the previous one because of the date - // in it. However it seems safe to run latex (at least) on time + // in it. However it seems safe to run latex (at least) one time // each time the .tex file changes. { int scanres = NO_ERRORS; @@ -158,7 +161,7 @@ int LaTeX::run(TeXErrors & terr) bool rerun = false; // rerun requested // The class LaTeX does not know the temp path. - theBufferList().updateIncludedTeXfiles(FileName::getcwd().absFilename(), + theBufferList().updateIncludedTeXfiles(FileName::getcwd().absFileName(), runparams); // Never write the depfile if an error was encountered. @@ -181,7 +184,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"); @@ -253,7 +256,7 @@ int LaTeX::run(TeXErrors & terr) // memoir (at least) writes an empty *idx file in the first place. // A second latex run is needed. - FileName const idxfile(changeExtension(file.absFilename(), ".idx")); + FileName const idxfile(changeExtension(file.absFileName(), ".idx")); rerun = idxfile.exists() && idxfile.isFileEmpty(); // run makeindex @@ -261,17 +264,23 @@ int LaTeX::run(TeXErrors & terr) // no checks for now LYXERR(Debug::LATEX, "Running MakeIndex."); message(_("Running Index Processor.")); - // onlyFilename() is needed for cygwin - rerun |= runMakeIndex(onlyFilename(idxfile.absFilename()), + // onlyFileName() is needed for cygwin + rerun |= runMakeIndex(onlyFileName(idxfile.absFileName()), runparams); } - FileName const nlofile(changeExtension(file.absFilename(), ".nlo")); + FileName const nlofile(changeExtension(file.absFileName(), ".nlo")); if (head.haschanged(nlofile)) rerun |= runMakeIndexNomencl(file, ".nlo", ".nls"); - FileName const glofile(changeExtension(file.absFilename(), ".glo")); + FileName const glofile(changeExtension(file.absFileName(), ".glo")); if (head.haschanged(glofile)) rerun |= runMakeIndexNomencl(file, ".glo", ".gls"); + // check if we're using biber instead of bibtex + // biber writes no info to the aux file, so we just check + // if a bcf file exists (and if it was updated) + FileName const bcffile(changeExtension(file.absFileName(), ".bcf")); + biber |= head.exist(bcffile); + // run bibtex // if (scanres & UNDEF_CIT || scanres & RERUN || run_bibtex) if (scanres & UNDEF_CIT || run_bibtex) { @@ -283,6 +292,14 @@ 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); + } } 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 @@ -350,9 +367,9 @@ int LaTeX::run(TeXErrors & terr) // no checks for now LYXERR(Debug::LATEX, "Running MakeIndex."); message(_("Running Index Processor.")); - // onlyFilename() is needed for cygwin - rerun = runMakeIndex(onlyFilename(changeExtension( - file.absFilename(), ".idx")), runparams); + // onlyFileName() is needed for cygwin + rerun = runMakeIndex(onlyFileName(changeExtension( + file.absFileName(), ".idx")), runparams); } // I am not pretty sure if need this twice. @@ -399,12 +416,12 @@ int LaTeX::run(TeXErrors & terr) int LaTeX::startscript() { - // onlyFilename() is needed for cygwin + // onlyFileName() is needed for cygwin string tmp = cmd + ' ' - + quoteName(onlyFilename(file.toFilesystemEncoding())) + + quoteName(onlyFileName(file.toFilesystemEncoding())) + " > " + os::nulldev(); Systemcall one; - return one.startscript(Systemcall::Wait, tmp); + return one.startscript(Systemcall::Wait, tmp, path); } @@ -431,7 +448,7 @@ bool LaTeX::runMakeIndex(string const & f, OutputParams const & runparams, tmp += quoteName(f); tmp += params; Systemcall one; - one.startscript(Systemcall::Wait, tmp); + one.startscript(Systemcall::Wait, tmp, path); return true; } @@ -442,12 +459,12 @@ bool LaTeX::runMakeIndexNomencl(FileName const & file, 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))); + // onlyFileName() is needed for cygwin + tmp += quoteName(onlyFileName(changeExtension(file.absFileName(), nlo))); tmp += " -o " - + onlyFilename(changeExtension(file.toFilesystemEncoding(), nls)); + + onlyFileName(changeExtension(file.toFilesystemEncoding(), nls)); Systemcall one; - one.startscript(Systemcall::Wait, tmp); + one.startscript(Systemcall::Wait, tmp, path); return true; } @@ -459,7 +476,7 @@ LaTeX::scanAuxFiles(FileName const & file) result.push_back(scanAuxFile(file)); - string const basename = removeExtension(file.absFilename()); + string const basename = removeExtension(file.absFileName()); for (int i = 1; i < 1000; ++i) { FileName const file2(basename + '.' + convert(i) @@ -539,7 +556,7 @@ void LaTeX::updateBibtexDependencies(DepTable & dep, // remove all ".bib" and ".bst" files. dep.remove_files_with_extension(".bib"); dep.remove_files_with_extension(".bst"); - //string aux = OnlyFilename(ChangeExtension(file, ".aux")); + //string aux = OnlyFileName(ChangeExtension(file, ".aux")); for (vector::const_iterator it = bibtex_info.begin(); it != bibtex_info.end(); ++it) { @@ -557,6 +574,12 @@ void LaTeX::updateBibtexDependencies(DepTable & dep, dep.insert(file, true); } } + + // biber writes nothing into the aux file. + // Instead, we have to scan the blg file + if (biber) { + scanBlgFile(dep); + } } @@ -566,7 +589,7 @@ bool LaTeX::runBibTeX(vector const & bibtex_info, bool result = false; for (vector::const_iterator it = bibtex_info.begin(); it != bibtex_info.end(); ++it) { - if (it->databases.empty()) + if (!biber && it->databases.empty()) continue; result = true; @@ -576,11 +599,11 @@ bool LaTeX::runBibTeX(vector const & bibtex_info, if (!runparams.bibtex_command.empty()) tmp = runparams.bibtex_command; tmp += " "; - // onlyFilename() is needed for cygwin - tmp += quoteName(onlyFilename(removeExtension( - it->aux_file.absFilename()))); + // onlyFileName() is needed for cygwin + tmp += quoteName(onlyFileName(removeExtension( + it->aux_file.absFileName()))); Systemcall one; - one.startscript(Systemcall::Wait, tmp); + one.startscript(Systemcall::Wait, tmp, path); } // Return whether bibtex was run return result; @@ -593,12 +616,19 @@ int LaTeX::scanLogFile(TeXErrors & terr) int line_count = 1; int retval = NO_ERRORS; string tmp = - onlyFilename(changeExtension(file.absFilename(), ".log")); + onlyFileName(changeExtension(file.absFileName(), ".log")); LYXERR(Debug::LATEX, "Log file: " << tmp); FileName const fn = FileName(makeAbsPath(tmp)); 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).*"); + // 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 > child; string token; while (getline(ifs, token)) { @@ -615,6 +645,29 @@ int LaTeX::scanLogFile(TeXErrors & terr) if (token.empty()) continue; + // Track child documents + for (size_t i = 0; i < token.length(); ++i) { + if (token[i] == '(') { + ++pnest; + size_t j = token.find('(', i + 1); + 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 name = sub.str(1); + child.push(make_pair(name, pnest)); + i += len; + } + } else if (token[i] == ')') { + if (!child.empty() + && child.top().second == pnest) + child.pop(); + --pnest; + } + } + child_name = child.empty() ? empty_string() : child.top().first; + if (contains(token, "file:line:error style messages enabled")) fle_style = true; @@ -633,6 +686,10 @@ int LaTeX::scanLogFile(TeXErrors & terr) && contains(token, "undefined")) { retval |= ERROR_RERUN; LYXERR(Debug::LATEX, "Force rerun."); + // package etaremune + } else if (contains(token, "Etaremune labels have changed")) { + retval |= ERROR_RERUN; + LYXERR(Debug::LATEX, "Force rerun."); } else if (contains(token, "Citation") && contains(token, "on page") && contains(token, "undefined")) { @@ -650,7 +707,11 @@ int LaTeX::scanLogFile(TeXErrors & terr) } } else if (contains(token, "run BibTeX")) { retval |= UNDEF_CIT; + } else if (contains(token, "run Biber")) { + retval |= UNDEF_CIT; + biber = true; } else if (contains(token, "Rerun LaTeX") || + contains(token, "Please rerun LaTeX") || contains(token, "Rerun to get")) { // at least longtable.sty and bibtopic.sty // might use this. @@ -685,12 +746,52 @@ int LaTeX::scanLogFile(TeXErrors & terr) desc = sub.str(); if (contains(token, "LaTeX Error:")) retval |= LATEX_ERROR; + + 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; + LYXERR(Debug::LATEX, "Force rerun."); + } else { + // bug 6445. 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; + LYXERR(Debug::LATEX, "Force rerun."); + } + + if (!wait_for_error.empty() && prefixIs(token, "! Emergency stop.")){ + retval |= LATEX_ERROR; + string errstr; + int count = 0; + errstr = wait_for_error; + do { + if (!getline(ifs, tmp)) + break; + tmp = rtrim(tmp, "\r"); + errstr += "\n" + tmp; + if (++count > 5) + break; + } while (!contains(tmp, "(job aborted")); + + terr.insertError(0, + from_local8bit("Emergency stop"), + from_local8bit(errstr), + child_name); + } + // get the next line string tmp; int count = 0; do { if (!getline(ifs, tmp)) break; + tmp = rtrim(tmp, "\r"); if (++count > 10) break; } while (!prefixIs(tmp, "l.")); @@ -709,6 +810,7 @@ int LaTeX::scanLogFile(TeXErrors & terr) string errstr(tmp, tmp.find(' ')); errstr += '\n'; getline(ifs, tmp); + tmp = rtrim(tmp, "\r"); while (!contains(errstr, "l.") && !tmp.empty() && !prefixIs(tmp, "! ") @@ -716,6 +818,7 @@ int LaTeX::scanLogFile(TeXErrors & terr) errstr += tmp; errstr += "\n"; getline(ifs, tmp); + tmp = rtrim(tmp, "\r"); } LYXERR(Debug::LATEX, "line: " << line << '\n' << "Desc: " << desc << '\n' << "Text: " << errstr); @@ -736,7 +839,8 @@ int LaTeX::scanLogFile(TeXErrors & terr) // assume here it can be wrong. terr.insertError(line, from_local8bit(desc), - from_local8bit(errstr)); + from_local8bit(errstr), + child_name); ++num_errors; } } @@ -756,6 +860,14 @@ int LaTeX::scanLogFile(TeXErrors & terr) } else if (contains(token, "That makes 100 errors")) { // More than 100 errors were reprted 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); } } } @@ -820,7 +932,7 @@ bool handleFoundFile(string const & ff, DepTable & head) } } - string onlyfile = onlyFilename(foundfile); + string onlyfile = onlyFileName(foundfile); absname = makeAbsPath(onlyfile); // check for spaces @@ -840,7 +952,7 @@ bool handleFoundFile(string const & ff, DepTable & head) string const stripoff = rsplit(foundfile, strippedfile, ' '); foundfile = strippedfile; - onlyfile = onlyFilename(strippedfile); + onlyfile = onlyFileName(strippedfile); absname = makeAbsPath(onlyfile); } } @@ -891,7 +1003,7 @@ void LaTeX::deplog(DepTable & head) // entered into the dependency file. string const logfile = - onlyFilename(changeExtension(file.absFilename(), ".log")); + onlyFileName(changeExtension(file.absFileName(), ".log")); static regex const reg1("File: (.+).*"); static regex const reg2("No file (.+)(.).*"); @@ -919,7 +1031,7 @@ void LaTeX::deplog(DepTable & head) string lastline; while (ifs) { // Ok, the scanning of files here is not sufficient. - // Sometimes files are named by "File: xxx" only + // Sometimes files are named by "File:� xxx" only // So I think we should use some regexps to find files instead. // Note: all file names and paths might contains spaces. bool found_file = false; @@ -936,6 +1048,10 @@ void LaTeX::deplog(DepTable & head) continue; } + // FIXME UNICODE: We assume that the file names in the log + // file are in the file system encoding. + token = to_utf8(from_filesystem8bit(token)); + // Sometimes, filenames are broken across lines. // We care for that and save suspicious lines. // Here we exclude some cases where we are sure @@ -965,10 +1081,6 @@ void LaTeX::deplog(DepTable & head) smatch sub; - // FIXME UNICODE: We assume that the file names in the log - // file are in the file system encoding. - token = to_utf8(from_filesystem8bit(token)); - // (1) "File: file.ext" if (regex_match(token, sub, reg1)) { // check for dot @@ -976,7 +1088,7 @@ void LaTeX::deplog(DepTable & head) // However, ... if (suffixIs(token, ")")) // no line break for sure - // pretend we've been succesfully searching + // pretend we've been successfully searching found_file = true; // (2) "No file file.ext" } else if (regex_match(token, sub, reg2)) { @@ -1013,11 +1125,11 @@ void LaTeX::deplog(DepTable & head) found_file = checkLineBreak(sub.str(1), head); // (7) "\tf@toc=\write" (for MikTeX) else if (regex_match(token, sub, miktexTocReg)) - found_file = handleFoundFile(onlyFilename(changeExtension( - file.absFilename(), ".toc")), head); + found_file = handleFoundFile(onlyFileName(changeExtension( + file.absFileName(), ".toc")), head); else // not found, but we won't check further - // pretend we've been succesfully searching + // pretend we've been successfully searching found_file = true; // (8) "(file.ext" @@ -1056,7 +1168,7 @@ void LaTeX::deplog(DepTable & head) // we have a closing bracket, so the content // is not a file name. // no need to investigate further - // pretend we've been succesfully searching + // pretend we've been successfully searching first = what[0].second; found_file = true; } @@ -1077,4 +1189,30 @@ void LaTeX::deplog(DepTable & head) } +void LaTeX::scanBlgFile(DepTable & dep) +{ + FileName const blg_file(changeExtension(file.absFileName(), "blg")); + LYXERR(Debug::LATEX, "Scanning blg file: " << blg_file); + + ifstream ifs(blg_file.toFilesystemEncoding().c_str()); + string token; + static regex const reg1(".*Found bibtex data file '([^']+).*"); + + while (getline(ifs, token)) { + token = rtrim(token, "\r"); + smatch sub; + // FIXME UNICODE: We assume that citation keys and filenames + // in the aux file are in the file system encoding. + token = to_utf8(from_filesystem8bit(token)); + if (regex_match(token, sub, reg1)) { + string data = sub.str(1); + if (!data.empty()) { + LYXERR(Debug::LATEX, "Found bib file: " << data); + handleFoundFile(data, dep); + } + } + } +} + + } // namespace lyx