]> git.lyx.org Git - lyx.git/blob - src/LaTeX.C
69ce0b6dc73b38ddefba1925d604fcadbaa4c3e3
[lyx.git] / src / LaTeX.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor         
5  *           Copyright (C) 1995 Matthias Ettrich
6  *           Copyright (C) 1995-1999 The LyX Team.
7  *
8  *           This file is Copyright (C) 1996-1999
9  *           Lars Gullik Bjønnes
10  *
11  *======================================================
12  */
13
14 #include <config.h>
15
16 #include <stdio.h>
17 #include <stdlib.h>
18
19 #ifdef __GNUG__
20 #pragma implementation
21 #endif
22
23 #include "filetools.h"
24 #include "LaTeX.h"
25 #include "lyxlex.h"
26 #include "FileInfo.h"
27 #include "error.h"
28 #include "lyxlib.h"
29 #include "syscall.h"
30 #include "syscontr.h"
31 #include "pathstack.h"
32 #include "bufferlist.h"
33 #include "minibuffer.h"
34 #include "gettext.h"
35
36 //      $Id: LaTeX.C,v 1.1 1999/09/27 18:44:36 larsbj Exp $     
37
38 #if !defined(lint) && !defined(WITH_WARNINGS)
39 static char vcid[] = "$Id: LaTeX.C,v 1.1 1999/09/27 18:44:36 larsbj Exp $";
40 #endif /* lint */
41
42 extern BufferList bufferlist;
43
44 struct texfile_struct {
45         LaTeX::TEX_FILES file;
46         char const *extension;
47 };
48
49 static
50 const texfile_struct all_files[] = {
51         { LaTeX::AUX, ".aux"},
52         { LaTeX::BBL, ".bbl"},
53         { LaTeX::DVI, ".dvi"},
54         { LaTeX::GLO, ".glo"},
55         { LaTeX::IDX, ".idx"},
56         { LaTeX::IND, ".ind"},
57         { LaTeX::LOF, ".lof"},
58         { LaTeX::LOA, ".loa"},
59         { LaTeX::LOG, ".log"},
60         { LaTeX::LOT, ".lot"},
61         { LaTeX::TOC, ".toc"},
62         { LaTeX::LTX, ".ltx"},
63         { LaTeX::TEX, ".tex"}
64 };
65
66 // This should perhaps be placed in LyXLex
67 static
68 LString readLine(FILE *file)
69 {
70         if (feof(file))
71                 return LString();
72
73         int i = 0;
74         char s[512];
75
76         do {
77                 s[i] = fgetc(file);
78                 i++;
79         } while (!feof(file) && s[i-1] != '\n' && i<510);
80         s[i] = '\0';
81         LString tmp;
82         if (i == 1 && feof(file))
83                 ;
84         else
85                 tmp = s;
86
87         return tmp;
88 }
89
90
91
92 /*
93  * CLASS TEXERRORS
94  */
95
96 // I did not leave this inlined because DEC cxx does not like
97 // variables declarations in inlined code (JMarc)
98 TeXErrors::~TeXErrors()
99 {
100         Error *tmp;
101         while (errors) {
102                 tmp = errors->next_error;
103                 delete errors;
104                 errors = tmp;
105         }
106 }
107
108
109 void TeXErrors::scanError(LyXLex &lex)
110 {
111         LString token = lex.GetString();
112         // Sometimes the error string goes over more than one
113         // line, and we need to get them all.
114         LString errstr;
115         LString tmp = readLine(lex.getFile()).frontStrip();
116         if (tmp == "\n" || tmp.empty()) {
117                 tmp = readLine(lex.getFile()).frontStrip();
118                 if (tmp.contains("See the LaTeX manual")) {
119                         do {
120                                 tmp = readLine(lex.getFile());
121                         } while (!tmp.empty() && !tmp.contains("..."));
122                 }
123                 tmp = readLine(lex.getFile()).frontStrip();
124         }
125
126         while ((tmp != "\n" || !errstr.contains("l."))
127                 && !tmp.prefixIs("! ")
128                 && !tmp.contains("(job aborted")
129                 && !tmp.empty()) {
130                 errstr += tmp;
131                 tmp = readLine(lex.getFile()).frontStrip();
132         }
133         lyxerr.debug("tmp: " + errstr);
134         int line = 0;
135         // unfortunately the error line is not always given
136         // by "l.###" in the beginning of the error string
137         // therefore we must search for "l.###" in the error
138         // msg.
139         if (errstr.contains("l.")) {
140                 // We make a const copy to make [] fast. (Asger)
141                 LString const es = errstr;
142                 for (int i = 2; i < es.length(); i++) {
143                         if (es[i-2] == 'l' && es[i-1] == '.' &&
144                             (es[i] >= '0' && es[i]<= '9')) {
145                                 line = atoi(es.c_str() + i);
146                                 break;
147                         }
148                 }
149         }
150         insertError(line, token, errstr);
151
152         if (tmp.prefixIs("! ")) {
153                 scanError(lex);
154         }
155 }
156
157
158 bool TeXErrors::getFirstError(int *line, LString *text)
159 {
160         next_error = errors;
161         if (next_error) {
162                 *line = next_error->error_in_line;
163                 *text = next_error->error_desc + "\n" + next_error->error_text;
164                 next_error = next_error->next_error;
165                 return true;
166         }
167         return false;
168 }
169
170
171 bool TeXErrors::getNextError(int *line, LString *text)
172 {
173         if (next_error) {
174                 *line = next_error->error_in_line;
175                 *text = next_error->error_desc + "\n" + next_error->error_text;
176                 next_error = next_error->next_error;
177                 return true;
178         }
179         return false;
180 }
181
182
183 void TeXErrors::insertError(int line, LString const &error_desc,
184                             LString const &error_text)
185 {
186         Error *newerr = new Error(line, error_desc, error_text);
187         if (errors) {
188                 Error *tmperr = errors;
189                 while (tmperr->next_error) tmperr = tmperr->next_error;
190                 tmperr->next_error = newerr;
191         } else {
192                 errors = newerr;
193         }
194 }
195
196
197 void TeXErrors::printErrors()
198 {
199         lyxerr.print("Printing errors.");
200         if (errors) {
201                 Error *tmperr = errors;
202                 do {
203                         lyxerr.print(LString("Error in line ")
204                                      + tmperr->error_in_line
205                                      + ": " + tmperr->error_desc
206                                      + '\n' + tmperr->error_text);
207                         //%d: %s\n%s\n", tmperr->error_in_line,
208                         //     tmperr->error_desc.c_str(),
209                         //     tmperr->error_text.c_str());
210                         tmperr = tmperr->next_error;
211                 } while (tmperr);
212         }
213 }
214
215
216 void TeXErrors::printWarnings()
217 {
218 }
219
220
221 void TeXErrors::printStatus()
222 {
223         lyxerr.print("Error struct:");
224         lyxerr.print(LString("   status: ") + int(status));
225         lyxerr.print(LString("   no err: ") + int(number_of_errors));
226         if (status == LaTeX::NO_ERRORS)  lyxerr.print("NO_ERRORS");
227         if (status & LaTeX::NO_LOGFILE)  lyxerr.print("NO_LOGFILE");
228         if (status & LaTeX::NO_OUTPUT)   lyxerr.print("NO_OUTPUT");
229         if (status & LaTeX::UNDEF_REF)   lyxerr. print("UNDEF_REF");
230         if (status & LaTeX::RERUN)       lyxerr. print("RERUN");
231         if (status & LaTeX::TEX_ERROR)   lyxerr.print("TEX_ERROR");
232         if (status & LaTeX::TEX_WARNING) lyxerr.print("TEX_WARNING");
233         if (status & LaTeX::NO_FILE)     lyxerr.print("NO_FILE");
234 }
235
236
237 /*
238  * CLASS LaTeX
239  */
240
241 LaTeX::LaTeX(LString const & latex, LString const & f, LString const & p)
242                 : cmd(latex), file(f), path(p)
243 {
244         tex_files = NO_FILES;
245         file_count = sizeof(all_files) / sizeof(texfile_struct);
246         num_errors = 0;
247         depfile = file + ".dep";
248 }
249
250
251 int LaTeX::run(TeXErrors &terr, MiniBuffer *minib)
252         // We know that this function will only be run if the lyx buffer
253         // has been changed. We also know that a newly written .tex file
254         // is always different from the previous one because of the date
255         // in it. However it seems safe to run latex (at least) on time each
256         // time the .tex file changes.
257 {
258         int scanres = LaTeX::NO_ERRORS;
259         unsigned int count = 0; // number of times run
260         num_errors = 0; // just to make sure.
261         const unsigned int MAX_RUN = 6;
262         DepTable head; // empty head
263         bool rerun = false; // rerun requested
264         
265         // The class LaTeX does not know the temp path.
266         bufferlist.updateIncludedTeXfiles(GetCWD());
267         
268         // Never write the depfile if an error was encountered.
269         
270         // 0
271         // first check if the file dependencies exist:
272         //     ->If it does exist
273         //             check if any of the files mentioned in it have
274         //             changed (done using a checksum).
275         //                 -> if changed:
276         //                        run latex once and
277         //                        remake the dependency file
278         //                 -> if not changed:
279         //                        just return there is nothing to do for us.
280         //     ->if it doesn't exist
281         //             make it and
282         //             run latex once (we need to run latex once anyway) and
283         //             remake the dependency file.
284         //
285         FileInfo fi(depfile);
286         if (fi.exist()) {
287                 // Read the dep file:
288                 head.read(depfile);
289                 // Update the checksums
290                 head.update();
291                 
292                 lyxerr.debug("Dependency file exists", Error::LATEX);
293                 if (head.sumchange()) {
294                         lyxerr.debug("Dependency file has changed", 
295                                      Error::LATEX);
296                         lyxerr.debug(LString(_("Run #")) + int(++count), 
297                                      Error::LATEX);
298                         minib->Set(LString(_("LaTeX run number ")) + int(count));
299                         minib->Store();
300                         this->operator()();
301                         scanres = scanLogFile(terr);
302                         if (scanres & LaTeX::ERRORS) return scanres; // return on error
303                 } else {
304                         lyxerr.debug("return no_change", Error::LATEX);
305                         return LaTeX::NO_CHANGE;
306                 }
307         } else {
308                 lyxerr.debug("Dependency file does not exist",
309                              Error::LATEX);
310                 lyxerr.debug(LString(_("Run #")) + int(++count),
311                              Error::LATEX); 
312                 head.insert(file, true);
313                 minib->Set(LString(_("LaTeX run number ")) + int(count));
314                 minib->Store();
315                 this->operator()();
316                 scanres = scanLogFile(terr);
317                 if (scanres & LaTeX::ERRORS) return scanres; // return on error
318         }
319
320         // update the dependencies.
321         deplog(head); // reads the latex log
322         deptex(head); // checks for latex files
323         head.update();
324
325         // 0.5
326         // At this point we must run external programs if needed.
327         // makeindex will be run if a .idx file changed or was generated.
328         // And if there were undefined citations or changes in references
329         // the .aux file is checked for signs of bibtex. Bibtex is then run
330         // if needed.
331         
332         // run makeindex
333         if (head.haschanged(ChangeExtension(file, ".idx", true))) {
334                 // no checks for now
335                 minib->Set(_("Running MakeIndex."));
336                 minib->Store();
337                 rerun=runMakeIndex(ChangeExtension(file,".idx",true));
338         }
339
340         // run bibtex
341         if (scanres & LaTeX::UNDEF_CIT || scanres & LaTeX::RERUN) {
342                 // Here we must scan the .aux file and look for
343                 // "\bibdata" and/or "\bibstyle". If one of those
344                 // tags is found -> run bibtex and set rerun = true;
345                 // no checks for now
346                 minib->Set(_("Running BibTeX."));
347                 minib->Store();
348                 rerun = runBibTeX(ChangeExtension(file, ".aux", true));
349         }
350         
351         // 1
352         // we know on this point that latex has been run once (or we just
353         // returned) and the question now is to decide if we need to run
354         // it any more. This is done by asking if any of the files in the
355         // dependency file has changed. (remember that the checksum for
356         // a given file is reported to have changed if it just was created)
357         //     -> if changed or rerun == true:
358         //             run latex once more and
359         //             update the dependency structure
360         //     -> if not changed:
361         //             we does nothing at this point
362         //
363         if (rerun || head.sumchange()) {
364                 rerun = false;
365                 lyxerr.debug("Dep. file has changed or rerun requested", 
366                              Error::LATEX);
367                 lyxerr.debug(LString("Run #") + int(++count),
368                              Error::LATEX);
369                 minib->Set(LString(_("LaTeX run number ")) + int(count));
370                 minib->Store();
371                 this->operator()();
372                 scanres = scanLogFile(terr);
373                 if (scanres & LaTeX::ERRORS) return scanres; // return on error
374                 // update the depedencies
375                 deplog(head); // reads the latex log
376                 head.update();
377         } else {
378                 lyxerr.debug("Dep. file has NOT changed", Error::LATEX);
379         }
380
381         // 1.5
382         // The inclusion of files generated by external programs like
383         // makeindex or bibtex might have done changes to pagenumbereing,
384         // etc. And because of this we must run the external programs
385         // again to make sure everything is redone correctly.
386         // Also there should be no need to run the external programs any
387         // more after this.
388         
389         // run makeindex if the <file>.idx has changed or was generated.
390         if (head.haschanged(ChangeExtension(file, ".idx", true))) {
391                 // no checks for now
392                 minib->Set(_("Running MakeIndex."));
393                 minib->Store();
394                 rerun = runMakeIndex(ChangeExtension(file, ".idx", true));
395         }
396         
397         // 2
398         // we will only run latex more if the log file asks for it.
399         // or if the sumchange() is true.
400         //     -> rerun asked for:
401         //             run latex and
402         //             remake the dependency file
403         //             goto 2 or return if max runs are reached.
404         //     -> rerun not asked for:
405         //             just return (fall out of bottom of func)
406         //
407         while ((head.sumchange() || rerun || (scanres & LaTeX::RERUN)) 
408                && count < MAX_RUN) {
409                 // Yes rerun until message goes away, or until
410                 // MAX_RUNS are reached.
411                 rerun = false;
412                 lyxerr.debug(LString(_("Run #")) + int(++count), Error::LATEX);
413                 minib->Set(LString(_("LaTeX run number ")) + int(count));
414                 minib->Store();
415                 this->operator()();
416                 scanres = scanLogFile(terr);
417                 if (scanres & LaTeX::ERRORS) return scanres; // return on error
418                 // keep this updated
419                 head.update();
420         }
421
422         // Write the dependencies to file.
423         head.write(depfile);
424         lyxerr.debug("Done.", Error::LATEX);
425         return scanres;
426 }
427
428
429 int LaTeX::operator()()
430 {
431 #ifndef __EMX__
432         LString tmp = cmd + ' ' + file + " > /dev/null";
433 #else // cmd.exe (OS/2) causes SYS0003 error at "/dev/null"
434         LString tmp = cmd + ' ' + file + " > nul";
435 #endif
436         Systemcalls one;
437         return one.Startscript(Systemcalls::System, tmp);
438 }
439
440
441 bool LaTeX::runMakeIndex(LString const &file)
442 {
443         lyxerr.debug("idx file has been made,"
444                       " running makeindex on file "
445                       + file, Error::LATEX);
446
447         // It should be possible to set the switches for makeindex
448         // sorting style and such. It would also be very convenient
449         // to be able to make style files from within LyX. This has
450         // to come for a later time. (0.13 perhaps?)
451         LString tmp = "makeindex -c -q ";
452         tmp += file;
453         Systemcalls one;
454         one.Startscript(Systemcalls::System, tmp);
455         return true;
456 }
457
458
459 bool LaTeX::runBibTeX(LString const &file)
460 {
461         LyXLex lex(NULL, 0);
462         LString token;
463         if (!lex.setFile(file)) {
464                 // unable to open .aux file
465                 // return at once
466                 return false;
467         }
468
469         while (lex.IsOK()) {
470                 if (lex.EatLine())
471                         token=lex.GetString();
472                 else // blank line in the file being read
473                         continue;
474
475                 if (token.contains("\\bibdata{")) {
476                         // run bibtex and
477                         LString tmp="bibtex ";
478                         tmp += ChangeExtension(file, LString(), true);
479                         Systemcalls one;
480                         one.Startscript(Systemcalls::System, tmp);
481                         return true;
482                 }
483                 
484         }
485         // bibtex was not run.
486         return false;
487 }
488
489
490 int LaTeX::scanLogFile(TeXErrors &terr)
491 {
492         LString token;
493         int retval = NO_ERRORS;
494         
495         LyXLex lex(NULL, 0);
496
497         LString tmp = ChangeExtension(file, ".log", true);
498         
499         if (!lex.setFile(tmp)) {
500                 // unable to open file
501                 // return at once
502                 retval |= NO_LOGFILE;
503                 return retval;
504         }
505         
506         while (lex.IsOK()) {
507                 if (lex.EatLine())
508                         token = lex.GetString();
509                 else // blank line in the file being read
510                         continue;
511
512                 lyxerr.debug(token, Error::LATEX);
513                 
514                 if (token.prefixIs("LaTeX Warning:")) {
515                         // Here shall we handle different
516                         // types of warnings
517                         retval |= LATEX_WARNING;
518                         lyxerr.debug("LaTeX Warning.", Error::LATEX);
519                         if (token.contains("Rerun to get cross-references")) {
520                                 retval |= RERUN;
521                                 lyxerr.debug("We should rerun.", Error::LATEX);
522                         } else if (token.contains("Citation")
523                                    && token.contains("on page")
524                                    && token.contains("undefined")) {
525                                 retval |= UNDEF_CIT;
526                         }
527                 } else if (token.prefixIs("Package")) {
528                         // Package warnings
529                         retval |= PACKAGE_WARNING;
530                         if (token.contains("natbib Warning:")) {
531                                 // Natbib warnings
532                                 if (token.contains("Citation")
533                                     && token.contains("on page")
534                                     && token.contains("undefined")) {
535                                         retval |= UNDEF_CIT;
536                                 }
537                         } else if (token.contains("Rerun LaTeX.")) {
538                                 // at least longtable.sty might use this.
539                                 retval |= RERUN;
540                         }
541                 } else if (token.prefixIs("! LaTeX Error:")) {
542                         // Here shall we handle different
543                         // types of errors
544                         retval |= LATEX_ERROR;
545                         lyxerr.debug("LaTeX Error.", Error::LATEX);
546                         // this is not correct yet
547                         terr.scanError(lex);
548                         num_errors++;
549                 } else if (token.prefixIs("! ")) {
550                         // Ok, we have something that looks like a TeX Error
551                         // but what do we really have.
552
553                         // Just get the error description:
554                         LString desc(token);
555                         desc.substring(2, desc.length() - 1);
556
557                         if (desc.contains("Undefined control sequence")) {
558                                 retval |= TEX_ERROR;
559                                 lyxerr.debug("TeX Error.", Error::LATEX);
560                                 terr.scanError(lex);
561                                 num_errors++;
562                         } else {
563                                 // get the next line
564                                 lex.next();
565                                 LString tmp = lex.GetString();
566                                 if (tmp.prefixIs("l.")) {
567                                 // we have a latex error
568                                         retval |=  TEX_ERROR;
569                                         lyxerr.debug("TeX Error.", Error::LATEX);
570                                 // get the line number:
571                                         int line = 0;
572                                         sscanf(tmp.c_str(), "l.%d", &line);
573                                 // get the rest of the message:
574                                         LString errstr;
575                                         lex.EatLine();
576                                         tmp = lex.GetString();
577                                         while ((tmp != "\n" || !errstr.contains("l."))
578                                                && !tmp.prefixIs("! ")
579                                                && !tmp.contains("(job aborted")
580                                                && !tmp.empty()) {
581                                                 errstr += tmp;
582                                                 errstr += "\n";
583                                                 lex.EatLine();
584                                                 tmp = lex.GetString();
585                                         }
586                                         terr.insertError(line, desc, errstr);
587                                         num_errors++;
588                                 }
589                         }
590                 } else {
591                         // information messages, TeX warnings and other
592                         // warnings we have not caught earlier.
593                         if (token.prefixIs("Overfull ")) {
594                                 retval |= TEX_WARNING;
595                         } else if (token.prefixIs("Underfull ")) {
596                                 retval |= TEX_WARNING;
597                         } else if (token.contains("Rerun to get citations")) {
598                                 // Natbib seems to use this.
599                                 retval |= RERUN;
600                         } else if (token.contains("No pages of output")) {
601                                 // A dvi file was not created
602                                 retval |= NO_OUTPUT;
603                         } else if (token.contains("That makes 100 errors")) {
604                                 // More than 100 errors were reprted
605                                 retval |= TOO_MANY_ERRORS;
606                         }
607                 }
608         }       
609         return retval;
610 }
611
612
613 void LaTeX::deplog(DepTable & head)
614 {
615         // This function reads the LaTeX log file end extracts all the external
616         // files used by the LaTeX run. The files are then entered into the
617         // dependency file.
618
619         LString logfile = ChangeExtension(file, ".log", true);
620         FilePtr in(logfile, FilePtr::read);
621         bool not_eof = true;
622         if (in()) while (not_eof) { // We were able to open the file
623                 // Now we read chars until we find a '('
624                 int c;
625                 do {
626                         c = fgetc(in());
627                 } while (c != EOF && c != '(');
628                 if (c == EOF) { 
629                         // Nothing more we can do
630                         not_eof = false; 
631                         continue;
632                 } 
633
634                 // We now have c == '(', we now read the the sequence of
635                 // chars until reaching EOL, or ' ' and put that into a string.
636
637                 LString foundfile;
638                 c = fgetc(in());
639                 while (c != '\n' && c != ' ' && c != ')') {
640                         foundfile += char(c);
641                         c = fgetc(in());
642                 }
643                 if (foundfile.empty()) continue;
644
645                 lyxerr.debug("Found file: " 
646                              + foundfile,
647                              Error::LATEX);
648                 // Ok now we found a file.
649                 // Now we should make sure that
650                 // this is a file that we can
651                 // access through the normal
652                 // paths:
653                 // (1) foundfile is an
654                 //     absolute path and should
655                 //     be inserted.
656                 if (AbsolutePath(foundfile)) {
657                         lyxerr.debug("AbsolutePath file: " 
658                                      + foundfile,
659                                      Error::LATEX);
660                         // On inital insert we want to do the update at once
661                         // since this file can not be a file generated by
662                         // the latex run.
663                         head.insert(foundfile, true);
664                         continue;
665                 }
666
667                 // (2) foundfile is in the tmpdir
668                 //     insert it into head
669                 if (FileInfo(OnlyFilename(foundfile)).exist()) {
670                         if (foundfile.suffixIs(".aux")) {
671                                 lyxerr.debug("We don't want "
672                                              + OnlyFilename(foundfile)
673                                              + " in the dep file",
674                                              Error::LATEX);
675                         } else if (foundfile.suffixIs(".tex")) {
676                                 // This is a tex file generated by LyX
677                                 // and latex is not likely to change this
678                                 // during its runs.
679                                 lyxerr.debug("Tmpdir TeX file: "
680                                              + OnlyFilename(foundfile),
681                                              Error::LATEX);
682                                 head.insert(foundfile, true);
683                         } else {
684                                 lyxerr.debug("In tmpdir file:"
685                                              + OnlyFilename(foundfile),
686                                              Error::LATEX);
687                                 head.insert(OnlyFilename(foundfile));
688                         }
689                         continue;
690                 }
691
692                 // (3) the foundfile can be
693                 //     found in the same dir
694                 //     as the .lyx file and
695                 //     should be inserted.
696                 PathPush(path);
697                 if (FileInfo(foundfile).exist()) {
698                         lyxerr.print("LyX Strange: this should actually never"
699                                      " happen anymore, this it should be"
700                                      " handled by the Absolute check.");
701                         lyxerr.debug("Same Directory file: " 
702                                      + foundfile,
703                                      Error::LATEX);
704                         head.insert(foundfile);
705                         PathPop();
706                         continue;
707                 }
708                 PathPop();
709                 
710                 lyxerr.debug("Not a file or we are unable to find it.",
711                              Error::LATEX);
712
713
714         }
715 }
716
717
718 void LaTeX::deptex(DepTable &head)
719 {
720         int except = AUX|LOG|DVI|BBL|IND|GLO; 
721         LString tmp;
722         FileInfo fi;
723         for (int i = 0; i < file_count; i++) {
724                 if (!(all_files[i].file & except)) {
725                         tmp = ChangeExtension(file,
726                                               all_files[i].extension,
727                                               true);
728                         lyxerr.debug("deptex: " + tmp, Error::LATEX);
729                         if (fi.newFile(tmp).exist())
730                                 head.insert(tmp);
731                 }
732         }
733 }