1 /**********************************************************************************
2 * Copyright (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net) *
3 * 2011 by Michel Ludwig (michel.ludwig@kdemail.net) *
4 ***********************************************************************************/
5
6 /***************************************************************************
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 ***************************************************************************/
14
15 #include "latexoutputparser.h"
16
17 #include <QDir>
18 #include <QFileInfo>
19
20 #include <KLocalizedString>
21
22 #include "kiletool_enums.h"
23 #include "parserthread.h"
24 #include "widgets/logwidget.h"
25
26 namespace KileParser {
27
LaTeXOutputParserInput(const QUrl & url,KileDocument::Extensions * extensions,const QString & sourceFile,const QString & texfilename,int selrow,int docrow)28 LaTeXOutputParserInput::LaTeXOutputParserInput(const QUrl &url, KileDocument::Extensions *extensions,
29 const QString& sourceFile,
30 const QString &texfilename,
31 int selrow,
32 int docrow)
33 : ParserInput(url),
34 extensions(extensions),
35 sourceFile(sourceFile),
36 texfilename(texfilename),
37 selrow(selrow),
38 docrow(docrow)
39 {
40 }
41
LaTeXOutputParserOutput()42 LaTeXOutputParserOutput::LaTeXOutputParserOutput()
43 {
44 }
45
~LaTeXOutputParserOutput()46 LaTeXOutputParserOutput::~LaTeXOutputParserOutput()
47 {
48 qCDebug(LOG_KILE_PARSER);
49 }
50
LaTeXOutputParser(ParserThread * parserThread,LaTeXOutputParserInput * input,QObject * parent)51 LaTeXOutputParser::LaTeXOutputParser(ParserThread *parserThread, LaTeXOutputParserInput *input, QObject *parent)
52 : Parser(parserThread, parent),
53 m_extensions(input->extensions),
54 m_infoList(Q_NULLPTR),
55 m_logFile(input->url.toLocalFile()),
56 texfilename(input->texfilename),
57 selrow(input->selrow),
58 docrow(input->docrow)
59 {
60 m_nErrors = 0;
61 m_nWarnings = 0;
62 m_nBadBoxes = 0;
63 setSource(input->sourceFile);
64 }
65
~LaTeXOutputParser()66 LaTeXOutputParser::~LaTeXOutputParser()
67 {
68 qCDebug(LOG_KILE_PARSER);
69 }
70
fileExists(const QString & name)71 bool LaTeXOutputParser::fileExists(const QString & name)
72 {
73 static QFileInfo fi;
74
75 if (QDir::isAbsolutePath(name)) {
76 fi.setFile(name);
77 if(fi.exists() && !fi.isDir()) {
78 return true;
79 }
80 else {
81 return false;
82 }
83 }
84
85 fi.setFile(path() + '/' + name);
86 if(fi.exists() && !fi.isDir()) {
87 return true;
88 }
89
90 fi.setFile(path() + '/' + name + m_extensions->latexDocumentDefault());
91 if(fi.exists() && !fi.isDir()) {
92 return true;
93 }
94
95 // try to determine the LaTeX source file
96 QStringList extlist = m_extensions->latexDocuments().split(' ');
97 for(QStringList::Iterator it = extlist.begin(); it!=extlist.end(); ++it) {
98 fi.setFile(path() + '/' + name + (*it));
99 if(fi.exists() && !fi.isDir()) {
100 return true;
101 }
102 }
103
104 return false;
105 }
106
107 // There are basically two ways to detect the current file TeX is processing:
108 // 1) Use \Input (i.c.w. srctex.sty or srcltx.sty) and \include exclusively. This will
109 // cause (La)TeX to print the line ":<+ filename" in the log file when opening a file,
110 // ":<-" when closing a file. Filenames pushed on the stack in this mode are marked
111 // as reliable.
112 //
113 // 2) Since people will probably also use the \input command, we also have to be
114 // to detect the old-fashioned way. TeX prints '(filename' when opening a file and a ')'
115 // when closing one. It is impossible to detect this with 100% certainty (TeX prints many messages
116 // and even text (a context) from the TeX source file, there could be unbalanced parentheses),
117 // so we use a heuristic algorithm. In heuristic mode a ')' will only be considered as a signal that
118 // TeX is closing a file if the top of the stack is not marked as "reliable".
119 // Also, when scanning for a TeX error linenumber (which sometimes causes a context to be printed
120 // to the log-file), updateFileStack is not called, helping not to pick up unbalanced parentheses
121 // from the context.
updateFileStack(const QString & strLine,short & dwCookie)122 void LaTeXOutputParser::updateFileStack(const QString &strLine, short& dwCookie)
123 {
124 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::updateFileStack()================" << endl;
125 static QString strPartialFileName;
126
127 switch (dwCookie) {
128 //we're looking for a filename
129 case Start :
130 case FileNameHeuristic :
131 //TeX is opening a file
132 if(strLine.startsWith(":<+ ")) {
133 // qCDebug(LOG_KILE_PARSER) << "filename detected" << endl;
134 //grab the filename, it might be a partial name (i.e. continued on the next line)
135 strPartialFileName = strLine.mid(4).trimmed();
136
137 //change the cookie so we remember we aren't sure the filename is complete
138 dwCookie = FileName;
139 }
140 //TeX closed a file
141 else if(strLine.contains(":<-")) {
142 // qCDebug(LOG_KILE_PARSER) << "\tpopping : " << m_stackFile.top().file() << endl;
143 if(!m_stackFile.isEmpty()) {
144 m_stackFile.pop();
145 }
146 dwCookie = Start;
147 }
148 else {
149 //fallback to the heuristic detection of filenames
150 updateFileStackHeuristic(strLine, dwCookie);
151 }
152 break;
153
154 case FileName :
155 //The partial filename was followed by '(', this means that TeX is signalling it is
156 //opening the file. We are sure the filename is complete now. Don't call updateFileStackHeuristic
157 //since we don't want the filename on the stack twice.
158 if(strLine.startsWith('(') || strLine.startsWith(QLatin1String("\\openout"))) {
159 //push the filename on the stack and mark it as 'reliable'
160 m_stackFile.push(LOFStackItem(strPartialFileName, true));
161 // qCDebug(LOG_KILE_PARSER) << "\tpushed : " << strPartialFileName << endl;
162 strPartialFileName.clear();
163 dwCookie = Start;
164 }
165 //The partial filename was followed by an TeX error, meaning the file doesn't exist.
166 //Don't push it on the stack, instead try to detect the error.
167 else if(strLine.startsWith('!')) {
168 // qCDebug(LOG_KILE_PARSER) << "oops!" << endl;
169 dwCookie = Start;
170 strPartialFileName.clear();
171 detectError(strLine, dwCookie);
172 }
173 else if(strLine.startsWith(QLatin1String("No file"))) {
174 // qCDebug(LOG_KILE_PARSER) << "No file: " << strLine << endl;
175 dwCookie = Start;
176 strPartialFileName.clear();
177 detectWarning(strLine, dwCookie);
178 }
179 //Partial filename still isn't complete.
180 else {
181 // qCDebug(LOG_KILE_PARSER) << "\tpartial file name, adding" << endl;
182 strPartialFileName = strPartialFileName + strLine.trimmed();
183 }
184 break;
185
186 default:
187 break;
188 }
189 }
190
updateFileStackHeuristic(const QString & strLine,short & dwCookie)191 void LaTeXOutputParser::updateFileStackHeuristic(const QString &strLine, short & dwCookie)
192 {
193 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::updateFileStackHeuristic()================";
194 //qCDebug(LOG_KILE_PARSER) << strLine << dwCookie;
195 static QString strPartialFileName;
196 bool expectFileName = (dwCookie == FileNameHeuristic);
197 int index = 0;
198
199 // handle special case (bug fix for 101810)
200 if(expectFileName && strLine.length() > 0 && strLine[0] == ')') {
201 m_stackFile.push(LOFStackItem(strPartialFileName));
202 expectFileName = false;
203 dwCookie = Start;
204 }
205
206 //scan for parentheses and grab filenames
207 for(int i = 0; i < strLine.length(); ++i) {
208 /*
209 We're expecting a filename. If a filename really ends at this position one of the following must be true:
210 1) Next character is a space (indicating the end of a filename (yes, there can't be spaces in the
211 path, this is a TeX limitation).
212 comment by tbraun: there is a workround \include{{"file name"}} according to http://groups.google.com/group/comp.text.tex/browse_thread/thread/af873534f0644e4f/cd7e0cdb61a8b837?lnk=st&q=include+space+tex#cd7e0cdb61a8b837,
213 but this is currently not supported by kile.
214 2) We're at the end of the line, the filename is probably continued on the next line.
215 3) The TeX was closed already, signalled by the ')'.
216 */
217
218 bool isLastChar = (i+1 == strLine.length());
219 bool nextIsTerminator = isLastChar ? false : (strLine[i+1].isSpace() || strLine[i+1] == ')');
220
221 if(expectFileName && (isLastChar || nextIsTerminator)) {
222 qCDebug(LOG_KILE_PARSER) << "Update the partial filename " << strPartialFileName;
223 strPartialFileName = strPartialFileName + strLine.mid(index, i-index + 1);
224
225 if(strPartialFileName.isEmpty()) { // nothing left to do here
226 continue;
227 }
228
229 //FIXME: improve these heuristics
230 if((isLastChar && (i < 78)) || nextIsTerminator || fileExists(strPartialFileName)) {
231 m_stackFile.push(LOFStackItem(strPartialFileName));
232 //qCDebug(LOG_KILE_PARSER) << "\tpushed (i = " << i << " length = " << strLine.length() << "): " << strPartialFileName;
233 expectFileName = false;
234 dwCookie = Start;
235 }
236 //Guess the filename is continued on the next line, only if the current strPartialFileName does not exist, see bug # 162899
237 else if(isLastChar) {
238 if(fileExists(strPartialFileName)) {
239 m_stackFile.push(LOFStackItem(strPartialFileName));
240 qCDebug(LOG_KILE_PARSER) << "pushed (i = " << i << " length = " << strLine.length() << "): " << strPartialFileName << endl;
241 expectFileName = false;
242 dwCookie = Start;
243 }
244 else {
245 qCDebug(LOG_KILE_PARSER) << "Filename spans more than one line." << endl;
246 dwCookie = FileNameHeuristic;
247 }
248 }
249 //bail out
250 else {
251 dwCookie = Start;
252 strPartialFileName.clear();
253 expectFileName = false;
254 }
255 }
256 //TeX is opening a file
257 else if(strLine[i] == '(') {
258 //we need to extract the filename
259 expectFileName = true;
260 strPartialFileName.clear();
261 dwCookie = Start;
262
263 //this is were the filename is supposed to start
264 index = i + 1;
265 }
266 //TeX is closing a file
267 else if(strLine[i] == ')') {
268 //qCDebug(LOG_KILE_PARSER) << "\tpopping : " << m_stackFile.top().file();
269 //If this filename was pushed on the stack by the reliable ":<+-" method, don't pop
270 //a ":<-" will follow. This helps in preventing unbalanced ')' from popping filenames
271 //from the stack too soon.
272 if(m_stackFile.count() > 1 && !m_stackFile.top().reliable()) {
273 m_stackFile.pop();
274 }
275 else {
276 qCDebug(LOG_KILE_PARSER) << "\t\toh no, forget about it!";
277 }
278 }
279 }
280 }
281
282
flushCurrentItem()283 void LaTeXOutputParser::flushCurrentItem()
284 {
285 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::flushCurrentItem()================" << endl;
286 int nItemType = m_currentItem.type();
287
288 while( m_stackFile.count() > 0 && !fileExists(m_stackFile.top().file()) ) {
289 m_stackFile.pop();
290 }
291
292 QString sourceFile = (m_stackFile.isEmpty()) ? QFileInfo(source()).fileName() : m_stackFile.top().file();
293 m_currentItem.setSource(sourceFile);
294 m_currentItem.setMainSourceFile(source());
295
296 switch (nItemType) {
297 case itmError:
298 ++m_nErrors;
299 m_infoList->push_back(m_currentItem);
300 //qCDebug(LOG_KILE_PARSER) << "Flushing Error in" << m_currentItem.source() << "@" << m_currentItem.sourceLine() << " reported in line " << m_currentItem.outputLine() << endl;
301 break;
302
303 case itmWarning:
304 ++m_nWarnings;
305 m_infoList->push_back(m_currentItem);
306 //qCDebug(LOG_KILE_PARSER) << "Flushing Warning in " << m_currentItem.source() << "@" << m_currentItem.sourceLine() << " reported in line " << m_currentItem.outputLine() << endl;
307 break;
308
309 case itmBadBox:
310 ++m_nBadBoxes;
311 m_infoList->push_back(m_currentItem);
312 //qCDebug(LOG_KILE_PARSER) << "Flushing BadBox in " << m_currentItem.source() << "@" << m_currentItem.sourceLine() << " reported in line " << m_currentItem.outputLine() << endl;
313 break;
314
315 default:
316 break;
317 }
318 m_currentItem.clear();
319 }
320
detectError(const QString & strLine,short & dwCookie)321 bool LaTeXOutputParser::detectError(const QString & strLine, short &dwCookie)
322 {
323 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::detectError(" << strLine.length() << ")================" << endl;
324
325 bool found = false, flush = false;
326
327 static QRegExp reLaTeXError("^! LaTeX Error: (.*)$", Qt::CaseInsensitive);
328 static QRegExp rePDFLaTeXError("^Error: pdflatex (.*)$", Qt::CaseInsensitive);
329 static QRegExp reTeXError("^! (.*)\\.$");
330 static QRegExp reLineNumber("^l\\.([0-9]+)(.*)");
331
332 switch (dwCookie) {
333 case Start :
334 if(reLaTeXError.indexIn(strLine) != -1) {
335 //qCDebug(LOG_KILE_PARSER) << "\tError : " << reLaTeXError.cap(1) << endl;
336 m_currentItem.setMessage(reLaTeXError.cap(1));
337 found = true;
338 }
339 else if(rePDFLaTeXError.indexIn(strLine) != -1) {
340 //qCDebug(LOG_KILE_PARSER) << "\tError : " << rePDFLaTeXError.cap(1) << endl;
341 m_currentItem.setMessage(rePDFLaTeXError.cap(1));
342 found = true;
343 }
344 else if(reTeXError.indexIn(strLine) != -1) {
345 //qCDebug(LOG_KILE_PARSER) << "\tError : " << reTeXError.cap(1) << endl;
346 m_currentItem.setMessage(reTeXError.cap(1));
347 found = true;
348 }
349 if(found) {
350 dwCookie = strLine.endsWith('.') ? LineNumber : Error;
351 m_currentItem.setOutputLine(GetCurrentOutputLine());
352 }
353 break;
354
355 case Error :
356 //qCDebug(LOG_KILE_PARSER) << "\tError (cont'd): " << strLine << endl;
357 if(strLine.endsWith('.')) {
358 dwCookie = LineNumber;
359 m_currentItem.setMessage(m_currentItem.message() + strLine);
360 }
361 else if(GetCurrentOutputLine() - m_currentItem.outputLine() > 3) {
362 qWarning() << "\tBAILING OUT: error description spans more than three lines" << endl;
363 dwCookie = Start;
364 flush = true;
365 }
366 break;
367
368 case LineNumber :
369 //qCDebug(LOG_KILE_PARSER) << "\tLineNumber " << endl;
370 if(reLineNumber.indexIn(strLine) != -1) {
371 dwCookie = Start;
372 flush = true;
373 //qCDebug(LOG_KILE_PARSER) << "\tline number: " << reLineNumber.cap(1) << endl;
374 m_currentItem.setSourceLine(reLineNumber.cap(1).toInt());
375 m_currentItem.setMessage(m_currentItem.message() + reLineNumber.cap(2));
376 }
377 else if(GetCurrentOutputLine() - m_currentItem.outputLine() > 10) {
378 dwCookie = Start;
379 flush = true;
380 qWarning() << "\tBAILING OUT: did not detect a TeX line number for an error" << endl;
381 m_currentItem.setSourceLine(0);
382 }
383 break;
384
385 default :
386 break;
387 }
388
389 if(found) {
390 m_currentItem.setType(itmError);
391 m_currentItem.setOutputLine(GetCurrentOutputLine());
392 }
393
394 if(flush) {
395 flushCurrentItem();
396 }
397
398 return found;
399 }
400
detectWarning(const QString & strLine,short & dwCookie)401 bool LaTeXOutputParser::detectWarning(const QString & strLine, short &dwCookie)
402 {
403 //qCDebug(LOG_KILE_PARSER) << strLine << strLine.length();
404
405 bool found = false, flush = false;
406 QString warning;
407
408 static QRegExp reLaTeXWarning("^(((! )?(La|pdf)TeX)|Package|Class) .*Warning.*:(.*)", Qt::CaseInsensitive);
409 static QRegExp reNoFile("No file (.*)");
410 static QRegExp reNoAsyFile("File .* does not exist."); // FIXME can be removed when http://sourceforge.net/tracker/index.php?func=detail&aid=1772022&group_id=120000&atid=685683 has promoted to the users
411
412 switch(dwCookie) {
413 //detect the beginning of a warning
414 case Start :
415 if(reLaTeXWarning.indexIn(strLine) != -1) {
416 warning = reLaTeXWarning.cap(5);
417 //qCDebug(LOG_KILE_PARSER) << "\tWarning found: " << warning << endl;
418
419 found = true;
420 dwCookie = Start;
421
422 m_currentItem.setMessage(warning);
423 m_currentItem.setOutputLine(GetCurrentOutputLine());
424
425 //do we expect a line number?
426 flush = detectLaTeXLineNumber(warning, dwCookie, strLine.length());
427 }
428 else if(reNoFile.indexIn(strLine) != -1) {
429 found = true;
430 flush = true;
431 m_currentItem.setSourceLine(0);
432 m_currentItem.setMessage(reNoFile.cap(0));
433 m_currentItem.setOutputLine(GetCurrentOutputLine());
434 }
435 else if(reNoAsyFile.indexIn(strLine) != -1) {
436 found = true;
437 flush = true;
438 m_currentItem.setSourceLine(0);
439 m_currentItem.setMessage(reNoAsyFile.cap(0));
440 m_currentItem.setOutputLine(GetCurrentOutputLine());
441 }
442
443 break;
444
445 //warning spans multiple lines, detect the end
446 case Warning :
447 warning = m_currentItem.message() + strLine;
448 //qCDebug(LOG_KILE_PARSER) << "'\tWarning (cont'd) : " << warning << endl;
449 flush = detectLaTeXLineNumber(warning, dwCookie, strLine.length());
450 m_currentItem.setMessage(warning);
451 break;
452
453 default:
454 break;
455 }
456
457 if(found) {
458 m_currentItem.setType(itmWarning);
459 m_currentItem.setOutputLine(GetCurrentOutputLine());
460 }
461
462 if(flush) {
463 flushCurrentItem();
464 }
465
466 return found;
467 }
468
detectLaTeXLineNumber(QString & warning,short & dwCookie,int len)469 bool LaTeXOutputParser::detectLaTeXLineNumber(QString & warning, short & dwCookie, int len)
470 {
471 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::detectLaTeXLineNumber(" << warning.length() << ")================" << endl;
472
473 static QRegExp reLaTeXLineNumber("(.*) on input line ([0-9]+)\\.$", Qt::CaseInsensitive);
474 static QRegExp reInternationalLaTeXLineNumber("(.*)([0-9]+)\\.$", Qt::CaseInsensitive);
475 if((reLaTeXLineNumber.indexIn(warning) != -1) || (reInternationalLaTeXLineNumber.indexIn(warning) != -1)) {
476 //qCDebug(LOG_KILE_PARSER) << "een" << endl;
477 m_currentItem.setSourceLine(reLaTeXLineNumber.cap(2).toInt());
478 warning += reLaTeXLineNumber.cap(1);
479 dwCookie = Start;
480 return true;
481 }
482 else if(warning.endsWith('.')) {
483 //qCDebug(LOG_KILE_PARSER) << "twee" << endl;
484 m_currentItem.setSourceLine(0);
485 dwCookie = Start;
486 return true;
487 }
488 //bailing out, did not find a line number
489 else if((GetCurrentOutputLine() - m_currentItem.outputLine() > 4) || (len == 0)) {
490 //qCDebug(LOG_KILE_PARSER) << "drie current " << GetCurrentOutputLine() << " " << m_currentItem.outputLine() << " len " << len << endl;
491 m_currentItem.setSourceLine(0);
492 dwCookie = Start;
493 return true;
494 }
495 //error message is continued on the other line
496 else {
497 //qCDebug(LOG_KILE_PARSER) << "vier" << endl;
498 dwCookie = Warning;
499 return false;
500 }
501 }
502
detectBadBox(const QString & strLine,short & dwCookie)503 bool LaTeXOutputParser::detectBadBox(const QString & strLine, short & dwCookie)
504 {
505 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::detectBadBox(" << strLine.length() << ")================" << endl;
506
507 bool found = false, flush = false;
508 QString badbox;
509
510 static QRegExp reBadBox("^(Over|Under)(full \\\\[hv]box .*)", Qt::CaseInsensitive);
511
512 switch(dwCookie) {
513 case Start :
514 if(reBadBox.indexIn(strLine) != -1) {
515 found = true;
516 dwCookie = Start;
517 badbox = strLine;
518 flush = detectBadBoxLineNumber(badbox, dwCookie, strLine.length());
519 m_currentItem.setMessage(badbox);
520 }
521 break;
522
523 case BadBox :
524 badbox = m_currentItem.message() + strLine;
525 flush = detectBadBoxLineNumber(badbox, dwCookie, strLine.length());
526 m_currentItem.setMessage(badbox);
527 break;
528
529 default:
530 break;
531 }
532
533 if(found) {
534 m_currentItem.setType(itmBadBox);
535 m_currentItem.setOutputLine(GetCurrentOutputLine());
536 }
537
538 if(flush) {
539 flushCurrentItem();
540 }
541
542 return found;
543 }
544
detectBadBoxLineNumber(QString & strLine,short & dwCookie,int len)545 bool LaTeXOutputParser::detectBadBoxLineNumber(QString & strLine, short & dwCookie, int len)
546 {
547 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::detectBadBoxLineNumber(" << strLine.length() << ")================" << endl;
548
549 static QRegExp reBadBoxLines("(.*) at lines ([0-9]+)--([0-9]+)", Qt::CaseInsensitive);
550 static QRegExp reBadBoxLine("(.*) at line ([0-9]+)", Qt::CaseInsensitive);
551 //Use the following only, if you know how to get the source line for it.
552 // This is not simple, as TeX is not reporting it.
553 static QRegExp reBadBoxOutput("(.*)has occurred while \\output is active^", Qt::CaseInsensitive);
554
555 if(reBadBoxLines.indexIn(strLine) != -1) {
556 dwCookie = Start;
557 strLine = reBadBoxLines.cap(1);
558 int n1 = reBadBoxLines.cap(2).toInt();
559 int n2 = reBadBoxLines.cap(3).toInt();
560 m_currentItem.setSourceLine(n1 < n2 ? n1 : n2);
561 return true;
562 }
563 else if(reBadBoxLine.indexIn(strLine) != -1) {
564 dwCookie = Start;
565 strLine = reBadBoxLine.cap(1);
566 m_currentItem.setSourceLine(reBadBoxLine.cap(2).toInt());
567 //qCDebug(LOG_KILE_PARSER) << "\tBadBox@" << reBadBoxLine.cap(2) << "." << endl;
568 return true;
569 }
570 else if(reBadBoxOutput.indexIn(strLine) != -1) {
571 dwCookie = Start;
572 strLine = reBadBoxLines.cap(1);
573 m_currentItem.setSourceLine(0);
574 return true;
575 }
576 //bailing out, did not find a line number
577 else if((GetCurrentOutputLine() - m_currentItem.outputLine() > 3) || (len == 0)) {
578 dwCookie = Start;
579 m_currentItem.setSourceLine(0);
580 return true;
581 }
582 else {
583 dwCookie = BadBox;
584 }
585
586 return false;
587 }
588
parseLine(const QString & strLine,short dwCookie)589 short LaTeXOutputParser::parseLine(const QString & strLine, short dwCookie)
590 {
591 //qCDebug(LOG_KILE_PARSER) << "==LaTeXOutputParser::parseLine(" << strLine << dwCookie << strLine.length() << ")================" << endl;
592
593 switch (dwCookie) {
594 case Start :
595 if(!(detectBadBox(strLine, dwCookie) || detectWarning(strLine, dwCookie) || detectError(strLine, dwCookie))) {
596 updateFileStack(strLine, dwCookie);
597 }
598 break;
599
600 case Warning :
601 detectWarning(strLine, dwCookie);
602 break;
603
604 case Error :
605 case LineNumber :
606 detectError(strLine, dwCookie);
607 break;
608
609 case BadBox :
610 detectBadBox(strLine, dwCookie);
611 break;
612
613 case FileName :
614 case FileNameHeuristic :
615 updateFileStack(strLine, dwCookie);
616 break;
617
618 default:
619 dwCookie = Start;
620 break;
621 }
622
623 return dwCookie;
624 }
625
parse()626 ParserOutput* LaTeXOutputParser::parse()
627 {
628 LaTeXOutputParserOutput *parserOutput = new LaTeXOutputParserOutput();
629
630 qCDebug(LOG_KILE_PARSER);
631
632 m_infoList = &(parserOutput->infoList);
633 m_nErrors = m_nWarnings = m_nBadBoxes = m_nParens = 0;
634 m_stackFile.clear();
635 m_stackFile.push(LOFStackItem(QFileInfo(source()).fileName(), true));
636
637 short sCookie = 0;
638 QString s;
639 QFile f(m_logFile);
640
641 m_log.clear();
642 m_nOutputLines = 0;
643
644 if(!f.open(QIODevice::ReadOnly)) {
645 parserOutput->problem = i18n("Cannot open log file; did you run LaTeX?");
646 return parserOutput;
647 }
648 m_infoList = &parserOutput->infoList;
649 QTextStream t(&f);
650 while(!t.atEnd()) {
651 if(!m_parserThread->shouldContinueDocumentParsing()) {
652 qCDebug(LOG_KILE_PARSER) << "stopping...";
653 delete(parserOutput);
654 f.close();
655 return Q_NULLPTR;
656 }
657 s = t.readLine();
658 sCookie = parseLine(s.trimmed(), sCookie);
659 ++m_nOutputLines;
660
661 m_log += s + '\n';
662 }
663 f.close();
664
665 parserOutput->nWarnings = m_nWarnings;
666 parserOutput->nErrors = m_nErrors;
667 parserOutput->nBadBoxes = m_nBadBoxes;
668 parserOutput->logFile = m_logFile;
669
670 // for quick preview
671 if(!texfilename.isEmpty() && selrow >= 0 && docrow >= 0) {
672 updateInfoLists(texfilename, selrow, docrow);
673 }
674
675 return parserOutput;
676 }
677
updateInfoLists(const QString & texfilename,int selrow,int docrow)678 void LaTeXOutputParser::updateInfoLists(const QString &texfilename, int selrow, int docrow)
679 {
680 // get a short name for the original tex file
681 QString filename = "./" + QFileInfo(texfilename).fileName();
682 // setSource(texfilename);
683
684 //print detailed error info
685 for(int i = 0; i < m_infoList->count(); ++i) {
686 // perhaps correct filename and line number in OutputInfo
687 OutputInfo &info = (*m_infoList)[i];
688 info.setSource(filename);
689
690 int linenumber = selrow + info.sourceLine() - docrow;
691 if(linenumber < 0) {
692 linenumber = 0;
693 }
694 info.setSourceLine(linenumber);
695 }
696 }
697
698
699
700 /** Return number of errors etc. found in log-file. */
getErrorCount(int * errors,int * warnings,int * badboxes)701 void LaTeXOutputParser::getErrorCount(int *errors, int *warnings, int *badboxes)
702 {
703 *errors = m_nErrors;
704 *warnings = m_nWarnings;
705 *badboxes = m_nBadBoxes;
706 }
707
GetCurrentOutputLine() const708 int LaTeXOutputParser::GetCurrentOutputLine() const
709 {
710 return m_nOutputLines;
711 }
712
setSource(const QString & src)713 void LaTeXOutputParser::setSource(const QString &src)
714 {
715 m_source = src;
716 m_srcPath = QFileInfo(src).path();
717 }
718
719 }
720