1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
5 * Copyright (C) 1995 Matthias Ettrich
6 * Copyright (C) 1995-1999 The LyX Team.
8 * This file is Copyright (C) 1996-1999
11 *======================================================
20 #pragma implementation
23 #include "filetools.h"
31 #include "pathstack.h"
32 #include "bufferlist.h"
33 #include "minibuffer.h"
36 // $Id: LaTeX.C,v 1.1 1999/09/27 18:44:36 larsbj Exp $
38 #if !defined(lint) && !defined(WITH_WARNINGS)
39 static char vcid[] = "$Id: LaTeX.C,v 1.1 1999/09/27 18:44:36 larsbj Exp $";
42 extern BufferList bufferlist;
44 struct texfile_struct {
45 LaTeX::TEX_FILES file;
46 char const *extension;
50 const texfile_struct all_files[] = {
51 { LaTeX::AUX, ".aux"},
52 { LaTeX::BBL, ".bbl"},
53 { LaTeX::DVI, ".dvi"},
54 { LaTeX::GLO, ".glo"},
55 { LaTeX::IDX, ".idx"},
56 { LaTeX::IND, ".ind"},
57 { LaTeX::LOF, ".lof"},
58 { LaTeX::LOA, ".loa"},
59 { LaTeX::LOG, ".log"},
60 { LaTeX::LOT, ".lot"},
61 { LaTeX::TOC, ".toc"},
62 { LaTeX::LTX, ".ltx"},
66 // This should perhaps be placed in LyXLex
68 LString readLine(FILE *file)
79 } while (!feof(file) && s[i-1] != '\n' && i<510);
82 if (i == 1 && feof(file))
96 // I did not leave this inlined because DEC cxx does not like
97 // variables declarations in inlined code (JMarc)
98 TeXErrors::~TeXErrors()
102 tmp = errors->next_error;
109 void TeXErrors::scanError(LyXLex &lex)
111 LString token = lex.GetString();
112 // Sometimes the error string goes over more than one
113 // line, and we need to get them all.
115 LString tmp = readLine(lex.getFile()).frontStrip();
116 if (tmp == "\n" || tmp.empty()) {
117 tmp = readLine(lex.getFile()).frontStrip();
118 if (tmp.contains("See the LaTeX manual")) {
120 tmp = readLine(lex.getFile());
121 } while (!tmp.empty() && !tmp.contains("..."));
123 tmp = readLine(lex.getFile()).frontStrip();
126 while ((tmp != "\n" || !errstr.contains("l."))
127 && !tmp.prefixIs("! ")
128 && !tmp.contains("(job aborted")
131 tmp = readLine(lex.getFile()).frontStrip();
133 lyxerr.debug("tmp: " + errstr);
135 // unfortunately the error line is not always given
136 // by "l.###" in the beginning of the error string
137 // therefore we must search for "l.###" in the error
139 if (errstr.contains("l.")) {
140 // We make a const copy to make [] fast. (Asger)
141 LString const es = errstr;
142 for (int i = 2; i < es.length(); i++) {
143 if (es[i-2] == 'l' && es[i-1] == '.' &&
144 (es[i] >= '0' && es[i]<= '9')) {
145 line = atoi(es.c_str() + i);
150 insertError(line, token, errstr);
152 if (tmp.prefixIs("! ")) {
158 bool TeXErrors::getFirstError(int *line, LString *text)
162 *line = next_error->error_in_line;
163 *text = next_error->error_desc + "\n" + next_error->error_text;
164 next_error = next_error->next_error;
171 bool TeXErrors::getNextError(int *line, LString *text)
174 *line = next_error->error_in_line;
175 *text = next_error->error_desc + "\n" + next_error->error_text;
176 next_error = next_error->next_error;
183 void TeXErrors::insertError(int line, LString const &error_desc,
184 LString const &error_text)
186 Error *newerr = new Error(line, error_desc, error_text);
188 Error *tmperr = errors;
189 while (tmperr->next_error) tmperr = tmperr->next_error;
190 tmperr->next_error = newerr;
197 void TeXErrors::printErrors()
199 lyxerr.print("Printing errors.");
201 Error *tmperr = errors;
203 lyxerr.print(LString("Error in line ")
204 + tmperr->error_in_line
205 + ": " + tmperr->error_desc
206 + '\n' + tmperr->error_text);
207 //%d: %s\n%s\n", tmperr->error_in_line,
208 // tmperr->error_desc.c_str(),
209 // tmperr->error_text.c_str());
210 tmperr = tmperr->next_error;
216 void TeXErrors::printWarnings()
221 void TeXErrors::printStatus()
223 lyxerr.print("Error struct:");
224 lyxerr.print(LString(" status: ") + int(status));
225 lyxerr.print(LString(" no err: ") + int(number_of_errors));
226 if (status == LaTeX::NO_ERRORS) lyxerr.print("NO_ERRORS");
227 if (status & LaTeX::NO_LOGFILE) lyxerr.print("NO_LOGFILE");
228 if (status & LaTeX::NO_OUTPUT) lyxerr.print("NO_OUTPUT");
229 if (status & LaTeX::UNDEF_REF) lyxerr. print("UNDEF_REF");
230 if (status & LaTeX::RERUN) lyxerr. print("RERUN");
231 if (status & LaTeX::TEX_ERROR) lyxerr.print("TEX_ERROR");
232 if (status & LaTeX::TEX_WARNING) lyxerr.print("TEX_WARNING");
233 if (status & LaTeX::NO_FILE) lyxerr.print("NO_FILE");
241 LaTeX::LaTeX(LString const & latex, LString const & f, LString const & p)
242 : cmd(latex), file(f), path(p)
244 tex_files = NO_FILES;
245 file_count = sizeof(all_files) / sizeof(texfile_struct);
247 depfile = file + ".dep";
251 int LaTeX::run(TeXErrors &terr, MiniBuffer *minib)
252 // We know that this function will only be run if the lyx buffer
253 // has been changed. We also know that a newly written .tex file
254 // is always different from the previous one because of the date
255 // in it. However it seems safe to run latex (at least) on time each
256 // time the .tex file changes.
258 int scanres = LaTeX::NO_ERRORS;
259 unsigned int count = 0; // number of times run
260 num_errors = 0; // just to make sure.
261 const unsigned int MAX_RUN = 6;
262 DepTable head; // empty head
263 bool rerun = false; // rerun requested
265 // The class LaTeX does not know the temp path.
266 bufferlist.updateIncludedTeXfiles(GetCWD());
268 // Never write the depfile if an error was encountered.
271 // first check if the file dependencies exist:
272 // ->If it does exist
273 // check if any of the files mentioned in it have
274 // changed (done using a checksum).
276 // run latex once and
277 // remake the dependency file
278 // -> if not changed:
279 // just return there is nothing to do for us.
280 // ->if it doesn't exist
282 // run latex once (we need to run latex once anyway) and
283 // remake the dependency file.
285 FileInfo fi(depfile);
287 // Read the dep file:
289 // Update the checksums
292 lyxerr.debug("Dependency file exists", Error::LATEX);
293 if (head.sumchange()) {
294 lyxerr.debug("Dependency file has changed",
296 lyxerr.debug(LString(_("Run #")) + int(++count),
298 minib->Set(LString(_("LaTeX run number ")) + int(count));
301 scanres = scanLogFile(terr);
302 if (scanres & LaTeX::ERRORS) return scanres; // return on error
304 lyxerr.debug("return no_change", Error::LATEX);
305 return LaTeX::NO_CHANGE;
308 lyxerr.debug("Dependency file does not exist",
310 lyxerr.debug(LString(_("Run #")) + int(++count),
312 head.insert(file, true);
313 minib->Set(LString(_("LaTeX run number ")) + int(count));
316 scanres = scanLogFile(terr);
317 if (scanres & LaTeX::ERRORS) return scanres; // return on error
320 // update the dependencies.
321 deplog(head); // reads the latex log
322 deptex(head); // checks for latex files
326 // At this point we must run external programs if needed.
327 // makeindex will be run if a .idx file changed or was generated.
328 // And if there were undefined citations or changes in references
329 // the .aux file is checked for signs of bibtex. Bibtex is then run
333 if (head.haschanged(ChangeExtension(file, ".idx", true))) {
335 minib->Set(_("Running MakeIndex."));
337 rerun=runMakeIndex(ChangeExtension(file,".idx",true));
341 if (scanres & LaTeX::UNDEF_CIT || scanres & LaTeX::RERUN) {
342 // Here we must scan the .aux file and look for
343 // "\bibdata" and/or "\bibstyle". If one of those
344 // tags is found -> run bibtex and set rerun = true;
346 minib->Set(_("Running BibTeX."));
348 rerun = runBibTeX(ChangeExtension(file, ".aux", true));
352 // we know on this point that latex has been run once (or we just
353 // returned) and the question now is to decide if we need to run
354 // it any more. This is done by asking if any of the files in the
355 // dependency file has changed. (remember that the checksum for
356 // a given file is reported to have changed if it just was created)
357 // -> if changed or rerun == true:
358 // run latex once more and
359 // update the dependency structure
360 // -> if not changed:
361 // we does nothing at this point
363 if (rerun || head.sumchange()) {
365 lyxerr.debug("Dep. file has changed or rerun requested",
367 lyxerr.debug(LString("Run #") + int(++count),
369 minib->Set(LString(_("LaTeX run number ")) + int(count));
372 scanres = scanLogFile(terr);
373 if (scanres & LaTeX::ERRORS) return scanres; // return on error
374 // update the depedencies
375 deplog(head); // reads the latex log
378 lyxerr.debug("Dep. file has NOT changed", Error::LATEX);
382 // The inclusion of files generated by external programs like
383 // makeindex or bibtex might have done changes to pagenumbereing,
384 // etc. And because of this we must run the external programs
385 // again to make sure everything is redone correctly.
386 // Also there should be no need to run the external programs any
389 // run makeindex if the <file>.idx has changed or was generated.
390 if (head.haschanged(ChangeExtension(file, ".idx", true))) {
392 minib->Set(_("Running MakeIndex."));
394 rerun = runMakeIndex(ChangeExtension(file, ".idx", true));
398 // we will only run latex more if the log file asks for it.
399 // or if the sumchange() is true.
400 // -> rerun asked for:
402 // remake the dependency file
403 // goto 2 or return if max runs are reached.
404 // -> rerun not asked for:
405 // just return (fall out of bottom of func)
407 while ((head.sumchange() || rerun || (scanres & LaTeX::RERUN))
408 && count < MAX_RUN) {
409 // Yes rerun until message goes away, or until
410 // MAX_RUNS are reached.
412 lyxerr.debug(LString(_("Run #")) + int(++count), Error::LATEX);
413 minib->Set(LString(_("LaTeX run number ")) + int(count));
416 scanres = scanLogFile(terr);
417 if (scanres & LaTeX::ERRORS) return scanres; // return on error
422 // Write the dependencies to file.
424 lyxerr.debug("Done.", Error::LATEX);
429 int LaTeX::operator()()
432 LString tmp = cmd + ' ' + file + " > /dev/null";
433 #else // cmd.exe (OS/2) causes SYS0003 error at "/dev/null"
434 LString tmp = cmd + ' ' + file + " > nul";
437 return one.Startscript(Systemcalls::System, tmp);
441 bool LaTeX::runMakeIndex(LString const &file)
443 lyxerr.debug("idx file has been made,"
444 " running makeindex on file "
445 + file, Error::LATEX);
447 // It should be possible to set the switches for makeindex
448 // sorting style and such. It would also be very convenient
449 // to be able to make style files from within LyX. This has
450 // to come for a later time. (0.13 perhaps?)
451 LString tmp = "makeindex -c -q ";
454 one.Startscript(Systemcalls::System, tmp);
459 bool LaTeX::runBibTeX(LString const &file)
463 if (!lex.setFile(file)) {
464 // unable to open .aux file
471 token=lex.GetString();
472 else // blank line in the file being read
475 if (token.contains("\\bibdata{")) {
477 LString tmp="bibtex ";
478 tmp += ChangeExtension(file, LString(), true);
480 one.Startscript(Systemcalls::System, tmp);
485 // bibtex was not run.
490 int LaTeX::scanLogFile(TeXErrors &terr)
493 int retval = NO_ERRORS;
497 LString tmp = ChangeExtension(file, ".log", true);
499 if (!lex.setFile(tmp)) {
500 // unable to open file
502 retval |= NO_LOGFILE;
508 token = lex.GetString();
509 else // blank line in the file being read
512 lyxerr.debug(token, Error::LATEX);
514 if (token.prefixIs("LaTeX Warning:")) {
515 // Here shall we handle different
517 retval |= LATEX_WARNING;
518 lyxerr.debug("LaTeX Warning.", Error::LATEX);
519 if (token.contains("Rerun to get cross-references")) {
521 lyxerr.debug("We should rerun.", Error::LATEX);
522 } else if (token.contains("Citation")
523 && token.contains("on page")
524 && token.contains("undefined")) {
527 } else if (token.prefixIs("Package")) {
529 retval |= PACKAGE_WARNING;
530 if (token.contains("natbib Warning:")) {
532 if (token.contains("Citation")
533 && token.contains("on page")
534 && token.contains("undefined")) {
537 } else if (token.contains("Rerun LaTeX.")) {
538 // at least longtable.sty might use this.
541 } else if (token.prefixIs("! LaTeX Error:")) {
542 // Here shall we handle different
544 retval |= LATEX_ERROR;
545 lyxerr.debug("LaTeX Error.", Error::LATEX);
546 // this is not correct yet
549 } else if (token.prefixIs("! ")) {
550 // Ok, we have something that looks like a TeX Error
551 // but what do we really have.
553 // Just get the error description:
555 desc.substring(2, desc.length() - 1);
557 if (desc.contains("Undefined control sequence")) {
559 lyxerr.debug("TeX Error.", Error::LATEX);
565 LString tmp = lex.GetString();
566 if (tmp.prefixIs("l.")) {
567 // we have a latex error
569 lyxerr.debug("TeX Error.", Error::LATEX);
570 // get the line number:
572 sscanf(tmp.c_str(), "l.%d", &line);
573 // get the rest of the message:
576 tmp = lex.GetString();
577 while ((tmp != "\n" || !errstr.contains("l."))
578 && !tmp.prefixIs("! ")
579 && !tmp.contains("(job aborted")
584 tmp = lex.GetString();
586 terr.insertError(line, desc, errstr);
591 // information messages, TeX warnings and other
592 // warnings we have not caught earlier.
593 if (token.prefixIs("Overfull ")) {
594 retval |= TEX_WARNING;
595 } else if (token.prefixIs("Underfull ")) {
596 retval |= TEX_WARNING;
597 } else if (token.contains("Rerun to get citations")) {
598 // Natbib seems to use this.
600 } else if (token.contains("No pages of output")) {
601 // A dvi file was not created
603 } else if (token.contains("That makes 100 errors")) {
604 // More than 100 errors were reprted
605 retval |= TOO_MANY_ERRORS;
613 void LaTeX::deplog(DepTable & head)
615 // This function reads the LaTeX log file end extracts all the external
616 // files used by the LaTeX run. The files are then entered into the
619 LString logfile = ChangeExtension(file, ".log", true);
620 FilePtr in(logfile, FilePtr::read);
622 if (in()) while (not_eof) { // We were able to open the file
623 // Now we read chars until we find a '('
627 } while (c != EOF && c != '(');
629 // Nothing more we can do
634 // We now have c == '(', we now read the the sequence of
635 // chars until reaching EOL, or ' ' and put that into a string.
639 while (c != '\n' && c != ' ' && c != ')') {
640 foundfile += char(c);
643 if (foundfile.empty()) continue;
645 lyxerr.debug("Found file: "
648 // Ok now we found a file.
649 // Now we should make sure that
650 // this is a file that we can
651 // access through the normal
653 // (1) foundfile is an
654 // absolute path and should
656 if (AbsolutePath(foundfile)) {
657 lyxerr.debug("AbsolutePath file: "
660 // On inital insert we want to do the update at once
661 // since this file can not be a file generated by
663 head.insert(foundfile, true);
667 // (2) foundfile is in the tmpdir
668 // insert it into head
669 if (FileInfo(OnlyFilename(foundfile)).exist()) {
670 if (foundfile.suffixIs(".aux")) {
671 lyxerr.debug("We don't want "
672 + OnlyFilename(foundfile)
673 + " in the dep file",
675 } else if (foundfile.suffixIs(".tex")) {
676 // This is a tex file generated by LyX
677 // and latex is not likely to change this
679 lyxerr.debug("Tmpdir TeX file: "
680 + OnlyFilename(foundfile),
682 head.insert(foundfile, true);
684 lyxerr.debug("In tmpdir file:"
685 + OnlyFilename(foundfile),
687 head.insert(OnlyFilename(foundfile));
692 // (3) the foundfile can be
693 // found in the same dir
694 // as the .lyx file and
695 // should be inserted.
697 if (FileInfo(foundfile).exist()) {
698 lyxerr.print("LyX Strange: this should actually never"
699 " happen anymore, this it should be"
700 " handled by the Absolute check.");
701 lyxerr.debug("Same Directory file: "
704 head.insert(foundfile);
710 lyxerr.debug("Not a file or we are unable to find it.",
718 void LaTeX::deptex(DepTable &head)
720 int except = AUX|LOG|DVI|BBL|IND|GLO;
723 for (int i = 0; i < file_count; i++) {
724 if (!(all_files[i].file & except)) {
725 tmp = ChangeExtension(file,
726 all_files[i].extension,
728 lyxerr.debug("deptex: " + tmp, Error::LATEX);
729 if (fi.newFile(tmp).exist())