]> git.lyx.org Git - lyx.git/blob - src/LaTeX.C
citation patch from Angus
[lyx.git] / src / LaTeX.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Processor         
5  *           Copyright 1995 Matthias Ettrich
6  *           Copyright 1995-2000 The LyX Team.
7  *
8  *           This file is Copyright 1996-2000
9  *           Lars Gullik Bjønnes
10  *
11  * ====================================================== 
12  */
13
14 #include <config.h>
15
16 #ifdef __GNUG__
17 #pragma implementation
18 #endif
19 #include <fstream>
20
21 #include "support/filetools.h"
22 #include "LaTeX.h"
23 #include "support/FileInfo.h"
24 #include "debug.h"
25 #include "support/lyxlib.h"
26 #include "support/syscall.h"
27 #include "support/syscontr.h"
28 #include "support/path.h"
29 #include "support/LRegex.h"
30 #include "support/LSubstring.h"
31 #include "bufferlist.h"
32 #include "minibuffer.h"
33 #include "gettext.h"
34
35 using std::ifstream;
36 using std::getline;
37 using std::endl;
38
39 // TODO: in no particular order
40 // - get rid of the extern BufferList and the call to
41 //   BufferList::updateIncludedTeXfiles, this should either
42 //   be done before calling LaTeX::funcs or in a completely
43 //   different way.
44 // - the bibtex command options should be supported.
45 // - the makeindex style files should be taken care of with
46 //   the dependency mechanism.
47 // - makeindex commandline options should be supported
48 // - somewhere support viewing of bibtex and makeindex log files.
49 // - we should perhaps also scan the bibtex log file
50 // - we should perhaps also scan the bibtex log file
51
52 extern BufferList bufferlist;
53
54 /*
55  * CLASS TEXERRORS
56  */
57
58 void TeXErrors::insertError(int line, string const & error_desc,
59                             string const & error_text)
60 {
61         Error newerr(line, error_desc, error_text);
62         errors.push_back(newerr);
63 }
64
65 /*
66  * CLASS LaTeX
67  */
68
69 LaTeX::LaTeX(string const & latex, string const & f, string const & p)
70                 : cmd(latex), file(f), path(p)
71 {
72         num_errors = 0;
73         depfile = file + ".dep";
74         if (prefixIs(cmd, "pdf")) // Do we use pdflatex ?
75                 depfile += "-pdf";
76 }
77
78
79 void LaTeX::deleteFilesOnError() const
80 {
81         // currently just a dummy function.
82
83         // What files do we have to delete?
84
85         // This will at least make latex do all the runs
86         ::unlink(depfile.c_str());
87
88         // but the reason for the error might be in a generated file...
89
90         string ofname = OnlyFilename(file);
91
92         // bibtex file
93         string bbl = ChangeExtension(ofname, ".bbl");
94         ::unlink(bbl.c_str());
95
96         // makeindex file
97         string ind = ChangeExtension(ofname, ".ind");
98         ::unlink(ind.c_str());
99         
100         // Also remove the aux file
101         string aux = ChangeExtension(ofname, ".aux");
102         ::unlink(aux.c_str());
103 }
104
105
106 int LaTeX::run(TeXErrors & terr, MiniBuffer * minib)
107         // We know that this function will only be run if the lyx buffer
108         // has been changed. We also know that a newly written .tex file
109         // is always different from the previous one because of the date
110         // in it. However it seems safe to run latex (at least) on time each
111         // time the .tex file changes.
112 {
113         int scanres = LaTeX::NO_ERRORS;
114         unsigned int count = 0; // number of times run
115         num_errors = 0; // just to make sure.
116         const unsigned int MAX_RUN = 6;
117         DepTable head; // empty head
118         bool rerun = false; // rerun requested
119         
120         // The class LaTeX does not know the temp path.
121         bufferlist.updateIncludedTeXfiles(GetCWD());
122         
123         // Never write the depfile if an error was encountered.
124         
125         // 0
126         // first check if the file dependencies exist:
127         //     ->If it does exist
128         //             check if any of the files mentioned in it have
129         //             changed (done using a checksum).
130         //                 -> if changed:
131         //                        run latex once and
132         //                        remake the dependency file
133         //                 -> if not changed:
134         //                        just return there is nothing to do for us.
135         //     ->if it doesn't exist
136         //             make it and
137         //             run latex once (we need to run latex once anyway) and
138         //             remake the dependency file.
139         //
140         FileInfo fi(depfile);
141         bool run_bibtex = false;
142         if (fi.exist()) {
143                 // Read the dep file:
144                 head.read(depfile);
145                 // Update the checksums
146                 head.update();
147                 
148                 lyxerr[Debug::DEPEND] << "Dependency file exists" << endl;
149                 if (head.sumchange()) {
150                         ++count;
151                         lyxerr[Debug::DEPEND]
152                                 << "Dependency file has changed" << endl;
153                         lyxerr[Debug::LATEX]
154                                 << "Run #" << count << endl; 
155                         minib->Set(string(_("LaTeX run number ")) + tostr(count));
156                         minib->Store();
157                         this->operator()();
158                         scanres = scanLogFile(terr);
159                         if (scanres & LaTeX::ERRORS) {
160                                 deleteFilesOnError();
161                                 return scanres; // return on error
162                         }
163                         
164                         run_bibtex = scanAux(head);
165                         if (run_bibtex)
166                                 lyxerr[Debug::DEPEND]
167                                         << "Bibtex demands rerun" << endl;
168                 } else {
169                         lyxerr[Debug::DEPEND] << "return no_change" << endl;
170                         return LaTeX::NO_CHANGE;
171                 }
172         } else {
173                 ++count;
174                 lyxerr[Debug::DEPEND]
175                         << "Dependency file does not exist" << endl;
176                 
177                 lyxerr[Debug::LATEX]
178                         << "Run #" << count << endl;
179                 head.insert(file, true);
180                 minib->Set(string(_("LaTeX run number ")) + tostr(count));
181                 minib->Store();
182                 this->operator()();
183                 scanres = scanLogFile(terr);
184                 if (scanres & LaTeX::ERRORS) {
185                         deleteFilesOnError();
186                         return scanres; // return on error
187                 }
188                 
189         }
190
191         // update the dependencies.
192         deplog(head); // reads the latex log
193         head.update();
194
195         // 0.5
196         // At this point we must run external programs if needed.
197         // makeindex will be run if a .idx file changed or was generated.
198         // And if there were undefined citations or changes in references
199         // the .aux file is checked for signs of bibtex. Bibtex is then run
200         // if needed.
201         
202         // run makeindex
203         if (head.haschanged(OnlyFilename(ChangeExtension(file, ".idx")))) {
204                 // no checks for now
205                 lyxerr[Debug::LATEX] << "Running MakeIndex." << endl;
206                 minib->Set(_("Running MakeIndex."));
207                 minib->Store();
208                 rerun = runMakeIndex(OnlyFilename(ChangeExtension(file, ".idx")));
209         }
210
211         // run bibtex
212         if (scanres & LaTeX::UNDEF_CIT
213             || scanres & LaTeX::RERUN
214             || run_bibtex) {
215                 // Here we must scan the .aux file and look for
216                 // "\bibdata" and/or "\bibstyle". If one of those
217                 // tags is found -> run bibtex and set rerun = true;
218                 // no checks for now
219                 lyxerr[Debug::LATEX] << "Running BibTeX." << endl;
220                 minib->Set(_("Running BibTeX."));
221                 minib->Store();
222                 rerun = runBibTeX(OnlyFilename(ChangeExtension(file, ".aux")), 
223                                   head);
224         }
225         
226         // 1
227         // we know on this point that latex has been run once (or we just
228         // returned) and the question now is to decide if we need to run
229         // it any more. This is done by asking if any of the files in the
230         // dependency file has changed. (remember that the checksum for
231         // a given file is reported to have changed if it just was created)
232         //     -> if changed or rerun == true:
233         //             run latex once more and
234         //             update the dependency structure
235         //     -> if not changed:
236         //             we does nothing at this point
237         //
238         if (rerun || head.sumchange()) {
239                 rerun = false;
240                 ++count;
241                 lyxerr[Debug::DEPEND]
242                         << "Dep. file has changed or rerun requested" << endl;
243                 lyxerr[Debug::LATEX]
244                         << "Run #" << count << endl;
245                 minib->Set(string(_("LaTeX run number ")) + tostr(count));
246                 minib->Store();
247                 this->operator()();
248                 scanres = scanLogFile(terr);
249                 if (scanres & LaTeX::ERRORS) {
250                         deleteFilesOnError();
251                         return scanres; // return on error
252                 }
253                 
254                 // update the depedencies
255                 deplog(head); // reads the latex log
256                 head.update();
257         } else {
258                 lyxerr[Debug::DEPEND] << "Dep. file has NOT changed" << endl;
259         }
260
261         // 1.5
262         // The inclusion of files generated by external programs like
263         // makeindex or bibtex might have done changes to pagenumbereing,
264         // etc. And because of this we must run the external programs
265         // again to make sure everything is redone correctly.
266         // Also there should be no need to run the external programs any
267         // more after this.
268         
269         // run makeindex if the <file>.idx has changed or was generated.
270         if (head.haschanged(OnlyFilename(ChangeExtension(file, ".idx")))) {
271                 // no checks for now
272                 lyxerr[Debug::LATEX] << "Running MakeIndex." << endl;
273                 minib->Set(_("Running MakeIndex."));
274                 minib->Store();
275                 rerun = runMakeIndex(OnlyFilename(ChangeExtension(file, ".idx")));
276         }
277         
278         // 2
279         // we will only run latex more if the log file asks for it.
280         // or if the sumchange() is true.
281         //     -> rerun asked for:
282         //             run latex and
283         //             remake the dependency file
284         //             goto 2 or return if max runs are reached.
285         //     -> rerun not asked for:
286         //             just return (fall out of bottom of func)
287         //
288         while ((head.sumchange() || rerun || (scanres & LaTeX::RERUN)) 
289                && count < MAX_RUN) {
290                 // Yes rerun until message goes away, or until
291                 // MAX_RUNS are reached.
292                 rerun = false;
293                 ++count;
294                 lyxerr[Debug::LATEX] << "Run #" << count << endl;
295                 minib->Set(string(_("LaTeX run number ")) + tostr(count));
296                 minib->Store();
297                 this->operator()();
298                 scanres = scanLogFile(terr);
299                 if (scanres & LaTeX::ERRORS) {
300                         deleteFilesOnError();
301                         return scanres; // return on error
302                 }
303                 
304                 // keep this updated
305                 head.update();
306         }
307
308         // Write the dependencies to file.
309         head.write(depfile);
310         lyxerr[Debug::LATEX] << "Done." << endl;
311         return scanres;
312 }
313
314
315 int LaTeX::operator()()
316 {
317 #ifndef __EMX__
318         string tmp = cmd + ' ' + QuoteName(file) + " > /dev/null";
319 #else // cmd.exe (OS/2) causes SYS0003 error at "/dev/null"
320         string tmp = cmd + ' ' + file + " > nul";
321 #endif
322         Systemcalls one;
323         return one.startscript(Systemcalls::System, tmp);
324 }
325
326
327 bool LaTeX::runMakeIndex(string const & f)
328 {
329         lyxerr[Debug::LATEX] << "idx file has been made,"
330                 " running makeindex on file "
331                              <<  f << endl;
332
333         // It should be possible to set the switches for makeindex
334         // sorting style and such. It would also be very convenient
335         // to be able to make style files from within LyX. This has
336         // to come for a later time. (0.13 perhaps?)
337         string tmp = "makeindex -c -q ";
338         tmp += f;
339         Systemcalls one;
340         one.startscript(Systemcalls::System, tmp);
341         return true;
342 }
343
344
345 bool LaTeX::scanAux(DepTable & dep)
346 {
347         // if any of the bib file has changed we don't have to
348         // check the .aux file.
349         if (dep.extchanged(".bib")
350             || dep.extchanged(".bst")) return true;
351         
352         string aux = OnlyFilename(ChangeExtension(file, ".aux"));
353         ifstream ifs(aux.c_str());
354         string token;
355         LRegex reg1("\\\\bibdata\\{([^}]+)\\}");
356         LRegex reg2("\\\\bibstyle\\{([^}]+)\\}");
357         while (getline(ifs, token)) {
358                 if (reg1.exact_match(token)) {
359                         LRegex::SubMatches sub = reg1.exec(token);
360                         string data = LSubstring(token, sub[1].first,
361                                                  sub[1].second);
362                         string::size_type b;
363                         do {
364                                 b = data.find_first_of(',', 0);
365                                 string l;
366                                 if (b == string::npos)
367                                         l = data;
368                                 else {
369                                         l = data.substr( 0, b - 0);
370                                         data.erase(0, b + 1);
371                                 }
372                                 string full_l =
373                                         findtexfile(
374                                                 ChangeExtension(l, "bib"), "bib");
375                                 if (!full_l.empty()) {
376                                         if (!dep.exist(full_l))
377                                                 return true;
378                                 }
379                         } while (b != string::npos);
380                 } else if (reg2.exact_match(token)) {
381                         LRegex::SubMatches sub = reg2.exec(token);
382                         string style = LSubstring(token, sub[1].first,
383                                                   sub[1].second);
384                         // token is now the style file
385                         // pass it to the helper
386                         string full_l =
387                                 findtexfile(
388                                         ChangeExtension(style, "bst"),
389                                         "bst");
390                         if (!full_l.empty()) {
391                                 if (!dep.exist(full_l))
392                                         return true;
393                         }
394                 }
395         }
396         return false;
397 }
398
399
400 bool LaTeX::runBibTeX(string const & f, DepTable & dep)
401 {
402         // Since a run of Bibtex mandates more latex runs it is ok to
403         // remove all ".bib" and ".bst" files, it is also required to
404         // discover style and database changes.
405         dep.remove_files_with_extension(".bib");
406         dep.remove_files_with_extension(".bst");
407         ifstream ifs(f.c_str());
408         string token;
409         bool using_bibtex = false;
410         LRegex reg1("\\\\bibdata\\{([^}]+)\\}");
411         LRegex reg2("\\\\bibstyle\\{([^}]+)\\}");
412         while (getline(ifs, token)) {
413                 if (reg1.exact_match(token)) {
414                         using_bibtex = true;
415                         LRegex::SubMatches const & sub = reg1.exec(token);
416                         string data = LSubstring(token, sub[1].first,
417                                                  sub[1].second);
418                         // data is now all the bib files separated by ','
419                         // get them one by one and pass them to the helper
420                         string::size_type b;
421                         do {
422                                 b = data.find_first_of(',', 0);
423                                 string l;
424                                 if (b == string::npos)
425                                         l = data;
426                                 else {
427                                         l = data.substr(0, b - 0);
428                                         data.erase(0, b + 1);
429                                 }
430                                 string full_l = 
431                                         findtexfile(
432                                                 ChangeExtension(l, "bib"),
433                                                 "bib");
434                                 lyxerr[Debug::LATEX] << "Bibtex database: `"
435                                                      << full_l << "'" << endl;
436                                 if (!full_l.empty()) {
437                                         // add full_l to the dep file.
438                                         dep.insert(full_l, true);
439                                 }
440                         } while (b != string::npos);
441                 } else if (reg2.exact_match(token)) {
442                         using_bibtex = true;
443                         LRegex::SubMatches const & sub = reg2.exec(token);
444                         string style = LSubstring(token, sub[1].first,
445                                                   sub[1].second);
446                         // token is now the style file
447                         // pass it to the helper
448                         string full_l = 
449                                 findtexfile(
450                                         ChangeExtension(style, "bst"),
451                                         "bst");
452                         lyxerr[Debug::LATEX] << "Bibtex style: `"
453                                              << full_l << "'" << endl;
454                         if (!full_l.empty()) {
455                                 // add full_l to the dep file.
456                                 dep.insert(full_l, true);
457                         }
458                 }
459         }
460         if (using_bibtex) {
461                 // run bibtex and
462                 string tmp = "bibtex ";
463                 tmp += OnlyFilename(ChangeExtension(file, string()));
464                 Systemcalls one;
465                 one.startscript(Systemcalls::System, tmp);
466                 return true;
467         }
468         // bibtex was not run.
469         return false;
470 }
471
472
473 int LaTeX::scanLogFile(TeXErrors & terr)
474 {
475         int last_line = -1;
476         int line_count = 1;
477         int retval = NO_ERRORS;
478         string tmp = OnlyFilename(ChangeExtension(file, ".log"));
479         lyxerr[Debug::LATEX] << "Log file: " << tmp << endl;
480         ifstream ifs(tmp.c_str());
481
482         string token;
483         while (getline(ifs, token)) {
484                 lyxerr[Debug::LATEX] << "Log line: " << token << endl;
485                 
486                 if (token.empty())
487                         continue;
488
489                 if (prefixIs(token, "LaTeX Warning:")) {
490                         // Here shall we handle different
491                         // types of warnings
492                         retval |= LATEX_WARNING;
493                         lyxerr[Debug::LATEX] << "LaTeX Warning." << endl;
494                         if (contains(token, "Rerun to get cross-references")) {
495                                 retval |= RERUN;
496                                 lyxerr[Debug::LATEX]
497                                         << "We should rerun." << endl;
498                         } else if (contains(token, "Citation")
499                                    && contains(token, "on page")
500                                    && contains(token, "undefined")) {
501                                 retval |= UNDEF_CIT;
502                         }
503                 } else if (prefixIs(token, "Package")) {
504                         // Package warnings
505                         retval |= PACKAGE_WARNING;
506                         if (contains(token, "natbib Warning:")) {
507                                 // Natbib warnings
508                                 if (contains(token, "Citation")
509                                     && contains(token, "on page")
510                                     && contains(token, "undefined")) {
511                                         retval |= UNDEF_CIT;
512                                 }
513                         } else if (contains(token, "Rerun LaTeX.")) {
514                                 // at least longtable.sty might use this.
515                                 retval |= RERUN;
516                         }
517                 } else if (prefixIs(token, "! ")) {
518                         // Ok, we have something that looks like a TeX Error
519                         // but what do we really have.
520
521                         // Just get the error description:
522                         string desc(token, 2);
523                         if (contains(token, "LaTeX Error:"))
524                                 retval |= LATEX_ERROR;
525                         // get the next line
526                         string tmp;
527                         int count = 0;
528                         do {
529                                 if (!getline(ifs, tmp))
530                                         break;
531                                 if (++count > 10)
532                                         break;
533                         } while (!prefixIs(tmp, "l."));
534                         if (prefixIs(tmp, "l.")) {
535                                 // we have a latex error
536                                 retval |=  TEX_ERROR;
537                                 // get the line number:
538                                 int line = 0;
539                                 sscanf(tmp.c_str(), "l.%d", &line);
540                                 // get the rest of the message:
541                                 string errstr(tmp, tmp.find(' '));
542                                 errstr += '\n';
543                                 getline(ifs, tmp);
544                                 while (!contains(errstr, "l.")
545                                        && !tmp.empty()
546                                        && !prefixIs(tmp, "! ")
547                                        && !contains(tmp, "(job aborted")) {
548                                         errstr += tmp;
549                                         errstr += "\n";
550                                         getline(ifs, tmp);
551                                 }
552                                 lyxerr[Debug::LATEX]
553                                         << "line: " << line << '\n'
554                                         << "Desc: " << desc << '\n'
555                                         << "Text: " << errstr << endl;
556                                 if (line == last_line)
557                                         ++line_count;
558                                 else {
559                                         line_count = 1;
560                                         last_line = line;
561                                 }
562                                 if (line_count <= 5) {
563                                         terr.insertError(line, desc, errstr);
564                                         ++num_errors;
565                                 }
566                         }
567                 } else {
568                         // information messages, TeX warnings and other
569                         // warnings we have not caught earlier.
570                         if (prefixIs(token, "Overfull ")) {
571                                 retval |= TEX_WARNING;
572                         } else if (prefixIs(token, "Underfull ")) {
573                                 retval |= TEX_WARNING;
574                         } else if (contains(token, "Rerun to get citations")) {
575                                 // Natbib seems to use this.
576                                 retval |= RERUN;
577                         } else if (contains(token, "No pages of output")) {
578                                 // A dvi file was not created
579                                 retval |= NO_OUTPUT;
580                         } else if (contains(token, "That makes 100 errors")) {
581                                 // More than 100 errors were reprted
582                                 retval |= TOO_MANY_ERRORS;
583                         }
584                 }
585         }
586         lyxerr[Debug::LATEX] << "Log line: " << token << endl;
587         return retval;
588 }
589
590
591 void LaTeX::deplog(DepTable & head)
592 {
593         // This function reads the LaTeX log file end extracts all the external
594         // files used by the LaTeX run. The files are then entered into the
595         // dependency file.
596
597         string logfile = OnlyFilename(ChangeExtension(file, ".log"));
598
599         LRegex reg1(")* *\\(([^ ]+).*");
600         LRegex reg2("File: ([^ ]+).*");
601         LRegex reg3("No file ([^ ]+)\\..*");
602         LRegex reg4("\\\\openout[0-9]+.*=.*`([^ ]+)'\\..*");
603         LRegex unwanted("^.*\\.(aux|log|dvi|bbl|ind|glo)$");
604         
605         ifstream ifs(logfile.c_str());
606         while (ifs) {
607                 // Ok, the scanning of files here is not sufficient.
608                 // Sometimes files are named by "File: xxx" only
609                 // So I think we should use some regexps to find files instead.
610                 // "(\([^ ]+\)"   should match the "(file " variant
611                 // "File: \([^ ]+\)" should match the "File: file" variant
612                 string foundfile;
613                 string token;
614                 getline(ifs, token);
615                 if (token.empty()) continue;
616                 
617                 if (reg1.exact_match(token)) {
618                         LRegex::SubMatches const & sub = reg1.exec(token);
619                         foundfile = LSubstring(token, sub[1].first,
620                                                sub[1].second);
621                 } else if (reg2.exact_match(token)) {
622                         LRegex::SubMatches const & sub = reg2.exec(token);
623                         foundfile = LSubstring(token, sub[1].first,
624                                                sub[1].second);
625                 } else if (reg3.exact_match(token)) {
626                         LRegex::SubMatches const & sub = reg3.exec(token);
627                         foundfile = LSubstring(token, sub[1].first,
628                                                sub[1].second);
629                 } else if (reg4.exact_match(token)) {
630                         LRegex::SubMatches const & sub = reg4.exec(token);
631                         foundfile = LSubstring(token, sub[1].first,
632                                                sub[1].second);
633                 } else {
634                         continue;
635                 }
636
637                 lyxerr[Debug::DEPEND] << "Found file: " 
638                                       << foundfile << endl;
639                 
640                 // Ok now we found a file.
641                 // Now we should make sure that this is a file that we can
642                 // access through the normal paths.
643                 // We will not try any fance search methods to
644                 // find the file.
645                 
646                 // (1) foundfile is an
647                 //     absolute path and should
648                 //     be inserted.
649                 if (AbsolutePath(foundfile)) {
650                         lyxerr[Debug::DEPEND] << "AbsolutePath file: " 
651                                               << foundfile << endl;
652                         // On inital insert we want to do the update at once
653                         // since this file can not be a file generated by
654                         // the latex run.
655                         head.insert(foundfile, true);
656                         continue;
657                 }
658
659                 // (2) foundfile is in the tmpdir
660                 //     insert it into head
661                 if (FileInfo(OnlyFilename(foundfile)).exist()) {
662                         if (unwanted.exact_match(foundfile)) {
663                                 lyxerr[Debug::DEPEND]
664                                         << "We don't want "
665                                         << OnlyFilename(foundfile)
666                                         << " in the dep file"
667                                         << endl;
668                         } else if (suffixIs(foundfile, ".tex")) {
669                                 // This is a tex file generated by LyX
670                                 // and latex is not likely to change this
671                                 // during its runs.
672                                 lyxerr[Debug::DEPEND]
673                                         << "Tmpdir TeX file: "
674                                         << OnlyFilename(foundfile)
675                                         << endl;
676                                 head.insert(foundfile, true);
677                         } else {
678                                 lyxerr[Debug::DEPEND]
679                                         << "In tmpdir file:"
680                                         << OnlyFilename(foundfile)
681                                         << endl;
682                                 head.insert(OnlyFilename(foundfile));
683                         }
684                         continue;
685                 }
686                 lyxerr[Debug::DEPEND]
687                         << "Not a file or we are unable to find it."
688                         << endl;
689         }
690 }