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 "support/path.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 tmperr = tmperr->next_error;
207 void TeXErrors::printWarnings()
212 void TeXErrors::printStatus()
214 lyxerr << "Error struct:"
215 << "\n status: " << status
216 << "\n no err: " << number_of_errors << endl;
217 if (status == LaTeX::NO_ERRORS) lyxerr << "NO_ERRORS" << endl;
218 if (status & LaTeX::NO_LOGFILE) lyxerr << "NO_LOGFILE" << endl;
219 if (status & LaTeX::NO_OUTPUT) lyxerr << "NO_OUTPUT" << endl;
220 if (status & LaTeX::UNDEF_REF) lyxerr << "UNDEF_REF" << endl;
221 if (status & LaTeX::RERUN) lyxerr << "RERUN" << endl;
222 if (status & LaTeX::TEX_ERROR) lyxerr << "TEX_ERROR" << endl;
223 if (status & LaTeX::TEX_WARNING) lyxerr << "TEX_WARNING" << endl;
224 if (status & LaTeX::NO_FILE) lyxerr << "NO_FILE" << endl;
232 LaTeX::LaTeX(string const & latex, string const & f, string const & p)
233 : cmd(latex), file(f), path(p)
235 tex_files = NO_FILES;
236 file_count = sizeof(all_files) / sizeof(texfile_struct);
238 depfile = file + ".dep";
242 int LaTeX::run(TeXErrors &terr, MiniBuffer *minib)
243 // We know that this function will only be run if the lyx buffer
244 // has been changed. We also know that a newly written .tex file
245 // is always different from the previous one because of the date
246 // in it. However it seems safe to run latex (at least) on time each
247 // time the .tex file changes.
249 int scanres = LaTeX::NO_ERRORS;
250 unsigned int count = 0; // number of times run
251 num_errors = 0; // just to make sure.
252 const unsigned int MAX_RUN = 6;
253 DepTable head; // empty head
254 bool rerun = false; // rerun requested
256 // The class LaTeX does not know the temp path.
257 bufferlist.updateIncludedTeXfiles(GetCWD());
259 // Never write the depfile if an error was encountered.
262 // first check if the file dependencies exist:
263 // ->If it does exist
264 // check if any of the files mentioned in it have
265 // changed (done using a checksum).
267 // run latex once and
268 // remake the dependency file
269 // -> if not changed:
270 // just return there is nothing to do for us.
271 // ->if it doesn't exist
273 // run latex once (we need to run latex once anyway) and
274 // remake the dependency file.
276 FileInfo fi(depfile);
278 // Read the dep file:
280 // Update the checksums
283 lyxerr[Debug::LATEX] << "Dependency file exists" << endl;
284 if (head.sumchange()) {
287 << "Dependency file has changed\n"
288 << "Run #" << count << endl;
289 minib->Set(string(_("LaTeX run number ")) + tostr(count));
292 scanres = scanLogFile(terr);
293 if (scanres & LaTeX::ERRORS) return scanres; // return on error
295 lyxerr[Debug::LATEX] << "return no_change" << endl;
296 return LaTeX::NO_CHANGE;
300 lyxerr[Debug::LATEX] << "Dependency file does not exist\n"
301 << "Run #" << count << endl;
302 head.insert(file, true);
303 minib->Set(string(_("LaTeX run number ")) + tostr(count));
306 scanres = scanLogFile(terr);
307 if (scanres & LaTeX::ERRORS) return scanres; // return on error
310 // update the dependencies.
311 deplog(head); // reads the latex log
312 deptex(head); // checks for latex files
316 // At this point we must run external programs if needed.
317 // makeindex will be run if a .idx file changed or was generated.
318 // And if there were undefined citations or changes in references
319 // the .aux file is checked for signs of bibtex. Bibtex is then run
323 if (head.haschanged(ChangeExtension(file, ".idx", true))) {
325 minib->Set(_("Running MakeIndex."));
327 rerun=runMakeIndex(ChangeExtension(file,".idx",true));
331 if (scanres & LaTeX::UNDEF_CIT || scanres & LaTeX::RERUN) {
332 // Here we must scan the .aux file and look for
333 // "\bibdata" and/or "\bibstyle". If one of those
334 // tags is found -> run bibtex and set rerun = true;
336 minib->Set(_("Running BibTeX."));
338 rerun = runBibTeX(ChangeExtension(file, ".aux", true));
342 // we know on this point that latex has been run once (or we just
343 // returned) and the question now is to decide if we need to run
344 // it any more. This is done by asking if any of the files in the
345 // dependency file has changed. (remember that the checksum for
346 // a given file is reported to have changed if it just was created)
347 // -> if changed or rerun == true:
348 // run latex once more and
349 // update the dependency structure
350 // -> if not changed:
351 // we does nothing at this point
353 if (rerun || head.sumchange()) {
357 << "Dep. file has changed or rerun requested\n"
358 << "Run #" << count << endl;
359 minib->Set(string(_("LaTeX run number ")) + tostr(count));
362 scanres = scanLogFile(terr);
363 if (scanres & LaTeX::ERRORS) return scanres; // return on error
364 // update the depedencies
365 deplog(head); // reads the latex log
368 lyxerr[Debug::LATEX] << "Dep. file has NOT changed" << endl;
372 // The inclusion of files generated by external programs like
373 // makeindex or bibtex might have done changes to pagenumbereing,
374 // etc. And because of this we must run the external programs
375 // again to make sure everything is redone correctly.
376 // Also there should be no need to run the external programs any
379 // run makeindex if the <file>.idx has changed or was generated.
380 if (head.haschanged(ChangeExtension(file, ".idx", true))) {
382 minib->Set(_("Running MakeIndex."));
384 rerun = runMakeIndex(ChangeExtension(file, ".idx", true));
388 // we will only run latex more if the log file asks for it.
389 // or if the sumchange() is true.
390 // -> rerun asked for:
392 // remake the dependency file
393 // goto 2 or return if max runs are reached.
394 // -> rerun not asked for:
395 // just return (fall out of bottom of func)
397 while ((head.sumchange() || rerun || (scanres & LaTeX::RERUN))
398 && count < MAX_RUN) {
399 // Yes rerun until message goes away, or until
400 // MAX_RUNS are reached.
403 lyxerr[Debug::LATEX] << "Run #" << count << endl;
404 minib->Set(string(_("LaTeX run number ")) + tostr(count));
407 scanres = scanLogFile(terr);
408 if (scanres & LaTeX::ERRORS) return scanres; // return on error
413 // Write the dependencies to file.
415 lyxerr[Debug::LATEX] << "Done." << endl;
420 int LaTeX::operator()()
423 string tmp = cmd + ' ' + file + " > /dev/null";
424 #else // cmd.exe (OS/2) causes SYS0003 error at "/dev/null"
425 string tmp = cmd + ' ' + file + " > nul";
428 return one.startscript(Systemcalls::System, tmp);
432 bool LaTeX::runMakeIndex(string const &file)
434 lyxerr[Debug::LATEX] << "idx file has been made,"
435 " running makeindex on file "
438 // It should be possible to set the switches for makeindex
439 // sorting style and such. It would also be very convenient
440 // to be able to make style files from within LyX. This has
441 // to come for a later time. (0.13 perhaps?)
442 string tmp = "makeindex -c -q ";
445 one.startscript(Systemcalls::System, tmp);
450 bool LaTeX::runBibTeX(string const &file)
454 if (!lex.setFile(file)) {
455 // unable to open .aux file
462 token=lex.GetString();
463 else // blank line in the file being read
466 if (contains(token, "\\bibdata{")) {
468 string tmp="bibtex ";
469 tmp += ChangeExtension(file, string(), true);
471 one.startscript(Systemcalls::System, tmp);
476 // bibtex was not run.
481 int LaTeX::scanLogFile(TeXErrors &terr)
483 int retval = NO_ERRORS;
484 string tmp = ChangeExtension(file, ".log", true);
487 if (!lex.setFile(tmp)) {
488 // unable to open file
490 retval |= NO_LOGFILE;
497 token = lex.GetString();
498 else // blank line in the file being read
501 lyxerr[Debug::LATEX] << token << endl;
503 if (prefixIs(token, "LaTeX Warning:")) {
504 // Here shall we handle different
506 retval |= LATEX_WARNING;
507 lyxerr[Debug::LATEX] << "LaTeX Warning." << endl;
508 if (contains(token, "Rerun to get cross-references")) {
511 << "We should rerun." << endl;
512 } else if (contains(token, "Citation")
513 && contains(token, "on page")
514 && contains(token, "undefined")) {
517 } else if (prefixIs(token, "Package")) {
519 retval |= PACKAGE_WARNING;
520 if (contains(token, "natbib Warning:")) {
522 if (contains(token, "Citation")
523 && contains(token, "on page")
524 && contains(token, "undefined")) {
527 } else if (contains(token, "Rerun LaTeX.")) {
528 // at least longtable.sty might use this.
531 } else if (prefixIs(token, "! LaTeX Error:")) {
532 // Here shall we handle different
534 retval |= LATEX_ERROR;
535 lyxerr[Debug::LATEX] << "LaTeX Error." << endl;
536 // this is not correct yet
539 } else if (prefixIs(token, "! ")) {
540 // Ok, we have something that looks like a TeX Error
541 // but what do we really have.
543 // Just get the error description:
547 if (contains(desc, "Undefined control sequence")) {
549 lyxerr[Debug::LATEX] << "TeX Error." << endl;
555 string tmp = lex.GetString();
556 if (prefixIs(tmp, "l.")) {
557 // we have a latex error
560 <<"TeX Error." << endl;
561 // get the line number:
563 sscanf(tmp.c_str(), "l.%d", &line);
564 // get the rest of the message:
567 tmp = lex.GetString();
568 while ((tmp != "\n" || !contains(errstr, "l."))
569 && !prefixIs(tmp, "! ")
570 && !contains(tmp, "(job aborted")
575 tmp = lex.GetString();
577 terr.insertError(line, desc, errstr);
582 // information messages, TeX warnings and other
583 // warnings we have not caught earlier.
584 if (prefixIs(token, "Overfull ")) {
585 retval |= TEX_WARNING;
586 } else if (prefixIs(token, "Underfull ")) {
587 retval |= TEX_WARNING;
588 } else if (contains(token, "Rerun to get citations")) {
589 // Natbib seems to use this.
591 } else if (contains(token, "No pages of output")) {
592 // A dvi file was not created
594 } else if (contains(token, "That makes 100 errors")) {
595 // More than 100 errors were reprted
596 retval |= TOO_MANY_ERRORS;
604 void LaTeX::deplog(DepTable & head)
606 // This function reads the LaTeX log file end extracts all the external
607 // files used by the LaTeX run. The files are then entered into the
610 string logfile = ChangeExtension(file, ".log", true);
611 FilePtr in(logfile, FilePtr::read);
613 if (in()) while (not_eof) { // We were able to open the file
614 // Now we read chars until we find a '('
618 } while (c != EOF && c != '(');
620 // Nothing more we can do
625 // We now have c == '(', we now read the the sequence of
626 // chars until reaching EOL, or ' ' and put that into a string.
630 while (c != '\n' && c != ' ' && c != ')') {
631 foundfile += char(c);
634 if (foundfile.empty()) continue;
636 lyxerr[Debug::LATEX] << "Found file: "
637 << foundfile << endl;
638 // Ok now we found a file.
639 // Now we should make sure that
640 // this is a file that we can
641 // access through the normal
643 // (1) foundfile is an
644 // absolute path and should
646 if (AbsolutePath(foundfile)) {
647 lyxerr[Debug::LATEX] << "AbsolutePath file: "
648 << foundfile << endl;
649 // On inital insert we want to do the update at once
650 // since this file can not be a file generated by
652 head.insert(foundfile, true);
656 // (2) foundfile is in the tmpdir
657 // insert it into head
658 if (FileInfo(OnlyFilename(foundfile)).exist()) {
659 if (suffixIs(foundfile, ".aux")) {
660 lyxerr[Debug::LATEX] << "We don't want "
661 << OnlyFilename(foundfile)
662 << " in the dep file"
664 } else if (suffixIs(foundfile, ".tex")) {
665 // This is a tex file generated by LyX
666 // and latex is not likely to change this
668 lyxerr[Debug::LATEX] << "Tmpdir TeX file: "
669 << OnlyFilename(foundfile)
671 head.insert(foundfile, true);
673 lyxerr[Debug::LATEX] << "In tmpdir file:"
674 << OnlyFilename(foundfile)
676 head.insert(OnlyFilename(foundfile));
681 // (3) the foundfile can be
682 // found in the same dir
683 // as the .lyx file and
684 // should be inserted.
686 if (FileInfo(foundfile).exist()) {
687 lyxerr << "LyX Strange: this should actually never"
688 " happen anymore, this it should be"
689 " handled by the Absolute check."
691 lyxerr[Debug::LATEX] << "Same Directory file: "
692 << foundfile << endl;
693 head.insert(foundfile);
698 << "Not a file or we are unable to find it."
704 void LaTeX::deptex(DepTable &head)
706 int except = AUX|LOG|DVI|BBL|IND|GLO;
709 for (int i = 0; i < file_count; i++) {
710 if (!(all_files[i].file & except)) {
711 tmp = ChangeExtension(file,
712 all_files[i].extension,
714 lyxerr[Debug::LATEX] << "deptex: " << tmp << endl;
715 if (fi.newFile(tmp).exist())