#include "LyXRC.h"
#include "LyX.h"
#include "DepTable.h"
+#include "Encoding.h"
#include "support/debug.h"
#include "support/convert.h"
}
+void TeXErrors::insertRef(int line, docstring const & error_desc,
+ docstring const & error_text,
+ string const & child_name)
+{
+ Error newerr(line, error_desc, error_text, child_name);
+ undef_ref.push_back(newerr);
+}
+
+
bool operator==(AuxInfo const & a, AuxInfo const & o)
{
return a.aux_file == o.aux_file
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)
+ allow_cancel(allow_cancellation)
{
num_errors = 0;
// lualatex can still produce a DVI with --output-format=dvi. However,
FileName const gls(changeExtension(file.absFileName(), ".gls"));
gls.removeFile();
+ // endnotes file
+ FileName const ent(changeExtension(file.absFileName(), ".ent"));
+ ent.removeFile();
+
// Also remove the aux file
FileName const aux(changeExtension(file.absFileName(), ".aux"));
aux.removeFile();
{
int scanres = NO_ERRORS;
int bscanres = NO_ERRORS;
+ int iscanres = NO_ERRORS;
unsigned int count = 0; // number of times run
num_errors = 0; // just to make sure.
unsigned int const MAX_RUN = 6;
runMakeIndex(onlyFileName(idxfile.absFileName()), runparams);
if (ret == Systemcall::KILLED)
return Systemcall::KILLED;
+ FileName const ilgfile(changeExtension(file.absFileName(), ".ilg"));
+ if (ilgfile.exists())
+ iscanres = scanIlgFile(terr);
rerun = true;
}
file.absFileName(), ".idx")), runparams);
if (ret == Systemcall::KILLED)
return Systemcall::KILLED;
- rerun = true;
+ FileName const ilgfile(changeExtension(file.absFileName(), ".ilg"));
+ if (ilgfile.exists())
+ iscanres = scanIlgFile(terr);
+ rerun = true;
}
// MSVC complains that bool |= int is unsafe. Not sure why.
if (bscanres & ERRORS)
return bscanres; // return on error
+ if (iscanres & ERRORS)
+ return iscanres; // return on error
+
return scanres;
}
}
-int 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;
+
+ if (contains(tmp, "$$x")) {
+ // This adds appropriate [te]xindy options
+ // such as language and codepage (for the
+ // main document language/encoding) as well
+ // as input markup (latex or xelatex)
+ string xdyopts = rp.xindy_language;
+ if (!xdyopts.empty())
+ xdyopts = "-L " + xdyopts;
+ if (rp.isFullUnicode() && rp.encoding->package() == Encoding::none) {
+ if (!xdyopts.empty())
+ xdyopts += " ";
+ // xelatex includes lualatex
+ xdyopts += "-I xelatex";
+ }
+ else if (rp.encoding->iconvName() == "UTF-8") {
+ if (!xdyopts.empty())
+ xdyopts += " ";
+ // -I not really needed for texindy, but for xindy
+ xdyopts += "-C utf8 -I latex";
+ }
+ else {
+ if (!xdyopts.empty())
+ xdyopts += " ";
+ // not really needed for texindy, but for xindy
+ xdyopts += "-I latex";
+ }
+ tmp = subst(tmp, "$$x", xdyopts);
+ }
+
+ if (contains(tmp, "$$b")) {
+ // advise xindy to write a log file
+ tmp = subst(tmp, "$$b", removeExtension(f));
+ }
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);
}
-int 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;
Systemcall::Starttype const starttype =
allow_cancel ? Systemcall::WaitLoop : Systemcall::Wait;
vector<AuxInfo> const
-LaTeX::scanAuxFiles(FileName const & file, bool const only_childbibs)
+LaTeX::scanAuxFiles(FileName const & fname, bool const only_childbibs)
{
vector<AuxInfo> result;
if (only_childbibs) {
for (string const &s: children) {
FileName fn =
- makeAbsPath(s, file.onlyPath().realPath());
+ makeAbsPath(s, fname.onlyPath().realPath());
fn.changeExtension("aux");
if (fn.exists())
result.push_back(scanAuxFile(fn));
return result;
}
- result.push_back(scanAuxFile(file));
+ result.push_back(scanAuxFile(fname));
// This is for bibtopic
- string const basename = removeExtension(file.absFileName());
+ string const basename = removeExtension(fname.absFileName());
for (int i = 1; i < 1000; ++i) {
FileName const file2(basename
+ '.' + convert<string>(i)
}
-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\\{([^}]+)\\}");
bool LaTeX::runBibTeX(vector<AuxInfo> const & bibtex_info,
- OutputParams const & runparams, int & exit_code)
+ OutputParams const & rp, int & exit_code)
{
bool result = false;
exit_code = 0;
continue;
result = true;
- string tmp = runparams.bibtex_command;
+ string tmp = rp.bibtex_command;
tmp += " ";
// onlyFileName() is needed for cygwin
tmp += quoteName(onlyFileName(removeExtension(
}
+//helper func for scanLogFile; gets line number X from strings "... on input line X ..."
+//returns 0 if none is found
+int getLineNumber(const string &token){
+ string l = support::token(token, ' ', tokenPos(token,' ',"line") + 1);
+ return l.empty() ? 0 : convert<int>(l);
+}
+
+
int LaTeX::scanLogFile(TeXErrors & terr)
{
int last_line = -1;
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;
stack <pair<string, int> > child;
children.clear();
+ terr.clearRefs();
+
string token;
while (getline(ifs, token)) {
// MikTeX sometimes inserts \0 in the log file. They can't be
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);
+ // 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] == ')') {
if (contains(token, "file:line:error style messages enabled"))
fle_style = true;
+ //Handles both "LaTeX Warning:" & "Package natbib Warning:"
+ //Various handlers for missing citations below won't catch the problem if citation
+ //key is long (>~25chars), because pdflatex splits output at line length 80.
+ //TODO: TL 2020 engines will contain new commandline switch --cnf-line which we
+ //can use to set max_print_line variable for appropriate length and detect all
+ //errors correctly.
+ if (contains(token, "There were undefined citations.") ||
+ prefixIs(token, "Package biblatex Warning: The following entry could not be found"))
+ retval |= UNDEF_CIT;
+
if (prefixIs(token, "LaTeX Warning:") ||
prefixIs(token, "! pdfTeX warning")) {
// Here shall we handle different
} else if (contains(token, "Etaremune labels have changed")) {
retval |= ERROR_RERUN;
LYXERR(Debug::LATEX, "Force rerun.");
+ // package enotez
+ } else if (contains(token, "Endnotes may have changed. Rerun")) {
+ retval |= RERUN;
+ LYXERR(Debug::LATEX, "We should rerun.");
+ //"Citation `cit' on page X undefined on input line X."
} else if (contains(token, "Citation")
- && contains(token, "on page")
+ //&& contains(token, "on input line") //often split to newline
&& contains(token, "undefined")) {
retval |= UNDEF_CIT;
- } else if (contains(token, "Citation")
- && contains(token, "on input line")
+ terr.insertRef(getLineNumber(token), from_ascii("Citation undefined"),
+ from_utf8(token), child_name);
+ //"Reference `X' on page Y undefined on input line Z."
+ } else if (contains(token, "Reference")
+ //&& contains(token, "on input line")) //often split to new line
&& contains(token, "undefined")) {
- retval |= UNDEF_CIT;
+ retval |= UNDEF_REF;
+ terr.insertRef(getLineNumber(token), from_ascii("Reference undefined"),
+ from_utf8(token), child_name);
+
+ //If label is too long pdlaftex log line splitting will make the above fail
+ //so we catch at least this generic statement occuring for both CIT & REF.
+ } else if (contains(token, "There were undefined references.")) {
+ if (!(retval & UNDEF_CIT)) //if not handled already
+ retval |= UNDEF_REF;
}
+
} else if (prefixIs(token, "Package")) {
// Package warnings
retval |= PACKAGE_WARNING;
&& contains(token, "on page")
&& contains(token, "undefined")) {
retval |= UNDEF_CIT;
+ //Unf only keys up to ~6 chars will make it due to line splits
+ terr.insertRef(getLineNumber(token), from_ascii("Citation undefined"),
+ from_utf8(token), child_name);
}
} else if (contains(token, "run BibTeX")) {
retval |= UNDEF_CIT;
}
// get the next line
- string tmp;
int count = 0;
do {
if (!getline(ifs, tmp))
}
-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();
// 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
static regex const reg1("File: (.+).*");
static regex const reg2("No file (.+)(.).*");
- static regex const reg3("\\\\openout[0-9]+.*=.*`(.+)(..).*");
+ static regex const reg3a("\\\\openout[0-9]+.*=.*`(.+)(..).*");
+ // LuaTeX has a slightly different output
+ static regex const reg3b("\\\\openout[0-9]+.*=\\s*(.+)");
// If an index should be created, MikTex does not write a line like
// \openout# = 'sample.idx'.
// but instead only a line like this into the log:
// Writing index file sample.idx
static regex const reg4("Writing index file (.+).*");
static regex const regoldnomencl("Writing glossary file (.+).*");
- static regex const regnomencl("Writing nomenclature file (.+).*");
+ static regex const regnomencl(".*Writing nomenclature file (.+).*");
// If a toc should be created, MikTex does not write a line like
// \openout# = `sample.toc'.
// but only a line like this into the log:
else
// we suspect a line break
fragment = true;
- // (3) "\openout<nr> = `file.ext'."
- } else if (regex_match(token, sub, reg3)) {
+ // (3)(a) "\openout<nr> = `file.ext'."
+ } else if (regex_match(token, sub, reg3a)) {
// search for closing '. at the end of the line
if (sub.str(2) == "\'.")
fragment = !handleFoundFile(sub.str(1), head);
else
// potential fragment
fragment = true;
+ // (3)(b) "\openout<nr> = file.ext" (LuaTeX)
+ } else if (regex_match(token, sub, reg3b)) {
+ // file names must contains a dot
+ if (contains(sub.str(1), '.'))
+ fragment = !handleFoundFile(sub.str(1), head);
+ else
+ // potential fragment
+ fragment = true;
// (4) "Writing index file file.ext"
} else if (regex_match(token, sub, reg4))
// fragmential file name?
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);
}
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);
}
|| 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;
}
}
+int LaTeX::scanIlgFile(TeXErrors & terr)
+{
+ FileName const ilg_file(changeExtension(file.absFileName(), "ilg"));
+ LYXERR(Debug::LATEX, "Scanning ilg file: " << ilg_file);
+
+ ifstream ifs(ilg_file.toFilesystemEncoding().c_str());
+ string token;
+ int retval = NO_ERRORS;
+
+ string prevtoken;
+ while (getline(ifs, token)) {
+ token = rtrim(token, "\r");
+ smatch sub;
+ if (prefixIs(token, "!! "))
+ prevtoken = token;
+ else if (!prevtoken.empty()) {
+ retval |= INDEX_ERROR;
+ string errstr = N_("Makeindex error: ") + prevtoken;
+ string msg = prevtoken + '\n';
+ msg += token;
+ terr.insertError(0,
+ from_local8bit(errstr),
+ from_local8bit(msg));
+ prevtoken.clear();
+ } else if (prefixIs(token, "ERROR: ")) {
+ retval |= BIBTEX_ERROR;
+ string errstr = N_("Xindy error: ") + token.substr(6);
+ string msg = token;
+ terr.insertError(0,
+ from_local8bit(errstr),
+ from_local8bit(msg));
+ }
+ }
+ return retval;
+}
+
+
+
} // namespace lyx