]> git.lyx.org Git - lyx.git/blob - src/LaTeX.C
merge from the string-switch branch and ready for a prelease.
[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 "error.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);
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.print("Printing errors.");
194         if (errors) {
195                 Error *tmperr = errors;
196                 do {
197                         lyxerr.print(string("Error in line ")
198                                      + tostr(tmperr->error_in_line)
199                                      + ": " + tmperr->error_desc
200                                      + '\n' + tmperr->error_text);
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.print("Error struct:");
218         lyxerr.print(string("   status: ") + tostr(status));
219         lyxerr.print(string("   no err: ") + tostr(number_of_errors));
220         if (status == LaTeX::NO_ERRORS)  lyxerr.print("NO_ERRORS");
221         if (status & LaTeX::NO_LOGFILE)  lyxerr.print("NO_LOGFILE");
222         if (status & LaTeX::NO_OUTPUT)   lyxerr.print("NO_OUTPUT");
223         if (status & LaTeX::UNDEF_REF)   lyxerr. print("UNDEF_REF");
224         if (status & LaTeX::RERUN)       lyxerr. print("RERUN");
225         if (status & LaTeX::TEX_ERROR)   lyxerr.print("TEX_ERROR");
226         if (status & LaTeX::TEX_WARNING) lyxerr.print("TEX_WARNING");
227         if (status & LaTeX::NO_FILE)     lyxerr.print("NO_FILE");
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("Dependency file exists", Error::LATEX);
287                 if (head.sumchange()) {
288                         lyxerr.debug("Dependency file has changed", 
289                                      Error::LATEX);
290                         lyxerr.debug(string(_("Run #")) + tostr(++count), 
291                                      Error::LATEX);
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("return no_change", Error::LATEX);
299                         return LaTeX::NO_CHANGE;
300                 }
301         } else {
302                 lyxerr.debug("Dependency file does not exist",
303                              Error::LATEX);
304                 lyxerr.debug(string(_("Run #")) + tostr(++count),
305                              Error::LATEX); 
306                 head.insert(file, true);
307                 minib->Set(string(_("LaTeX run number ")) + tostr(count));
308                 minib->Store();
309                 this->operator()();
310                 scanres = scanLogFile(terr);
311                 if (scanres & LaTeX::ERRORS) return scanres; // return on error
312         }
313
314         // update the dependencies.
315         deplog(head); // reads the latex log
316         deptex(head); // checks for latex files
317         head.update();
318
319         // 0.5
320         // At this point we must run external programs if needed.
321         // makeindex will be run if a .idx file changed or was generated.
322         // And if there were undefined citations or changes in references
323         // the .aux file is checked for signs of bibtex. Bibtex is then run
324         // if needed.
325         
326         // run makeindex
327         if (head.haschanged(ChangeExtension(file, ".idx", true))) {
328                 // no checks for now
329                 minib->Set(_("Running MakeIndex."));
330                 minib->Store();
331                 rerun=runMakeIndex(ChangeExtension(file,".idx",true));
332         }
333
334         // run bibtex
335         if (scanres & LaTeX::UNDEF_CIT || scanres & LaTeX::RERUN) {
336                 // Here we must scan the .aux file and look for
337                 // "\bibdata" and/or "\bibstyle". If one of those
338                 // tags is found -> run bibtex and set rerun = true;
339                 // no checks for now
340                 minib->Set(_("Running BibTeX."));
341                 minib->Store();
342                 rerun = runBibTeX(ChangeExtension(file, ".aux", true));
343         }
344         
345         // 1
346         // we know on this point that latex has been run once (or we just
347         // returned) and the question now is to decide if we need to run
348         // it any more. This is done by asking if any of the files in the
349         // dependency file has changed. (remember that the checksum for
350         // a given file is reported to have changed if it just was created)
351         //     -> if changed or rerun == true:
352         //             run latex once more and
353         //             update the dependency structure
354         //     -> if not changed:
355         //             we does nothing at this point
356         //
357         if (rerun || head.sumchange()) {
358                 rerun = false;
359                 lyxerr.debug("Dep. file has changed or rerun requested", 
360                              Error::LATEX);
361                 lyxerr.debug(string("Run #") + tostr(++count),
362                              Error::LATEX);
363                 minib->Set(string(_("LaTeX run number ")) + tostr(count));
364                 minib->Store();
365                 this->operator()();
366                 scanres = scanLogFile(terr);
367                 if (scanres & LaTeX::ERRORS) return scanres; // return on error
368                 // update the depedencies
369                 deplog(head); // reads the latex log
370                 head.update();
371         } else {
372                 lyxerr.debug("Dep. file has NOT changed", Error::LATEX);
373         }
374
375         // 1.5
376         // The inclusion of files generated by external programs like
377         // makeindex or bibtex might have done changes to pagenumbereing,
378         // etc. And because of this we must run the external programs
379         // again to make sure everything is redone correctly.
380         // Also there should be no need to run the external programs any
381         // more after this.
382         
383         // run makeindex if the <file>.idx has changed or was generated.
384         if (head.haschanged(ChangeExtension(file, ".idx", true))) {
385                 // no checks for now
386                 minib->Set(_("Running MakeIndex."));
387                 minib->Store();
388                 rerun = runMakeIndex(ChangeExtension(file, ".idx", true));
389         }
390         
391         // 2
392         // we will only run latex more if the log file asks for it.
393         // or if the sumchange() is true.
394         //     -> rerun asked for:
395         //             run latex and
396         //             remake the dependency file
397         //             goto 2 or return if max runs are reached.
398         //     -> rerun not asked for:
399         //             just return (fall out of bottom of func)
400         //
401         while ((head.sumchange() || rerun || (scanres & LaTeX::RERUN)) 
402                && count < MAX_RUN) {
403                 // Yes rerun until message goes away, or until
404                 // MAX_RUNS are reached.
405                 rerun = false;
406                 lyxerr.debug(string(_("Run #")) + tostr(++count), Error::LATEX);
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("Done.", Error::LATEX);
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("idx file has been made,"
438                       " running makeindex on file "
439                       + file, Error::LATEX);
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(token, Error::LATEX);
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 Warning.", Error::LATEX);
513                         if (contains(token, "Rerun to get cross-references")) {
514                                 retval |= RERUN;
515                                 lyxerr.debug("We should rerun.", Error::LATEX);
516                         } else if (contains(token, "Citation")
517                                    && contains(token, "on page")
518                                    && contains(token, "undefined")) {
519                                 retval |= UNDEF_CIT;
520                         }
521                 } else if (prefixIs(token, "Package")) {
522                         // Package warnings
523                         retval |= PACKAGE_WARNING;
524                         if (contains(token, "natbib Warning:")) {
525                                 // Natbib warnings
526                                 if (contains(token, "Citation")
527                                     && contains(token, "on page")
528                                     && contains(token, "undefined")) {
529                                         retval |= UNDEF_CIT;
530                                 }
531                         } else if (contains(token, "Rerun LaTeX.")) {
532                                 // at least longtable.sty might use this.
533                                 retval |= RERUN;
534                         }
535                 } else if (prefixIs(token, "! LaTeX Error:")) {
536                         // Here shall we handle different
537                         // types of errors
538                         retval |= LATEX_ERROR;
539                         lyxerr.debug("LaTeX Error.", Error::LATEX);
540                         // this is not correct yet
541                         terr.scanError(lex);
542                         num_errors++;
543                 } else if (prefixIs(token, "! ")) {
544                         // Ok, we have something that looks like a TeX Error
545                         // but what do we really have.
546
547                         // Just get the error description:
548                         string desc(token);
549                         desc.erase(0, 2);
550
551                         if (contains(desc, "Undefined control sequence")) {
552                                 retval |= TEX_ERROR;
553                                 lyxerr.debug("TeX Error.", Error::LATEX);
554                                 terr.scanError(lex);
555                                 num_errors++;
556                         } else {
557                                 // get the next line
558                                 lex.next();
559                                 string tmp = lex.GetString();
560                                 if (prefixIs(tmp, "l.")) {
561                                 // we have a latex error
562                                         retval |=  TEX_ERROR;
563                                         lyxerr.debug("TeX Error.", Error::LATEX);
564                                 // get the line number:
565                                         int line = 0;
566                                         sscanf(tmp.c_str(), "l.%d", &line);
567                                 // get the rest of the message:
568                                         string errstr;
569                                         lex.EatLine();
570                                         tmp = lex.GetString();
571                                         while ((tmp != "\n" || !contains(errstr, "l."))
572                                                && !prefixIs(tmp, "! ")
573                                                && !contains(tmp, "(job aborted")
574                                                && !tmp.empty()) {
575                                                 errstr += tmp;
576                                                 errstr += "\n";
577                                                 lex.EatLine();
578                                                 tmp = lex.GetString();
579                                         }
580                                         terr.insertError(line, desc, errstr);
581                                         num_errors++;
582                                 }
583                         }
584                 } else {
585                         // information messages, TeX warnings and other
586                         // warnings we have not caught earlier.
587                         if (prefixIs(token, "Overfull ")) {
588                                 retval |= TEX_WARNING;
589                         } else if (prefixIs(token, "Underfull ")) {
590                                 retval |= TEX_WARNING;
591                         } else if (contains(token, "Rerun to get citations")) {
592                                 // Natbib seems to use this.
593                                 retval |= RERUN;
594                         } else if (contains(token, "No pages of output")) {
595                                 // A dvi file was not created
596                                 retval |= NO_OUTPUT;
597                         } else if (contains(token, "That makes 100 errors")) {
598                                 // More than 100 errors were reprted
599                                 retval |= TOO_MANY_ERRORS;
600                         }
601                 }
602         }       
603         return retval;
604 }
605
606
607 void LaTeX::deplog(DepTable & head)
608 {
609         // This function reads the LaTeX log file end extracts all the external
610         // files used by the LaTeX run. The files are then entered into the
611         // dependency file.
612
613         string logfile = ChangeExtension(file, ".log", true);
614         FilePtr in(logfile, FilePtr::read);
615         bool not_eof = true;
616         if (in()) while (not_eof) { // We were able to open the file
617                 // Now we read chars until we find a '('
618                 int c;
619                 do {
620                         c = fgetc(in());
621                 } while (c != EOF && c != '(');
622                 if (c == EOF) { 
623                         // Nothing more we can do
624                         not_eof = false; 
625                         continue;
626                 } 
627
628                 // We now have c == '(', we now read the the sequence of
629                 // chars until reaching EOL, or ' ' and put that into a string.
630
631                 string foundfile;
632                 c = fgetc(in());
633                 while (c != '\n' && c != ' ' && c != ')') {
634                         foundfile += char(c);
635                         c = fgetc(in());
636                 }
637                 if (foundfile.empty()) continue;
638
639                 lyxerr.debug("Found file: " 
640                              + foundfile,
641                              Error::LATEX);
642                 // Ok now we found a file.
643                 // Now we should make sure that
644                 // this is a file that we can
645                 // access through the normal
646                 // paths:
647                 // (1) foundfile is an
648                 //     absolute path and should
649                 //     be inserted.
650                 if (AbsolutePath(foundfile)) {
651                         lyxerr.debug("AbsolutePath file: " 
652                                      + foundfile,
653                                      Error::LATEX);
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("We don't want "
666                                              + OnlyFilename(foundfile)
667                                              + " in the dep file",
668                                              Error::LATEX);
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("Tmpdir TeX file: "
674                                              + OnlyFilename(foundfile),
675                                              Error::LATEX);
676                                 head.insert(foundfile, true);
677                         } else {
678                                 lyxerr.debug("In tmpdir file:"
679                                              + OnlyFilename(foundfile),
680                                              Error::LATEX);
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.print("LyX Strange: this should actually never"
693                                      " happen anymore, this it should be"
694                                      " handled by the Absolute check.");
695                         lyxerr.debug("Same Directory file: " 
696                                      + foundfile,
697                                      Error::LATEX);
698                         head.insert(foundfile);
699                         PathPop();
700                         continue;
701                 }
702                 PathPop();
703                 
704                 lyxerr.debug("Not a file or we are unable to find it.",
705                              Error::LATEX);
706
707
708         }
709 }
710
711
712 void LaTeX::deptex(DepTable &head)
713 {
714         int except = AUX|LOG|DVI|BBL|IND|GLO; 
715         string tmp;
716         FileInfo fi;
717         for (int i = 0; i < file_count; i++) {
718                 if (!(all_files[i].file & except)) {
719                         tmp = ChangeExtension(file,
720                                               all_files[i].extension,
721                                               true);
722                         lyxerr.debug("deptex: " + tmp, Error::LATEX);
723                         if (fi.newFile(tmp).exist())
724                                 head.insert(tmp);
725                 }
726         }
727 }