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 << endl;
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 << "Printing errors." << endl;
195 Error *tmperr = errors;
197 lyxerr << "Error in line "
198 << tmperr->error_in_line
199 << ": " << tmperr->error_desc
200 << '\n' << tmperr->error_text << endl;
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 << "Error struct:"
218 << "\n status: " << status
219 << "\n no err: " << number_of_errors << endl;
220 if (status == LaTeX::NO_ERRORS) lyxerr << "NO_ERRORS" << endl;
221 if (status & LaTeX::NO_LOGFILE) lyxerr << "NO_LOGFILE" << endl;
222 if (status & LaTeX::NO_OUTPUT) lyxerr << "NO_OUTPUT" << endl;
223 if (status & LaTeX::UNDEF_REF) lyxerr << "UNDEF_REF" << endl;
224 if (status & LaTeX::RERUN) lyxerr << "RERUN" << endl;
225 if (status & LaTeX::TEX_ERROR) lyxerr << "TEX_ERROR" << endl;
226 if (status & LaTeX::TEX_WARNING) lyxerr << "TEX_WARNING" << endl;
227 if (status & LaTeX::NO_FILE) lyxerr << "NO_FILE" << endl;
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::LATEX] << "Dependency file exists" << endl;
287 if (head.sumchange()) {
290 << "Dependency file has changed\n"
291 << "Run #" << count << endl;
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::LATEX] << "return no_change" << endl;
299 return LaTeX::NO_CHANGE;
303 lyxerr[Debug::LATEX] << "Dependency file does not exist\n"
304 << "Run #" << count << endl;
305 head.insert(file, true);
306 minib->Set(string(_("LaTeX run number ")) + tostr(count));
309 scanres = scanLogFile(terr);
310 if (scanres & LaTeX::ERRORS) return scanres; // return on error
313 // update the dependencies.
314 deplog(head); // reads the latex log
315 deptex(head); // checks for latex files
319 // At this point we must run external programs if needed.
320 // makeindex will be run if a .idx file changed or was generated.
321 // And if there were undefined citations or changes in references
322 // the .aux file is checked for signs of bibtex. Bibtex is then run
326 if (head.haschanged(ChangeExtension(file, ".idx", true))) {
328 minib->Set(_("Running MakeIndex."));
330 rerun=runMakeIndex(ChangeExtension(file,".idx",true));
334 if (scanres & LaTeX::UNDEF_CIT || scanres & LaTeX::RERUN) {
335 // Here we must scan the .aux file and look for
336 // "\bibdata" and/or "\bibstyle". If one of those
337 // tags is found -> run bibtex and set rerun = true;
339 minib->Set(_("Running BibTeX."));
341 rerun = runBibTeX(ChangeExtension(file, ".aux", true));
345 // we know on this point that latex has been run once (or we just
346 // returned) and the question now is to decide if we need to run
347 // it any more. This is done by asking if any of the files in the
348 // dependency file has changed. (remember that the checksum for
349 // a given file is reported to have changed if it just was created)
350 // -> if changed or rerun == true:
351 // run latex once more and
352 // update the dependency structure
353 // -> if not changed:
354 // we does nothing at this point
356 if (rerun || head.sumchange()) {
360 << "Dep. file has changed or rerun requested\n"
361 << "Run #" << count << endl;
362 minib->Set(string(_("LaTeX run number ")) + tostr(count));
365 scanres = scanLogFile(terr);
366 if (scanres & LaTeX::ERRORS) return scanres; // return on error
367 // update the depedencies
368 deplog(head); // reads the latex log
371 lyxerr[Debug::LATEX] << "Dep. file has NOT changed" << endl;
375 // The inclusion of files generated by external programs like
376 // makeindex or bibtex might have done changes to pagenumbereing,
377 // etc. And because of this we must run the external programs
378 // again to make sure everything is redone correctly.
379 // Also there should be no need to run the external programs any
382 // run makeindex if the <file>.idx has changed or was generated.
383 if (head.haschanged(ChangeExtension(file, ".idx", true))) {
385 minib->Set(_("Running MakeIndex."));
387 rerun = runMakeIndex(ChangeExtension(file, ".idx", true));
391 // we will only run latex more if the log file asks for it.
392 // or if the sumchange() is true.
393 // -> rerun asked for:
395 // remake the dependency file
396 // goto 2 or return if max runs are reached.
397 // -> rerun not asked for:
398 // just return (fall out of bottom of func)
400 while ((head.sumchange() || rerun || (scanres & LaTeX::RERUN))
401 && count < MAX_RUN) {
402 // Yes rerun until message goes away, or until
403 // MAX_RUNS are reached.
406 lyxerr[Debug::LATEX] << "Run #" << count << endl;
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::LATEX] << "Done." << endl;
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::LATEX] << "idx file has been made,"
438 " running makeindex on file "
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::LATEX] << token << endl;
508 if (prefixIs(token, "LaTeX Warning:")) {
509 // Here shall we handle different
511 retval |= LATEX_WARNING;
512 lyxerr[Debug::LATEX] << "LaTeX Warning." << endl;
513 if (contains(token, "Rerun to get cross-references")) {
516 << "We should rerun." << endl;
517 } else if (contains(token, "Citation")
518 && contains(token, "on page")
519 && contains(token, "undefined")) {
522 } else if (prefixIs(token, "Package")) {
524 retval |= PACKAGE_WARNING;
525 if (contains(token, "natbib Warning:")) {
527 if (contains(token, "Citation")
528 && contains(token, "on page")
529 && contains(token, "undefined")) {
532 } else if (contains(token, "Rerun LaTeX.")) {
533 // at least longtable.sty might use this.
536 } else if (prefixIs(token, "! LaTeX Error:")) {
537 // Here shall we handle different
539 retval |= LATEX_ERROR;
540 lyxerr[Debug::LATEX] << "LaTeX Error." << endl;
541 // this is not correct yet
544 } else if (prefixIs(token, "! ")) {
545 // Ok, we have something that looks like a TeX Error
546 // but what do we really have.
548 // Just get the error description:
552 if (contains(desc, "Undefined control sequence")) {
554 lyxerr[Debug::LATEX] << "TeX Error." << endl;
560 string tmp = lex.GetString();
561 if (prefixIs(tmp, "l.")) {
562 // we have a latex error
565 <<"TeX Error." << endl;
566 // get the line number:
568 sscanf(tmp.c_str(), "l.%d", &line);
569 // get the rest of the message:
572 tmp = lex.GetString();
573 while ((tmp != "\n" || !contains(errstr, "l."))
574 && !prefixIs(tmp, "! ")
575 && !contains(tmp, "(job aborted")
580 tmp = lex.GetString();
582 terr.insertError(line, desc, errstr);
587 // information messages, TeX warnings and other
588 // warnings we have not caught earlier.
589 if (prefixIs(token, "Overfull ")) {
590 retval |= TEX_WARNING;
591 } else if (prefixIs(token, "Underfull ")) {
592 retval |= TEX_WARNING;
593 } else if (contains(token, "Rerun to get citations")) {
594 // Natbib seems to use this.
596 } else if (contains(token, "No pages of output")) {
597 // A dvi file was not created
599 } else if (contains(token, "That makes 100 errors")) {
600 // More than 100 errors were reprted
601 retval |= TOO_MANY_ERRORS;
609 void LaTeX::deplog(DepTable & head)
611 // This function reads the LaTeX log file end extracts all the external
612 // files used by the LaTeX run. The files are then entered into the
615 string logfile = ChangeExtension(file, ".log", true);
616 FilePtr in(logfile, FilePtr::read);
618 if (in()) while (not_eof) { // We were able to open the file
619 // Now we read chars until we find a '('
623 } while (c != EOF && c != '(');
625 // Nothing more we can do
630 // We now have c == '(', we now read the the sequence of
631 // chars until reaching EOL, or ' ' and put that into a string.
635 while (c != '\n' && c != ' ' && c != ')') {
636 foundfile += char(c);
639 if (foundfile.empty()) continue;
641 lyxerr[Debug::LATEX] << "Found file: "
642 << foundfile << endl;
643 // Ok now we found a file.
644 // Now we should make sure that
645 // this is a file that we can
646 // access through the normal
648 // (1) foundfile is an
649 // absolute path and should
651 if (AbsolutePath(foundfile)) {
652 lyxerr[Debug::LATEX] << "AbsolutePath file: "
653 << foundfile << endl;
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::LATEX] << "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::LATEX] << "Tmpdir TeX file: "
674 << OnlyFilename(foundfile)
676 head.insert(foundfile, true);
678 lyxerr[Debug::LATEX] << "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 << "LyX Strange: this should actually never"
693 " happen anymore, this it should be"
694 " handled by the Absolute check."
696 lyxerr[Debug::LATEX] << "Same Directory file: "
697 << foundfile << endl;
698 head.insert(foundfile);
705 << "Not a file or we are unable to find it."
711 void LaTeX::deptex(DepTable &head)
713 int except = AUX|LOG|DVI|BBL|IND|GLO;
716 for (int i = 0; i < file_count; i++) {
717 if (!(all_files[i].file & except)) {
718 tmp = ChangeExtension(file,
719 all_files[i].extension,
721 lyxerr[Debug::LATEX] << "deptex: " << tmp << endl;
722 if (fi.newFile(tmp).exist())