Warning, file /office/kile/src/parser/latexoutputparser.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /********************************************************************************** 0002 * Copyright (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net) * 0003 * 2011 by Michel Ludwig (michel.ludwig@kdemail.net) * 0004 ***********************************************************************************/ 0005 0006 /*************************************************************************** 0007 * * 0008 * This program is free software; you can redistribute it and/or modify * 0009 * it under the terms of the GNU General Public License as published by * 0010 * the Free Software Foundation; either version 2 of the License, or * 0011 * (at your option) any later version. * 0012 * * 0013 ***************************************************************************/ 0014 0015 #include "latexoutputparser.h" 0016 0017 #include <QDir> 0018 #include <QFileInfo> 0019 #include <QRegularExpression> 0020 0021 #include <KLocalizedString> 0022 0023 #include "kiletool_enums.h" 0024 #include "parserthread.h" 0025 #include "widgets/logwidget.h" 0026 0027 namespace KileParser { 0028 0029 LaTeXOutputParserInput::LaTeXOutputParserInput(const QUrl &url, KileDocument::Extensions *extensions, 0030 const QString& sourceFile, 0031 const QString &texfilename, 0032 int selrow, 0033 int docrow) 0034 : ParserInput(url), 0035 extensions(extensions), 0036 sourceFile(sourceFile), 0037 texfilename(texfilename), 0038 selrow(selrow), 0039 docrow(docrow) 0040 { 0041 } 0042 0043 LaTeXOutputParserOutput::LaTeXOutputParserOutput() 0044 { 0045 } 0046 0047 LaTeXOutputParserOutput::~LaTeXOutputParserOutput() 0048 { 0049 qCDebug(LOG_KILE_PARSER); 0050 } 0051 0052 LaTeXOutputParser::LaTeXOutputParser(ParserThread *parserThread, LaTeXOutputParserInput *input, QObject *parent) 0053 : Parser(parserThread, parent), 0054 m_extensions(input->extensions), 0055 m_infoList(Q_NULLPTR), 0056 m_logFile(input->url.toLocalFile()), 0057 texfilename(input->texfilename), 0058 selrow(input->selrow), 0059 docrow(input->docrow) 0060 { 0061 m_nErrors = 0; 0062 m_nWarnings = 0; 0063 m_nBadBoxes = 0; 0064 setSource(input->sourceFile); 0065 } 0066 0067 LaTeXOutputParser::~LaTeXOutputParser() 0068 { 0069 qCDebug(LOG_KILE_PARSER); 0070 } 0071 0072 bool LaTeXOutputParser::fileExists(const QString & name) 0073 { 0074 static QFileInfo fi; 0075 0076 if (QDir::isAbsolutePath(name)) { 0077 fi.setFile(name); 0078 if(fi.exists() && !fi.isDir()) { 0079 return true; 0080 } 0081 else { 0082 return false; 0083 } 0084 } 0085 0086 fi.setFile(path() + '/' + name); 0087 if(fi.exists() && !fi.isDir()) { 0088 return true; 0089 } 0090 0091 fi.setFile(path() + '/' + name + m_extensions->latexDocumentDefault()); 0092 if(fi.exists() && !fi.isDir()) { 0093 return true; 0094 } 0095 0096 // try to determine the LaTeX source file 0097 QStringList extlist = m_extensions->latexDocuments().split(' '); 0098 for(QStringList::Iterator it = extlist.begin(); it!=extlist.end(); ++it) { 0099 fi.setFile(path() + '/' + name + (*it)); 0100 if(fi.exists() && !fi.isDir()) { 0101 return true; 0102 } 0103 } 0104 0105 return false; 0106 } 0107 0108 // There are basically two ways to detect the current file TeX is processing: 0109 // 1) Use \Input (i.c.w. srctex.sty or srcltx.sty) and \include exclusively. This will 0110 // cause (La)TeX to print the line ":<+ filename" in the log file when opening a file, 0111 // ":<-" when closing a file. Filenames pushed on the stack in this mode are marked 0112 // as reliable. 0113 // 0114 // 2) Since people will probably also use the \input command, we also have to be 0115 // to detect the old-fashioned way. TeX prints '(filename' when opening a file and a ')' 0116 // when closing one. It is impossible to detect this with 100% certainty (TeX prints many messages 0117 // and even text (a context) from the TeX source file, there could be unbalanced parentheses), 0118 // so we use a heuristic algorithm. In heuristic mode a ')' will only be considered as a signal that 0119 // TeX is closing a file if the top of the stack is not marked as "reliable". 0120 // Also, when scanning for a TeX error linenumber (which sometimes causes a context to be printed 0121 // to the log-file), updateFileStack is not called, helping not to pick up unbalanced parentheses 0122 // from the context. 0123 void LaTeXOutputParser::updateFileStack(const QString &strLine, short& dwCookie) 0124 { 0125 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::updateFileStack()================" << Qt::endl; 0126 static QString strPartialFileName; 0127 0128 switch (dwCookie) { 0129 //we're looking for a filename 0130 case Start : 0131 case FileNameHeuristic : 0132 //TeX is opening a file 0133 if(strLine.startsWith(":<+ ")) { 0134 // qCDebug(LOG_KILE_PARSER) << "filename detected" << Qt::endl; 0135 //grab the filename, it might be a partial name (i.e. continued on the next line) 0136 strPartialFileName = strLine.mid(4).trimmed(); 0137 0138 //change the cookie so we remember we aren't sure the filename is complete 0139 dwCookie = FileName; 0140 } 0141 //TeX closed a file 0142 else if(strLine.contains(":<-")) { 0143 // qCDebug(LOG_KILE_PARSER) << "\tpopping : " << m_stackFile.top().file() << Qt::endl; 0144 if(!m_stackFile.isEmpty()) { 0145 m_stackFile.pop(); 0146 } 0147 dwCookie = Start; 0148 } 0149 else { 0150 //fallback to the heuristic detection of filenames 0151 updateFileStackHeuristic(strLine, dwCookie); 0152 } 0153 break; 0154 0155 case FileName : 0156 //The partial filename was followed by '(', this means that TeX is signalling it is 0157 //opening the file. We are sure the filename is complete now. Don't call updateFileStackHeuristic 0158 //since we don't want the filename on the stack twice. 0159 if(strLine.startsWith('(') || strLine.startsWith(QLatin1String("\\openout"))) { 0160 //push the filename on the stack and mark it as 'reliable' 0161 m_stackFile.push(LOFStackItem(strPartialFileName, true)); 0162 // qCDebug(LOG_KILE_PARSER) << "\tpushed : " << strPartialFileName << Qt::endl; 0163 strPartialFileName.clear(); 0164 dwCookie = Start; 0165 } 0166 //The partial filename was followed by an TeX error, meaning the file doesn't exist. 0167 //Don't push it on the stack, instead try to detect the error. 0168 else if(strLine.startsWith('!')) { 0169 // qCDebug(LOG_KILE_PARSER) << "oops!" << Qt::endl; 0170 dwCookie = Start; 0171 strPartialFileName.clear(); 0172 detectError(strLine, dwCookie); 0173 } 0174 else if(strLine.startsWith(QLatin1String("No file"))) { 0175 // qCDebug(LOG_KILE_PARSER) << "No file: " << strLine << Qt::endl; 0176 dwCookie = Start; 0177 strPartialFileName.clear(); 0178 detectWarning(strLine, dwCookie); 0179 } 0180 //Partial filename still isn't complete. 0181 else { 0182 // qCDebug(LOG_KILE_PARSER) << "\tpartial file name, adding" << Qt::endl; 0183 strPartialFileName = strPartialFileName + strLine.trimmed(); 0184 } 0185 break; 0186 0187 default: 0188 break; 0189 } 0190 } 0191 0192 void LaTeXOutputParser::updateFileStackHeuristic(const QString &strLine, short & dwCookie) 0193 { 0194 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::updateFileStackHeuristic()================"; 0195 //qCDebug(LOG_KILE_PARSER) << strLine << dwCookie; 0196 static QString strPartialFileName; 0197 bool expectFileName = (dwCookie == FileNameHeuristic); 0198 int index = 0; 0199 0200 // handle special case (bug fix for 101810) 0201 if(expectFileName && strLine.length() > 0 && strLine[0] == ')') { 0202 m_stackFile.push(LOFStackItem(strPartialFileName)); 0203 expectFileName = false; 0204 dwCookie = Start; 0205 } 0206 0207 //scan for parentheses and grab filenames 0208 for(int i = 0; i < strLine.length(); ++i) { 0209 /* 0210 We're expecting a filename. If a filename really ends at this position one of the following must be true: 0211 1) Next character is a space (indicating the end of a filename (yes, there can't be spaces in the 0212 path, this is a TeX limitation). 0213 comment by tbraun: there is a workaround \include{{"file name"}} according to https://groups.google.com/forum/#!topic/comp.text.tex/r4c1NPBkTk8, 0214 but this is currently not supported by kile. 0215 2) We're at the end of the line, the filename is probably continued on the next line. 0216 3) The TeX was closed already, signaled by the ')'. 0217 */ 0218 0219 bool isLastChar = (i+1 == strLine.length()); 0220 bool nextIsTerminator = isLastChar ? false : (strLine[i+1].isSpace() || strLine[i+1] == ')'); 0221 0222 if(expectFileName && (isLastChar || nextIsTerminator)) { 0223 qCDebug(LOG_KILE_PARSER) << "Update the partial filename " << strPartialFileName; 0224 strPartialFileName = strPartialFileName + strLine.mid(index, i-index + 1); 0225 0226 if(strPartialFileName.isEmpty()) { // nothing left to do here 0227 continue; 0228 } 0229 0230 //FIXME: improve these heuristics 0231 if((isLastChar && (i < 78)) || nextIsTerminator || fileExists(strPartialFileName)) { 0232 m_stackFile.push(LOFStackItem(strPartialFileName)); 0233 //qCDebug(LOG_KILE_PARSER) << "\tpushed (i = " << i << " length = " << strLine.length() << "): " << strPartialFileName; 0234 expectFileName = false; 0235 dwCookie = Start; 0236 } 0237 //Guess the filename is continued on the next line, only if the current strPartialFileName does not exist, see bug # 162899 0238 else if(isLastChar) { 0239 if(fileExists(strPartialFileName)) { 0240 m_stackFile.push(LOFStackItem(strPartialFileName)); 0241 qCDebug(LOG_KILE_PARSER) << "pushed (i = " << i << " length = " << strLine.length() << "): " << strPartialFileName << Qt::endl; 0242 expectFileName = false; 0243 dwCookie = Start; 0244 } 0245 else { 0246 qCDebug(LOG_KILE_PARSER) << "Filename spans more than one line." << Qt::endl; 0247 dwCookie = FileNameHeuristic; 0248 } 0249 } 0250 //bail out 0251 else { 0252 dwCookie = Start; 0253 strPartialFileName.clear(); 0254 expectFileName = false; 0255 } 0256 } 0257 //TeX is opening a file 0258 else if(strLine[i] == '(') { 0259 //we need to extract the filename 0260 expectFileName = true; 0261 strPartialFileName.clear(); 0262 dwCookie = Start; 0263 0264 //this is were the filename is supposed to start 0265 index = i + 1; 0266 } 0267 //TeX is closing a file 0268 else if(strLine[i] == ')') { 0269 //qCDebug(LOG_KILE_PARSER) << "\tpopping : " << m_stackFile.top().file(); 0270 //If this filename was pushed on the stack by the reliable ":<+-" method, don't pop 0271 //a ":<-" will follow. This helps in preventing unbalanced ')' from popping filenames 0272 //from the stack too soon. 0273 if(m_stackFile.count() > 1 && !m_stackFile.top().reliable()) { 0274 m_stackFile.pop(); 0275 } 0276 else { 0277 qCDebug(LOG_KILE_PARSER) << "\t\toh no, forget about it!"; 0278 } 0279 } 0280 } 0281 } 0282 0283 0284 void LaTeXOutputParser::flushCurrentItem() 0285 { 0286 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::flushCurrentItem()================" << Qt::endl; 0287 int nItemType = m_currentItem.type(); 0288 0289 while( m_stackFile.count() > 0 && !fileExists(m_stackFile.top().file()) ) { 0290 m_stackFile.pop(); 0291 } 0292 0293 QString sourceFile = (m_stackFile.isEmpty()) ? QFileInfo(source()).fileName() : m_stackFile.top().file(); 0294 m_currentItem.setSource(sourceFile); 0295 m_currentItem.setMainSourceFile(source()); 0296 0297 switch (nItemType) { 0298 case itmError: 0299 ++m_nErrors; 0300 m_infoList->push_back(m_currentItem); 0301 //qCDebug(LOG_KILE_PARSER) << "Flushing Error in" << m_currentItem.source() << "@" << m_currentItem.sourceLine() << " reported in line " << m_currentItem.outputLine() << Qt::endl; 0302 break; 0303 0304 case itmWarning: 0305 ++m_nWarnings; 0306 m_infoList->push_back(m_currentItem); 0307 //qCDebug(LOG_KILE_PARSER) << "Flushing Warning in " << m_currentItem.source() << "@" << m_currentItem.sourceLine() << " reported in line " << m_currentItem.outputLine() << Qt::endl; 0308 break; 0309 0310 case itmBadBox: 0311 ++m_nBadBoxes; 0312 m_infoList->push_back(m_currentItem); 0313 //qCDebug(LOG_KILE_PARSER) << "Flushing BadBox in " << m_currentItem.source() << "@" << m_currentItem.sourceLine() << " reported in line " << m_currentItem.outputLine() << Qt::endl; 0314 break; 0315 0316 default: 0317 break; 0318 } 0319 m_currentItem.clear(); 0320 } 0321 0322 bool LaTeXOutputParser::detectError(const QString & strLine, short &dwCookie) 0323 { 0324 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::detectError(" << strLine.length() << ")================" << Qt::endl; 0325 0326 bool found = false, flush = false; 0327 0328 static QRegularExpression reLaTeXError("^! LaTeX Error: (.*)$"); 0329 reLaTeXError.setPatternOptions(QRegularExpression::CaseInsensitiveOption); 0330 static QRegularExpression rePDFLaTeXError("^Error: pdflatex (.*)$"); 0331 rePDFLaTeXError.setPatternOptions(QRegularExpression::CaseInsensitiveOption); 0332 static QRegularExpression reTeXError("^! (.*)\\.$"); 0333 static QRegularExpression reLineNumber("^l\\.([0-9]+)(.*)"); 0334 0335 switch (dwCookie) { 0336 case Start: { 0337 QRegularExpressionMatch latexErrorMatch = reLaTeXError.match(strLine); 0338 if(latexErrorMatch.hasMatch()) { 0339 //qCDebug(LOG_KILE_PARSER) << "\tError : " << reLaTeXError.cap(1) << Qt::endl; 0340 m_currentItem.setMessage(latexErrorMatch.captured(1)); 0341 found = true; 0342 } 0343 else { 0344 QRegularExpressionMatch pdflatexErrorMatch = rePDFLaTeXError.match(strLine); 0345 if(pdflatexErrorMatch.hasMatch()) { 0346 //qCDebug(LOG_KILE_PARSER) << "\tError : " << rePDFLaTeXError.cap(1) << Qt::endl; 0347 m_currentItem.setMessage(pdflatexErrorMatch.captured(1)); 0348 found = true; 0349 } 0350 else { 0351 QRegularExpressionMatch texErrorMatch = reTeXError.match(strLine); 0352 if(texErrorMatch.hasMatch()) { 0353 //qCDebug(LOG_KILE_PARSER) << "\tError : " << reTeXError.cap(1) << Qt::endl; 0354 m_currentItem.setMessage(texErrorMatch.captured(1)); 0355 found = true; 0356 } 0357 } 0358 } 0359 0360 if(found) { 0361 dwCookie = strLine.endsWith('.') ? LineNumber : Error; 0362 m_currentItem.setOutputLine(GetCurrentOutputLine()); 0363 } 0364 break; 0365 } 0366 case Error : 0367 //qCDebug(LOG_KILE_PARSER) << "\tError (cont'd): " << strLine << Qt::endl; 0368 if(strLine.endsWith('.')) { 0369 dwCookie = LineNumber; 0370 m_currentItem.setMessage(m_currentItem.message() + strLine); 0371 } 0372 else if(GetCurrentOutputLine() - m_currentItem.outputLine() > 3) { 0373 qWarning() << "\tBAILING OUT: error description spans more than three lines" << Qt::endl; 0374 dwCookie = Start; 0375 flush = true; 0376 } 0377 break; 0378 0379 case LineNumber: { 0380 //qCDebug(LOG_KILE_PARSER) << "\tLineNumber " << Qt::endl; 0381 QRegularExpressionMatch lineNumberMatch = reLineNumber.match(strLine); 0382 if(lineNumberMatch.hasMatch()) { 0383 dwCookie = Start; 0384 flush = true; 0385 //qCDebug(LOG_KILE_PARSER) << "\tline number: " << reLineNumber.cap(1) << Qt::endl; 0386 m_currentItem.setSourceLine(lineNumberMatch.captured(1).toInt()); 0387 m_currentItem.setMessage(m_currentItem.message() + lineNumberMatch.captured(2)); 0388 } 0389 else if(GetCurrentOutputLine() - m_currentItem.outputLine() > 10) { 0390 dwCookie = Start; 0391 flush = true; 0392 qWarning() << "\tBAILING OUT: did not detect a TeX line number for an error" << Qt::endl; 0393 m_currentItem.setSourceLine(0); 0394 } 0395 break; 0396 } 0397 0398 default : 0399 break; 0400 } 0401 0402 if(found) { 0403 m_currentItem.setType(itmError); 0404 m_currentItem.setOutputLine(GetCurrentOutputLine()); 0405 } 0406 0407 if(flush) { 0408 flushCurrentItem(); 0409 } 0410 0411 return found; 0412 } 0413 0414 bool LaTeXOutputParser::detectWarning(const QString & strLine, short &dwCookie) 0415 { 0416 //qCDebug(LOG_KILE_PARSER) << strLine << strLine.length(); 0417 0418 bool found = false, flush = false; 0419 QString warning; 0420 0421 static QRegularExpression reLaTeXWarning("^(((! )?(La|pdf)TeX)|Package|Class) .*Warning.*:(.*)"); 0422 reLaTeXWarning.setPatternOptions(QRegularExpression::CaseInsensitiveOption); 0423 static QRegularExpression reNoFile("No file (.*)"); 0424 static QRegularExpression reNoAsyFile("File .* does not exist."); // FIXME can be removed when https://sourceforge.net/p/asymptote/bugs/70/ has promoted to the users 0425 0426 switch(dwCookie) { 0427 //detect the beginning of a warning 0428 case Start: { 0429 QRegularExpressionMatch laTeXWarningMatch = reLaTeXWarning.match(strLine); 0430 if(laTeXWarningMatch.hasMatch()) { 0431 warning = laTeXWarningMatch.captured(5); 0432 //qCDebug(LOG_KILE_PARSER) << "\tWarning found: " << warning << Qt::endl; 0433 0434 found = true; 0435 dwCookie = Start; 0436 0437 m_currentItem.setMessage(warning); 0438 m_currentItem.setOutputLine(GetCurrentOutputLine()); 0439 0440 //do we expect a line number? 0441 flush = detectLaTeXLineNumber(warning, dwCookie, strLine.length()); 0442 break; 0443 } 0444 0445 QRegularExpressionMatch noFileMatch = reNoFile.match(strLine); 0446 if(noFileMatch.hasMatch()) { 0447 found = true; 0448 flush = true; 0449 m_currentItem.setSourceLine(0); 0450 m_currentItem.setMessage(noFileMatch.captured(0)); 0451 m_currentItem.setOutputLine(GetCurrentOutputLine()); 0452 break; 0453 } 0454 0455 QRegularExpressionMatch noAsyFileMatch = reNoAsyFile.match(strLine); 0456 if(noAsyFileMatch.hasMatch()) { 0457 found = true; 0458 flush = true; 0459 m_currentItem.setSourceLine(0); 0460 m_currentItem.setMessage(noAsyFileMatch.captured(0)); 0461 m_currentItem.setOutputLine(GetCurrentOutputLine()); 0462 } 0463 0464 break; 0465 } 0466 0467 //warning spans multiple lines, detect the end 0468 case Warning : 0469 warning = m_currentItem.message() + strLine; 0470 //qCDebug(LOG_KILE_PARSER) << "'\tWarning (cont'd) : " << warning << Qt::endl; 0471 flush = detectLaTeXLineNumber(warning, dwCookie, strLine.length()); 0472 m_currentItem.setMessage(warning); 0473 break; 0474 0475 default: 0476 break; 0477 } 0478 0479 if(found) { 0480 m_currentItem.setType(itmWarning); 0481 m_currentItem.setOutputLine(GetCurrentOutputLine()); 0482 } 0483 0484 if(flush) { 0485 flushCurrentItem(); 0486 } 0487 0488 return found; 0489 } 0490 0491 bool LaTeXOutputParser::detectLaTeXLineNumber(QString & warning, short & dwCookie, int len) 0492 { 0493 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::detectLaTeXLineNumber(" << warning.length() << ")================" << Qt::endl; 0494 0495 static QRegularExpression reLaTeXLineNumber("(.*) on input line ([0-9]+)\\.$"); 0496 reLaTeXLineNumber.setPatternOptions(QRegularExpression::CaseInsensitiveOption); 0497 static QRegularExpression reInternationalLaTeXLineNumber("(.*)([0-9]+)\\.$"); 0498 reInternationalLaTeXLineNumber.setPatternOptions(QRegularExpression::CaseInsensitiveOption); 0499 0500 QRegularExpressionMatch laTeXLineNumberMatch = reLaTeXLineNumber.match(warning); 0501 0502 if(laTeXLineNumberMatch.hasMatch()) { 0503 //qCDebug(LOG_KILE_PARSER) << "een" << Qt::endl; 0504 m_currentItem.setSourceLine(laTeXLineNumberMatch.captured(2).toInt()); 0505 warning += laTeXLineNumberMatch.captured(1); 0506 dwCookie = Start; 0507 return true; 0508 } 0509 0510 QRegularExpressionMatch internaltionalLaTeXLineNumberMatch = reInternationalLaTeXLineNumber.match(warning); 0511 0512 if(internaltionalLaTeXLineNumberMatch.hasMatch()) { 0513 m_currentItem.setSourceLine(internaltionalLaTeXLineNumberMatch.captured(2).toInt()); 0514 warning += internaltionalLaTeXLineNumberMatch.captured(1); 0515 dwCookie = Start; 0516 return true; 0517 } 0518 0519 if(warning.endsWith('.')) { 0520 //qCDebug(LOG_KILE_PARSER) << "twee" << Qt::endl; 0521 m_currentItem.setSourceLine(0); 0522 dwCookie = Start; 0523 return true; 0524 } 0525 0526 //bailing out, did not find a line number 0527 if((GetCurrentOutputLine() - m_currentItem.outputLine() > 4) || (len == 0)) { 0528 //qCDebug(LOG_KILE_PARSER) << "drie current " << GetCurrentOutputLine() << " " << m_currentItem.outputLine() << " len " << len << Qt::endl; 0529 m_currentItem.setSourceLine(0); 0530 dwCookie = Start; 0531 return true; 0532 } 0533 0534 //error message is continued on the other line 0535 dwCookie = Warning; 0536 return false; 0537 } 0538 0539 bool LaTeXOutputParser::detectBadBox(const QString & strLine, short & dwCookie) 0540 { 0541 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::detectBadBox(" << strLine.length() << ")================" << Qt::endl; 0542 0543 bool found = false, flush = false; 0544 QString badbox; 0545 0546 static QRegularExpression reBadBox("^(Over|Under)(full \\\\[hv]box .*)"); 0547 reBadBox.setPatternOptions(QRegularExpression::CaseInsensitiveOption); 0548 0549 switch(dwCookie) { 0550 case Start: { 0551 const QRegularExpressionMatch match = reBadBox.match(strLine); 0552 if(match.hasMatch()) { 0553 found = true; 0554 dwCookie = Start; 0555 badbox = strLine; 0556 flush = detectBadBoxLineNumber(badbox, dwCookie, strLine.length()); 0557 m_currentItem.setMessage(badbox); 0558 } 0559 break; 0560 } 0561 0562 case BadBox : 0563 badbox = m_currentItem.message() + strLine; 0564 flush = detectBadBoxLineNumber(badbox, dwCookie, strLine.length()); 0565 m_currentItem.setMessage(badbox); 0566 break; 0567 0568 default: 0569 break; 0570 } 0571 0572 if(found) { 0573 m_currentItem.setType(itmBadBox); 0574 m_currentItem.setOutputLine(GetCurrentOutputLine()); 0575 } 0576 0577 if(flush) { 0578 flushCurrentItem(); 0579 } 0580 0581 return found; 0582 } 0583 0584 bool LaTeXOutputParser::detectBadBoxLineNumber(QString & strLine, short & dwCookie, int len) 0585 { 0586 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::detectBadBoxLineNumber(" << strLine.length() << ")================" << Qt::endl; 0587 0588 static QRegularExpression reBadBoxLines("(.*) at lines ([0-9]+)--([0-9]+)"); 0589 reBadBoxLines.setPatternOptions(QRegularExpression::CaseInsensitiveOption); 0590 static QRegularExpression reBadBoxLine("(.*) at line ([0-9]+)"); 0591 reBadBoxLine.setPatternOptions(QRegularExpression::CaseInsensitiveOption); 0592 //Use the following only, if you know how to get the source line for it. 0593 // This is not simple, as TeX is not reporting it. 0594 static QRegularExpression reBadBoxOutput("(.*)has occurred while \\output is active^"); 0595 reBadBoxOutput.setPatternOptions(QRegularExpression::CaseInsensitiveOption); 0596 0597 QRegularExpressionMatch badBoxLinesMatch = reBadBoxLines.match(strLine); 0598 if(badBoxLinesMatch.hasMatch()) { 0599 dwCookie = Start; 0600 strLine = badBoxLinesMatch.captured(1); 0601 int n1 = badBoxLinesMatch.captured(2).toInt(); 0602 int n2 = badBoxLinesMatch.captured(3).toInt(); 0603 m_currentItem.setSourceLine(n1 < n2 ? n1 : n2); 0604 return true; 0605 } 0606 0607 QRegularExpressionMatch badBoxLineMatch = reBadBoxLine.match(strLine); 0608 if(badBoxLineMatch.hasMatch()) { 0609 dwCookie = Start; 0610 strLine = badBoxLineMatch.captured(1); 0611 m_currentItem.setSourceLine(badBoxLineMatch.captured(2).toInt()); 0612 //qCDebug(LOG_KILE_PARSER) << "\tBadBox@" << reBadBoxLine.cap(2) << "." << Qt::endl; 0613 return true; 0614 } 0615 0616 QRegularExpressionMatch badBoxOutputMatch = reBadBoxOutput.match(strLine); 0617 if(badBoxOutputMatch.hasMatch()) { 0618 dwCookie = Start; 0619 strLine = badBoxOutputMatch.captured(1); 0620 m_currentItem.setSourceLine(0); 0621 return true; 0622 } 0623 0624 //bailing out, did not find a line number 0625 if((GetCurrentOutputLine() - m_currentItem.outputLine() > 3) || (len == 0)) { 0626 dwCookie = Start; 0627 m_currentItem.setSourceLine(0); 0628 return true; 0629 } 0630 0631 dwCookie = BadBox; 0632 return false; 0633 } 0634 0635 short LaTeXOutputParser::parseLine(const QString & strLine, short dwCookie) 0636 { 0637 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::parseLine(" << strLine << dwCookie << strLine.length() << ")================" << Qt::endl; 0638 0639 switch (dwCookie) { 0640 case Start : 0641 if(!(detectBadBox(strLine, dwCookie) || detectWarning(strLine, dwCookie) || detectError(strLine, dwCookie))) { 0642 updateFileStack(strLine, dwCookie); 0643 } 0644 break; 0645 0646 case Warning : 0647 detectWarning(strLine, dwCookie); 0648 break; 0649 0650 case Error : 0651 case LineNumber : 0652 detectError(strLine, dwCookie); 0653 break; 0654 0655 case BadBox : 0656 detectBadBox(strLine, dwCookie); 0657 break; 0658 0659 case FileName : 0660 case FileNameHeuristic : 0661 updateFileStack(strLine, dwCookie); 0662 break; 0663 0664 default: 0665 dwCookie = Start; 0666 break; 0667 } 0668 0669 return dwCookie; 0670 } 0671 0672 ParserOutput* LaTeXOutputParser::parse() 0673 { 0674 LaTeXOutputParserOutput *parserOutput = new LaTeXOutputParserOutput(); 0675 0676 qCDebug(LOG_KILE_PARSER); 0677 0678 m_infoList = &(parserOutput->infoList); 0679 m_nErrors = m_nWarnings = m_nBadBoxes = m_nParens = 0; 0680 m_stackFile.clear(); 0681 m_stackFile.push(LOFStackItem(QFileInfo(source()).fileName(), true)); 0682 0683 short sCookie = 0; 0684 QFile f(m_logFile); 0685 0686 m_log.clear(); 0687 m_nOutputLines = 0; 0688 0689 if(!f.open(QIODevice::ReadOnly)) { 0690 parserOutput->problem = i18n("Cannot open log file; did you run LaTeX?"); 0691 return parserOutput; 0692 } 0693 m_infoList = &parserOutput->infoList; 0694 QTextStream t(&f); 0695 while(!t.atEnd()) { 0696 if(!m_parserThread->shouldContinueDocumentParsing()) { 0697 qCDebug(LOG_KILE_PARSER) << "stopping..."; 0698 delete(parserOutput); 0699 f.close(); 0700 return Q_NULLPTR; 0701 } 0702 QString s = t.readLine(); 0703 sCookie = parseLine(s.trimmed(), sCookie); 0704 ++m_nOutputLines; 0705 0706 m_log += s + '\n'; 0707 } 0708 f.close(); 0709 0710 parserOutput->nWarnings = m_nWarnings; 0711 parserOutput->nErrors = m_nErrors; 0712 parserOutput->nBadBoxes = m_nBadBoxes; 0713 parserOutput->logFile = m_logFile; 0714 0715 // for quick preview 0716 if(!texfilename.isEmpty() && selrow >= 0 && docrow >= 0) { 0717 updateInfoLists(texfilename, selrow, docrow); 0718 } 0719 0720 return parserOutput; 0721 } 0722 0723 void LaTeXOutputParser::updateInfoLists(const QString &texfilename, int selrow, int docrow) 0724 { 0725 // get a short name for the original tex file 0726 QString filename = "./" + QFileInfo(texfilename).fileName(); 0727 // setSource(texfilename); 0728 0729 //print detailed error info 0730 for(int i = 0; i < m_infoList->count(); ++i) { 0731 // perhaps correct filename and line number in OutputInfo 0732 OutputInfo &info = (*m_infoList)[i]; 0733 info.setSource(filename); 0734 0735 int linenumber = selrow + info.sourceLine() - docrow; 0736 if(linenumber < 0) { 0737 linenumber = 0; 0738 } 0739 info.setSourceLine(linenumber); 0740 } 0741 } 0742 0743 0744 0745 /** Return number of errors etc. found in log-file. */ 0746 void LaTeXOutputParser::getErrorCount(int *errors, int *warnings, int *badboxes) 0747 { 0748 *errors = m_nErrors; 0749 *warnings = m_nWarnings; 0750 *badboxes = m_nBadBoxes; 0751 } 0752 0753 int LaTeXOutputParser::GetCurrentOutputLine() const 0754 { 0755 return m_nOutputLines; 0756 } 0757 0758 void LaTeXOutputParser::setSource(const QString &src) 0759 { 0760 m_source = src; 0761 m_srcPath = QFileInfo(src).path(); 0762 } 0763 0764 }