1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
5 * Copyright 1995 Matthias Ettrich
6 * Copyright 1995-1999 The LyX Team.
8 * This file is Copyright 1996-1999
11 * ======================================================
20 #pragma implementation
23 #include "support/filetools.h"
26 #include "support/FileInfo.h"
28 #include "support/lyxlib.h"
29 #include "support/syscall.h"
30 #include "support/syscontr.h"
31 #include "pathstack.h"
32 #include "bufferlist.h"
33 #include "minibuffer.h"
36 extern BufferList bufferlist;
38 struct texfile_struct {
39 LaTeX::TEX_FILES file;
40 char const *extension;
44 const texfile_struct all_files[] = {
45 { LaTeX::AUX, ".aux"},
46 { LaTeX::BBL, ".bbl"},
47 { LaTeX::DVI, ".dvi"},
48 { LaTeX::GLO, ".glo"},
49 { LaTeX::IDX, ".idx"},
50 { LaTeX::IND, ".ind"},
51 { LaTeX::LOF, ".lof"},
52 { LaTeX::LOA, ".loa"},
53 { LaTeX::LOG, ".log"},
54 { LaTeX::LOT, ".lot"},
55 { LaTeX::TOC, ".toc"},
56 { LaTeX::LTX, ".ltx"},
60 // This should perhaps be placed in LyXLex
62 string readLine(FILE *file)
73 } while (!feof(file) && s[i-1] != '\n' && i<510);
76 if (i == 1 && feof(file))
90 // I did not leave this inlined because DEC cxx does not like
91 // variables declarations in inlined code (JMarc)
92 TeXErrors::~TeXErrors()
96 tmp = errors->next_error;
103 void TeXErrors::scanError(LyXLex &lex)
105 string token = lex.GetString();
106 // Sometimes the error string goes over more than one
107 // line, and we need to get them all.
109 string tmp = frontStrip(readLine(lex.getFile()));
110 if (tmp == "\n" || tmp.empty()) {
111 tmp = frontStrip(readLine(lex.getFile()));
112 if (contains(tmp, "See the LaTeX manual")) {
114 tmp = readLine(lex.getFile());
115 } while (!tmp.empty() && !contains(tmp, "..."));
117 tmp = frontStrip(readLine(lex.getFile()));
120 while ((tmp != "\n" || !contains(errstr, "l."))
121 && !prefixIs(tmp, "! ")
122 && !contains(tmp, "(job aborted")
125 tmp = frontStrip(readLine(lex.getFile()));
127 lyxerr.debug("tmp: " + errstr);
129 // unfortunately the error line is not always given
130 // by "l.###" in the beginning of the error string
131 // therefore we must search for "l.###" in the error
133 if (contains(errstr, "l.")) {
134 // We make a const copy to make [] fast. (Asger)
135 string const es = errstr;
136 for (string::size_type i = 2; i < es.length(); ++i) {
137 if (es[i-2] == 'l' && es[i-1] == '.' &&
138 (es[i] >= '0' && es[i]<= '9')) {
139 line = atoi(es.c_str() + i);
144 insertError(line, token, errstr);
146 if (prefixIs(tmp, "! ")) {
152 bool TeXErrors::getFirstError(int *line, string *text)
156 *line = next_error->error_in_line;
157 *text = next_error->error_desc + "\n" + next_error->error_text;
158 next_error = next_error->next_error;
165 bool TeXErrors::getNextError(int *line, string *text)
168 *line = next_error->error_in_line;
169 *text = next_error->error_desc + "\n" + next_error->error_text;
170 next_error = next_error->next_error;
177 void TeXErrors::insertError(int line, string const &error_desc,
178 string const &error_text)
180 Error *newerr = new Error(line, error_desc, error_text);
182 Error *tmperr = errors;
183 while (tmperr->next_error) tmperr = tmperr->next_error;
184 tmperr->next_error = newerr;
191 void TeXErrors::printErrors()
193 lyxerr.print("Printing errors.");
195 Error *tmperr = errors;
197 lyxerr.print(string("Error in line ")
198 + tostr(tmperr->error_in_line)
199 + ": " + tmperr->error_desc
200 + '\n' + tmperr->error_text);
201 //%d: %s\n%s\n", tmperr->error_in_line,
202 // tmperr->error_desc.c_str(),
203 // tmperr->error_text.c_str());
204 tmperr = tmperr->next_error;
210 void TeXErrors::printWarnings()
215 void TeXErrors::printStatus()
217 lyxerr.print("Error struct:");
218 lyxerr.print(string(" status: ") + tostr(status));
219 lyxerr.print(string(" no err: ") + tostr(number_of_errors));
220 if (status == LaTeX::NO_ERRORS) lyxerr.print("NO_ERRORS");
221 if (status & LaTeX::NO_LOGFILE) lyxerr.print("NO_LOGFILE");
222 if (status & LaTeX::NO_OUTPUT) lyxerr.print("NO_OUTPUT");
223 if (status & LaTeX::UNDEF_REF) lyxerr. print("UNDEF_REF");
224 if (status & LaTeX::RERUN) lyxerr. print("RERUN");
225 if (status & LaTeX::TEX_ERROR) lyxerr.print("TEX_ERROR");
226 if (status & LaTeX::TEX_WARNING) lyxerr.print("TEX_WARNING");
227 if (status & LaTeX::NO_FILE) lyxerr.print("NO_FILE");
235 LaTeX::LaTeX(string const & latex, string const & f, string const & p)
236 : cmd(latex), file(f), path(p)
238 tex_files = NO_FILES;
239 file_count = sizeof(all_files) / sizeof(texfile_struct);
241 depfile = file + ".dep";
245 int LaTeX::run(TeXErrors &terr, MiniBuffer *minib)
246 // We know that this function will only be run if the lyx buffer
247 // has been changed. We also know that a newly written .tex file
248 // is always different from the previous one because of the date
249 // in it. However it seems safe to run latex (at least) on time each
250 // time the .tex file changes.
252 int scanres = LaTeX::NO_ERRORS;
253 unsigned int count = 0; // number of times run
254 num_errors = 0; // just to make sure.
255 const unsigned int MAX_RUN = 6;
256 DepTable head; // empty head
257 bool rerun = false; // rerun requested
259 // The class LaTeX does not know the temp path.
260 bufferlist.updateIncludedTeXfiles(GetCWD());
262 // Never write the depfile if an error was encountered.
265 // first check if the file dependencies exist:
266 // ->If it does exist
267 // check if any of the files mentioned in it have
268 // changed (done using a checksum).
270 // run latex once and
271 // remake the dependency file
272 // -> if not changed:
273 // just return there is nothing to do for us.
274 // ->if it doesn't exist
276 // run latex once (we need to run latex once anyway) and
277 // remake the dependency file.
279 FileInfo fi(depfile);
281 // Read the dep file:
283 // Update the checksums
286 lyxerr.debug("Dependency file exists", Error::LATEX);
287 if (head.sumchange()) {
288 lyxerr.debug("Dependency file has changed",
290 lyxerr.debug(string(_("Run #")) + tostr(++count),
292 minib->Set(string(_("LaTeX run number ")) + tostr(count));
295 scanres = scanLogFile(terr);
296 if (scanres & LaTeX::ERRORS) return scanres; // return on error
298 lyxerr.debug("return no_change", Error::LATEX);
299 return LaTeX::NO_CHANGE;
302 lyxerr.debug("Dependency file does not exist",
304 lyxerr.debug(string(_("Run #")) + tostr(++count),
306 head.insert(file, true);
307 minib->Set(string(_("LaTeX run number ")) + tostr(count));
310 scanres = scanLogFile(terr);
311 if (scanres & LaTeX::ERRORS) return scanres; // return on error
314 // update the dependencies.
315 deplog(head); // reads the latex log
316 deptex(head); // checks for latex files
320 // At this point we must run external programs if needed.
321 // makeindex will be run if a .idx file changed or was generated.
322 // And if there were undefined citations or changes in references
323 // the .aux file is checked for signs of bibtex. Bibtex is then run
327 if (head.haschanged(ChangeExtension(file, ".idx", true))) {
329 minib->Set(_("Running MakeIndex."));
331 rerun=runMakeIndex(ChangeExtension(file,".idx",true));
335 if (scanres & LaTeX::UNDEF_CIT || scanres & LaTeX::RERUN) {
336 // Here we must scan the .aux file and look for
337 // "\bibdata" and/or "\bibstyle". If one of those
338 // tags is found -> run bibtex and set rerun = true;
340 minib->Set(_("Running BibTeX."));
342 rerun = runBibTeX(ChangeExtension(file, ".aux", true));
346 // we know on this point that latex has been run once (or we just
347 // returned) and the question now is to decide if we need to run
348 // it any more. This is done by asking if any of the files in the
349 // dependency file has changed. (remember that the checksum for
350 // a given file is reported to have changed if it just was created)
351 // -> if changed or rerun == true:
352 // run latex once more and
353 // update the dependency structure
354 // -> if not changed:
355 // we does nothing at this point
357 if (rerun || head.sumchange()) {
359 lyxerr.debug("Dep. file has changed or rerun requested",
361 lyxerr.debug(string("Run #") + tostr(++count),
363 minib->Set(string(_("LaTeX run number ")) + tostr(count));
366 scanres = scanLogFile(terr);
367 if (scanres & LaTeX::ERRORS) return scanres; // return on error
368 // update the depedencies
369 deplog(head); // reads the latex log
372 lyxerr.debug("Dep. file has NOT changed", Error::LATEX);
376 // The inclusion of files generated by external programs like
377 // makeindex or bibtex might have done changes to pagenumbereing,
378 // etc. And because of this we must run the external programs
379 // again to make sure everything is redone correctly.
380 // Also there should be no need to run the external programs any
383 // run makeindex if the <file>.idx has changed or was generated.
384 if (head.haschanged(ChangeExtension(file, ".idx", true))) {
386 minib->Set(_("Running MakeIndex."));
388 rerun = runMakeIndex(ChangeExtension(file, ".idx", true));
392 // we will only run latex more if the log file asks for it.
393 // or if the sumchange() is true.
394 // -> rerun asked for:
396 // remake the dependency file
397 // goto 2 or return if max runs are reached.
398 // -> rerun not asked for:
399 // just return (fall out of bottom of func)
401 while ((head.sumchange() || rerun || (scanres & LaTeX::RERUN))
402 && count < MAX_RUN) {
403 // Yes rerun until message goes away, or until
404 // MAX_RUNS are reached.
406 lyxerr.debug(string(_("Run #")) + tostr(++count), Error::LATEX);
407 minib->Set(string(_("LaTeX run number ")) + tostr(count));
410 scanres = scanLogFile(terr);
411 if (scanres & LaTeX::ERRORS) return scanres; // return on error
416 // Write the dependencies to file.
418 lyxerr.debug("Done.", Error::LATEX);
423 int LaTeX::operator()()
426 string tmp = cmd + ' ' + file + " > /dev/null";
427 #else // cmd.exe (OS/2) causes SYS0003 error at "/dev/null"
428 string tmp = cmd + ' ' + file + " > nul";
431 return one.Startscript(Systemcalls::System, tmp);
435 bool LaTeX::runMakeIndex(string const &file)
437 lyxerr.debug("idx file has been made,"
438 " running makeindex on file "
439 + file, Error::LATEX);
441 // It should be possible to set the switches for makeindex
442 // sorting style and such. It would also be very convenient
443 // to be able to make style files from within LyX. This has
444 // to come for a later time. (0.13 perhaps?)
445 string tmp = "makeindex -c -q ";
448 one.Startscript(Systemcalls::System, tmp);
453 bool LaTeX::runBibTeX(string const &file)
457 if (!lex.setFile(file)) {
458 // unable to open .aux file
465 token=lex.GetString();
466 else // blank line in the file being read
469 if (contains(token, "\\bibdata{")) {
471 string tmp="bibtex ";
472 tmp += ChangeExtension(file, string(), true);
474 one.Startscript(Systemcalls::System, tmp);
479 // bibtex was not run.
484 int LaTeX::scanLogFile(TeXErrors &terr)
487 int retval = NO_ERRORS;
491 string tmp = ChangeExtension(file, ".log", true);
493 if (!lex.setFile(tmp)) {
494 // unable to open file
496 retval |= NO_LOGFILE;
502 token = lex.GetString();
503 else // blank line in the file being read
506 lyxerr.debug(token, Error::LATEX);
508 if (prefixIs(token, "LaTeX Warning:")) {
509 // Here shall we handle different
511 retval |= LATEX_WARNING;
512 lyxerr.debug("LaTeX Warning.", Error::LATEX);
513 if (contains(token, "Rerun to get cross-references")) {
515 lyxerr.debug("We should rerun.", Error::LATEX);
516 } else if (contains(token, "Citation")
517 && contains(token, "on page")
518 && contains(token, "undefined")) {
521 } else if (prefixIs(token, "Package")) {
523 retval |= PACKAGE_WARNING;
524 if (contains(token, "natbib Warning:")) {
526 if (contains(token, "Citation")
527 && contains(token, "on page")
528 && contains(token, "undefined")) {
531 } else if (contains(token, "Rerun LaTeX.")) {
532 // at least longtable.sty might use this.
535 } else if (prefixIs(token, "! LaTeX Error:")) {
536 // Here shall we handle different
538 retval |= LATEX_ERROR;
539 lyxerr.debug("LaTeX Error.", Error::LATEX);
540 // this is not correct yet
543 } else if (prefixIs(token, "! ")) {
544 // Ok, we have something that looks like a TeX Error
545 // but what do we really have.
547 // Just get the error description:
551 if (contains(desc, "Undefined control sequence")) {
553 lyxerr.debug("TeX Error.", Error::LATEX);
559 string tmp = lex.GetString();
560 if (prefixIs(tmp, "l.")) {
561 // we have a latex error
563 lyxerr.debug("TeX Error.", Error::LATEX);
564 // get the line number:
566 sscanf(tmp.c_str(), "l.%d", &line);
567 // get the rest of the message:
570 tmp = lex.GetString();
571 while ((tmp != "\n" || !contains(errstr, "l."))
572 && !prefixIs(tmp, "! ")
573 && !contains(tmp, "(job aborted")
578 tmp = lex.GetString();
580 terr.insertError(line, desc, errstr);
585 // information messages, TeX warnings and other
586 // warnings we have not caught earlier.
587 if (prefixIs(token, "Overfull ")) {
588 retval |= TEX_WARNING;
589 } else if (prefixIs(token, "Underfull ")) {
590 retval |= TEX_WARNING;
591 } else if (contains(token, "Rerun to get citations")) {
592 // Natbib seems to use this.
594 } else if (contains(token, "No pages of output")) {
595 // A dvi file was not created
597 } else if (contains(token, "That makes 100 errors")) {
598 // More than 100 errors were reprted
599 retval |= TOO_MANY_ERRORS;
607 void LaTeX::deplog(DepTable & head)
609 // This function reads the LaTeX log file end extracts all the external
610 // files used by the LaTeX run. The files are then entered into the
613 string logfile = ChangeExtension(file, ".log", true);
614 FilePtr in(logfile, FilePtr::read);
616 if (in()) while (not_eof) { // We were able to open the file
617 // Now we read chars until we find a '('
621 } while (c != EOF && c != '(');
623 // Nothing more we can do
628 // We now have c == '(', we now read the the sequence of
629 // chars until reaching EOL, or ' ' and put that into a string.
633 while (c != '\n' && c != ' ' && c != ')') {
634 foundfile += char(c);
637 if (foundfile.empty()) continue;
639 lyxerr.debug("Found file: "
642 // Ok now we found a file.
643 // Now we should make sure that
644 // this is a file that we can
645 // access through the normal
647 // (1) foundfile is an
648 // absolute path and should
650 if (AbsolutePath(foundfile)) {
651 lyxerr.debug("AbsolutePath file: "
654 // On inital insert we want to do the update at once
655 // since this file can not be a file generated by
657 head.insert(foundfile, true);
661 // (2) foundfile is in the tmpdir
662 // insert it into head
663 if (FileInfo(OnlyFilename(foundfile)).exist()) {
664 if (suffixIs(foundfile, ".aux")) {
665 lyxerr.debug("We don't want "
666 + OnlyFilename(foundfile)
667 + " in the dep file",
669 } else if (suffixIs(foundfile, ".tex")) {
670 // This is a tex file generated by LyX
671 // and latex is not likely to change this
673 lyxerr.debug("Tmpdir TeX file: "
674 + OnlyFilename(foundfile),
676 head.insert(foundfile, true);
678 lyxerr.debug("In tmpdir file:"
679 + OnlyFilename(foundfile),
681 head.insert(OnlyFilename(foundfile));
686 // (3) the foundfile can be
687 // found in the same dir
688 // as the .lyx file and
689 // should be inserted.
691 if (FileInfo(foundfile).exist()) {
692 lyxerr.print("LyX Strange: this should actually never"
693 " happen anymore, this it should be"
694 " handled by the Absolute check.");
695 lyxerr.debug("Same Directory file: "
698 head.insert(foundfile);
704 lyxerr.debug("Not a file or we are unable to find it.",
712 void LaTeX::deptex(DepTable &head)
714 int except = AUX|LOG|DVI|BBL|IND|GLO;
717 for (int i = 0; i < file_count; i++) {
718 if (!(all_files[i].file & except)) {
719 tmp = ChangeExtension(file,
720 all_files[i].extension,
722 lyxerr.debug("deptex: " + tmp, Error::LATEX);
723 if (fi.newFile(tmp).exist())