]> git.lyx.org Git - lyx.git/blob - src/LaTeX.C
the merge from branch debugstream
[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-1999 The LyX Team.
7  *
8  *           This file is Copyright 1996-1999
9  *           Lars Gullik Bjønnes
10  *
11  * ======================================================
12  */
13
14 #include <config.h>
15
16 #include <cstdio>
17 #include <cstdlib>
18
19 #ifdef __GNUG__
20 #pragma implementation
21 #endif
22
23 #include "support/filetools.h"
24 #include "LaTeX.h"
25 #include "lyxlex.h"
26 #include "support/FileInfo.h"
27 #include "debug.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"
34 #include "gettext.h"
35
36 extern BufferList bufferlist;
37
38 struct texfile_struct {
39         LaTeX::TEX_FILES file;
40         char const *extension;
41 };
42
43 static
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"},
57         { LaTeX::TEX, ".tex"}
58 };
59
60 // This should perhaps be placed in LyXLex
61 static
62 string readLine(FILE *file)
63 {
64         if (feof(file))
65                 return string();
66
67         int i = 0;
68         char s[512];
69
70         do {
71                 s[i] = fgetc(file);
72                 i++;
73         } while (!feof(file) && s[i-1] != '\n' && i<510);
74         s[i] = '\0';
75         string tmp;
76         if (i == 1 && feof(file))
77                 ;
78         else
79                 tmp = s;
80
81         return tmp;
82 }
83
84
85
86 /*
87  * CLASS TEXERRORS
88  */
89
90 // I did not leave this inlined because DEC cxx does not like
91 // variables declarations in inlined code (JMarc)
92 TeXErrors::~TeXErrors()
93 {
94         Error *tmp;
95         while (errors) {
96                 tmp = errors->next_error;
97                 delete errors;
98                 errors = tmp;
99         }
100 }
101
102
103 void TeXErrors::scanError(LyXLex &lex)
104 {
105         string token = lex.GetString();
106         // Sometimes the error string goes over more than one
107         // line, and we need to get them all.
108         string errstr;
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")) {
113                         do {
114                                 tmp = readLine(lex.getFile());
115                         } while (!tmp.empty() && !contains(tmp, "..."));
116                 }
117                 tmp = frontStrip(readLine(lex.getFile()));
118         }
119
120         while ((tmp != "\n" || !contains(errstr, "l."))
121                 && !prefixIs(tmp, "! ")
122                 && !contains(tmp, "(job aborted")
123                 && !tmp.empty()) {
124                 errstr += tmp;
125                 tmp = frontStrip(readLine(lex.getFile()));
126         }
127         lyxerr.debug() << "tmp: " << errstr << endl;
128         int line = 0;
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
132         // msg.
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);
140                                 break;
141                         }
142                 }
143         }
144         insertError(line, token, errstr);
145
146         if (prefixIs(tmp, "! ")) {
147                 scanError(lex);
148         }
149 }
150
151
152 bool TeXErrors::getFirstError(int *line, string *text)
153 {
154         next_error = errors;
155         if (next_error) {
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;
159                 return true;
160         }
161         return false;
162 }
163
164
165 bool TeXErrors::getNextError(int *line, string *text)
166 {
167         if (next_error) {
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;
171                 return true;
172         }
173         return false;
174 }
175
176
177 void TeXErrors::insertError(int line, string const &error_desc,
178                             string const &error_text)
179 {
180         Error *newerr = new Error(line, error_desc, error_text);
181         if (errors) {
182                 Error *tmperr = errors;
183                 while (tmperr->next_error) tmperr = tmperr->next_error;
184                 tmperr->next_error = newerr;
185         } else {
186                 errors = newerr;
187         }
188 }
189
190
191 void TeXErrors::printErrors()
192 {
193         lyxerr << "Printing errors." << endl;
194         if (errors) {
195                 Error *tmperr = errors;
196                 do {
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;
205                 } while (tmperr);
206         }
207 }
208
209
210 void TeXErrors::printWarnings()
211 {
212 }
213
214
215 void TeXErrors::printStatus()
216 {
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;
228 }
229
230
231 /*
232  * CLASS LaTeX
233  */
234
235 LaTeX::LaTeX(string const & latex, string const & f, string const & p)
236                 : cmd(latex), file(f), path(p)
237 {
238         tex_files = NO_FILES;
239         file_count = sizeof(all_files) / sizeof(texfile_struct);
240         num_errors = 0;
241         depfile = file + ".dep";
242 }
243
244
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.
251 {
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
258         
259         // The class LaTeX does not know the temp path.
260         bufferlist.updateIncludedTeXfiles(GetCWD());
261         
262         // Never write the depfile if an error was encountered.
263         
264         // 0
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).
269         //                 -> if changed:
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
275         //             make it and
276         //             run latex once (we need to run latex once anyway) and
277         //             remake the dependency file.
278         //
279         FileInfo fi(depfile);
280         if (fi.exist()) {
281                 // Read the dep file:
282                 head.read(depfile);
283                 // Update the checksums
284                 head.update();
285                 
286                 lyxerr[Debug::LATEX] << "Dependency file exists" << endl;
287                 if (head.sumchange()) {
288                         ++count;
289                         lyxerr[Debug::LATEX]
290                                 << "Dependency file has changed\n"
291                                 << "Run #" << count << endl; 
292                         minib->Set(string(_("LaTeX run number ")) + tostr(count));
293                         minib->Store();
294                         this->operator()();
295                         scanres = scanLogFile(terr);
296                         if (scanres & LaTeX::ERRORS) return scanres; // return on error
297                 } else {
298                         lyxerr[Debug::LATEX] << "return no_change" << endl;
299                         return LaTeX::NO_CHANGE;
300                 }
301         } else {
302                 ++count;
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));
307                 minib->Store();
308                 this->operator()();
309                 scanres = scanLogFile(terr);
310                 if (scanres & LaTeX::ERRORS) return scanres; // return on error
311         }
312
313         // update the dependencies.
314         deplog(head); // reads the latex log
315         deptex(head); // checks for latex files
316         head.update();
317
318         // 0.5
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
323         // if needed.
324         
325         // run makeindex
326         if (head.haschanged(ChangeExtension(file, ".idx", true))) {
327                 // no checks for now
328                 minib->Set(_("Running MakeIndex."));
329                 minib->Store();
330                 rerun=runMakeIndex(ChangeExtension(file,".idx",true));
331         }
332
333         // run bibtex
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;
338                 // no checks for now
339                 minib->Set(_("Running BibTeX."));
340                 minib->Store();
341                 rerun = runBibTeX(ChangeExtension(file, ".aux", true));
342         }
343         
344         // 1
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
355         //
356         if (rerun || head.sumchange()) {
357                 rerun = false;
358                 ++count;
359                 lyxerr[Debug::LATEX]
360                         << "Dep. file has changed or rerun requested\n"
361                         << "Run #" << count << endl;
362                 minib->Set(string(_("LaTeX run number ")) + tostr(count));
363                 minib->Store();
364                 this->operator()();
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
369                 head.update();
370         } else {
371                 lyxerr[Debug::LATEX] << "Dep. file has NOT changed" << endl;
372         }
373
374         // 1.5
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
380         // more after this.
381         
382         // run makeindex if the <file>.idx has changed or was generated.
383         if (head.haschanged(ChangeExtension(file, ".idx", true))) {
384                 // no checks for now
385                 minib->Set(_("Running MakeIndex."));
386                 minib->Store();
387                 rerun = runMakeIndex(ChangeExtension(file, ".idx", true));
388         }
389         
390         // 2
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:
394         //             run latex and
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)
399         //
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.
404                 rerun = false;
405                 ++count;
406                 lyxerr[Debug::LATEX] << "Run #" << count << endl;
407                 minib->Set(string(_("LaTeX run number ")) + tostr(count));
408                 minib->Store();
409                 this->operator()();
410                 scanres = scanLogFile(terr);
411                 if (scanres & LaTeX::ERRORS) return scanres; // return on error
412                 // keep this updated
413                 head.update();
414         }
415
416         // Write the dependencies to file.
417         head.write(depfile);
418         lyxerr[Debug::LATEX] << "Done." << endl;
419         return scanres;
420 }
421
422
423 int LaTeX::operator()()
424 {
425 #ifndef __EMX__
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";
429 #endif
430         Systemcalls one;
431         return one.Startscript(Systemcalls::System, tmp);
432 }
433
434
435 bool LaTeX::runMakeIndex(string const &file)
436 {
437         lyxerr[Debug::LATEX] << "idx file has been made,"
438                 " running makeindex on file "
439                              <<  file << endl;
440
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 ";
446         tmp += file;
447         Systemcalls one;
448         one.Startscript(Systemcalls::System, tmp);
449         return true;
450 }
451
452
453 bool LaTeX::runBibTeX(string const &file)
454 {
455         LyXLex lex(0, 0);
456         string token;
457         if (!lex.setFile(file)) {
458                 // unable to open .aux file
459                 // return at once
460                 return false;
461         }
462
463         while (lex.IsOK()) {
464                 if (lex.EatLine())
465                         token=lex.GetString();
466                 else // blank line in the file being read
467                         continue;
468
469                 if (contains(token, "\\bibdata{")) {
470                         // run bibtex and
471                         string tmp="bibtex ";
472                         tmp += ChangeExtension(file, string(), true);
473                         Systemcalls one;
474                         one.Startscript(Systemcalls::System, tmp);
475                         return true;
476                 }
477                 
478         }
479         // bibtex was not run.
480         return false;
481 }
482
483
484 int LaTeX::scanLogFile(TeXErrors &terr)
485 {
486         string token;
487         int retval = NO_ERRORS;
488         
489         LyXLex lex(0, 0);
490
491         string tmp = ChangeExtension(file, ".log", true);
492         
493         if (!lex.setFile(tmp)) {
494                 // unable to open file
495                 // return at once
496                 retval |= NO_LOGFILE;
497                 return retval;
498         }
499         
500         while (lex.IsOK()) {
501                 if (lex.EatLine())
502                         token = lex.GetString();
503                 else // blank line in the file being read
504                         continue;
505
506                 lyxerr[Debug::LATEX] << token << endl;
507                 
508                 if (prefixIs(token, "LaTeX Warning:")) {
509                         // Here shall we handle different
510                         // types of warnings
511                         retval |= LATEX_WARNING;
512                         lyxerr[Debug::LATEX] << "LaTeX Warning." << endl;
513                         if (contains(token, "Rerun to get cross-references")) {
514                                 retval |= RERUN;
515                                 lyxerr[Debug::LATEX]
516                                         << "We should rerun." << endl;
517                         } else if (contains(token, "Citation")
518                                    && contains(token, "on page")
519                                    && contains(token, "undefined")) {
520                                 retval |= UNDEF_CIT;
521                         }
522                 } else if (prefixIs(token, "Package")) {
523                         // Package warnings
524                         retval |= PACKAGE_WARNING;
525                         if (contains(token, "natbib Warning:")) {
526                                 // Natbib warnings
527                                 if (contains(token, "Citation")
528                                     && contains(token, "on page")
529                                     && contains(token, "undefined")) {
530                                         retval |= UNDEF_CIT;
531                                 }
532                         } else if (contains(token, "Rerun LaTeX.")) {
533                                 // at least longtable.sty might use this.
534                                 retval |= RERUN;
535                         }
536                 } else if (prefixIs(token, "! LaTeX Error:")) {
537                         // Here shall we handle different
538                         // types of errors
539                         retval |= LATEX_ERROR;
540                         lyxerr[Debug::LATEX] << "LaTeX Error." << endl;
541                         // this is not correct yet
542                         terr.scanError(lex);
543                         num_errors++;
544                 } else if (prefixIs(token, "! ")) {
545                         // Ok, we have something that looks like a TeX Error
546                         // but what do we really have.
547
548                         // Just get the error description:
549                         string desc(token);
550                         desc.erase(0, 2);
551
552                         if (contains(desc, "Undefined control sequence")) {
553                                 retval |= TEX_ERROR;
554                                 lyxerr[Debug::LATEX] << "TeX Error." << endl;
555                                 terr.scanError(lex);
556                                 num_errors++;
557                         } else {
558                                 // get the next line
559                                 lex.next();
560                                 string tmp = lex.GetString();
561                                 if (prefixIs(tmp, "l.")) {
562                                 // we have a latex error
563                                         retval |=  TEX_ERROR;
564                                         lyxerr[Debug::LATEX]
565                                                 <<"TeX Error." << endl;
566                                 // get the line number:
567                                         int line = 0;
568                                         sscanf(tmp.c_str(), "l.%d", &line);
569                                 // get the rest of the message:
570                                         string errstr;
571                                         lex.EatLine();
572                                         tmp = lex.GetString();
573                                         while ((tmp != "\n" || !contains(errstr, "l."))
574                                                && !prefixIs(tmp, "! ")
575                                                && !contains(tmp, "(job aborted")
576                                                && !tmp.empty()) {
577                                                 errstr += tmp;
578                                                 errstr += "\n";
579                                                 lex.EatLine();
580                                                 tmp = lex.GetString();
581                                         }
582                                         terr.insertError(line, desc, errstr);
583                                         num_errors++;
584                                 }
585                         }
586                 } else {
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.
595                                 retval |= RERUN;
596                         } else if (contains(token, "No pages of output")) {
597                                 // A dvi file was not created
598                                 retval |= NO_OUTPUT;
599                         } else if (contains(token, "That makes 100 errors")) {
600                                 // More than 100 errors were reprted
601                                 retval |= TOO_MANY_ERRORS;
602                         }
603                 }
604         }       
605         return retval;
606 }
607
608
609 void LaTeX::deplog(DepTable & head)
610 {
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
613         // dependency file.
614
615         string logfile = ChangeExtension(file, ".log", true);
616         FilePtr in(logfile, FilePtr::read);
617         bool not_eof = true;
618         if (in()) while (not_eof) { // We were able to open the file
619                 // Now we read chars until we find a '('
620                 int c;
621                 do {
622                         c = fgetc(in());
623                 } while (c != EOF && c != '(');
624                 if (c == EOF) { 
625                         // Nothing more we can do
626                         not_eof = false; 
627                         continue;
628                 } 
629
630                 // We now have c == '(', we now read the the sequence of
631                 // chars until reaching EOL, or ' ' and put that into a string.
632
633                 string foundfile;
634                 c = fgetc(in());
635                 while (c != '\n' && c != ' ' && c != ')') {
636                         foundfile += char(c);
637                         c = fgetc(in());
638                 }
639                 if (foundfile.empty()) continue;
640
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
647                 // paths:
648                 // (1) foundfile is an
649                 //     absolute path and should
650                 //     be inserted.
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
656                         // the latex run.
657                         head.insert(foundfile, true);
658                         continue;
659                 }
660
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"
668                                                      << endl;
669                         } else if (suffixIs(foundfile, ".tex")) {
670                                 // This is a tex file generated by LyX
671                                 // and latex is not likely to change this
672                                 // during its runs.
673                                 lyxerr[Debug::LATEX] << "Tmpdir TeX file: "
674                                                      << OnlyFilename(foundfile)
675                                                      << endl;
676                                 head.insert(foundfile, true);
677                         } else {
678                                 lyxerr[Debug::LATEX] << "In tmpdir file:"
679                                                      << OnlyFilename(foundfile)
680                                                      << endl;
681                                 head.insert(OnlyFilename(foundfile));
682                         }
683                         continue;
684                 }
685
686                 // (3) the foundfile can be
687                 //     found in the same dir
688                 //     as the .lyx file and
689                 //     should be inserted.
690                 PathPush(path);
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."
695                                << endl;
696                         lyxerr[Debug::LATEX] << "Same Directory file: " 
697                                              << foundfile << endl;
698                         head.insert(foundfile);
699                         PathPop();
700                         continue;
701                 }
702                 PathPop();
703                 
704                 lyxerr[Debug::LATEX]
705                         << "Not a file or we are unable to find it."
706                         << endl;
707         }
708 }
709
710
711 void LaTeX::deptex(DepTable &head)
712 {
713         int except = AUX|LOG|DVI|BBL|IND|GLO; 
714         string tmp;
715         FileInfo fi;
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,
720                                               true);
721                         lyxerr[Debug::LATEX] << "deptex: " << tmp << endl;
722                         if (fi.newFile(tmp).exist())
723                                 head.insert(tmp);
724                 }
725         }
726 }