]> git.lyx.org Git - lyx.git/blob - src/converter.C
changes read the dirr and changelog, something should be remvoed/changed
[lyx.git] / src / converter.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Processor
5  *        
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2000 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include <queue>
18
19 #include "converter.h"
20 #include "lyxrc.h"
21 #include "support/syscall.h"
22 #include "support/path.h"
23 #include "support/filetools.h"
24 #include "buffer.h"
25 #include "bufferview_funcs.h"
26 #include "LaTeX.h"
27 #include "LyXView.h"
28 #include "minibuffer.h"
29 #include "lyx_gui_misc.h"
30 #include "lyx_cb.h" // ShowMessage()
31
32 using std::map;
33 using std::vector;
34 using std::queue;
35 using std::pair;
36 using std::endl;
37 using std::find;
38 using std::find_if;
39
40 //////////////////////////////////////////////////////////////////////////////
41
42 vector<Command> Converter::commands;
43 string Converter::latex_command;
44
45 static inline
46 string const add_options(string const & command, string const & options)
47 {
48         string head;
49         string const tail = split(command, head, ' ');
50         return head + ' ' + options + ' ' + tail;
51 }
52
53 //////////////////////////////////////////////////////////////////////////////
54
55
56 bool Format::dummy() const
57 {
58         return extension.empty();
59 }
60
61
62 void Formats::Add(string const & name)
63 {
64         if (formats.find(name) == formats.end())
65                 formats[name] = Format(name, name, name, "", "");
66 }
67
68
69 void Formats::Add(string const & name, string const & extension, 
70                   string const & prettyname, string const & shortcut)
71 {
72
73         if (prettyname.empty()) {
74                 FormatList::iterator it = formats.find(name);
75                 if (it != formats.end())
76                         formats.erase(it);
77                 return;
78         }
79
80         string const old_viewer = formats[name].viewer;
81         formats[name] = Format(name, extension, prettyname, shortcut,
82                                old_viewer);
83 }
84
85
86 void Formats::SetViewer(string const & name, string const & command)
87 {
88         string command2 = command;
89         if (!command2.empty() && !contains(command2,"$$FName"))
90                 command2 += " $$FName";
91
92         Add(name);
93         GetFormat(name)->viewer = command2;
94 }
95
96
97 bool Formats::View(Buffer const * buffer, string const & filename,
98                    string const & format_name)
99 {
100         if (filename.empty())
101                 return false;
102
103         Format const * format = GetFormat(format_name);
104         if (!format || format->viewer.empty()) {
105                 WriteAlert(_("Can not view file"),
106                            _("No information for viewing ")
107                            + PrettyName(format_name));
108                            return false;
109         }
110
111         string command = format->viewer;
112
113         if (format_name == "dvi" &&
114             !lyxrc.view_dvi_paper_option.empty()) {
115                 string options = lyxrc.view_dvi_paper_option;
116                 options += " " + Converter::dvi_papersize(buffer);
117                 if (buffer->params.orientation 
118                     == BufferParams::ORIENTATION_LANDSCAPE)
119                         options += 'r';
120                 command = add_options(command, options);
121         }
122
123         string command2 = subst(command, "$$FName", OnlyFilename(filename));
124         lyxerr << "Executing command: " << command2 << endl;
125         ShowMessage(buffer, _("Executing command:"), command2);
126
127         command = subst(command, "$$FName", QuoteName(filename));
128         Systemcalls one;
129         int res = one.startscript(Systemcalls::SystemDontWait, command);
130
131         if (res) {
132                 WriteAlert(_("Can not view file"),
133                            _("Error while executing"),
134                            command.substr(0, 50));
135                 return false;
136         }
137         return true;
138 }
139
140
141 // This method should return a reference, and throw an exception
142 // if the format named name cannot be found (Lgb)
143 Format * Formats::GetFormat(string const & name)
144 {
145         FormatList::iterator it = formats.find(name);
146         if (it != formats.end())
147                 return &it->second;
148         else
149                 return 0;
150 }
151
152
153 string const Formats::PrettyName(string const & name)
154 {
155         Format const * format = GetFormat(name);
156         if (format)
157                 return format->prettyname;
158         else
159                 return name;
160 }
161
162
163 string const Formats::Extension(string const & name)
164 {
165         Format const * format = GetFormat(name);
166         if (format)
167                 return format->extension;
168         else
169                 return name;
170 }
171
172
173 vector<Format> 
174 const Formats::GetAllFormats() const
175 {
176         vector<Format> result;
177         for (FormatList::const_iterator cit = formats.begin(); 
178              cit != formats.end(); ++cit)
179                 result.push_back(cit->second);
180         return result;
181 }
182
183
184 //////////////////////////////////////////////////////////////////////////////
185
186 // Instead of storing an object we could just store an const reference.
187 // _but_ that is not guaranteed to work in all cases. (Lgb)
188 class compare_Command {
189 public:
190         compare_Command(Command const & c) : com(c) {}
191         bool operator()(Command const & c) {
192                 return c.from == com.from && c.to == com.to;
193         }
194 private:
195         Command com;
196 };
197
198
199 void Converter::Add(string const & from, string const & to,
200                     string const & command, string const & flags)
201 {
202         formats.Add(from);
203         formats.Add(to);
204         Command Com(formats.GetFormat(from), formats.GetFormat(to), command);
205         vector<Command>::iterator it = find_if(commands.begin(),
206                                                commands.end(),
207                                                compare_Command(Com));
208
209         if (command.empty() || command == "none") {
210                 if (it != commands.end())
211                         commands.erase(it);
212                 return;
213         }
214
215
216         // Read the flags
217         string flag_list(flags);
218         while (!flag_list.empty()) {
219                 string flag_name, flag_value;
220                 flag_list = split(flag_list, flag_value, ',');
221                 flag_value = split(flag_value, flag_name, '=');
222                 if (flag_name == "*") {
223                         if (it != commands.end()) {
224                                 Com = *it;
225                                 Com.command = command;
226                         }
227                 } 
228                 else if (flag_name == "importer")
229                         Com.importer = true;
230                 else if (flag_name == "latex")
231                         Com.latex = true;
232                 else if (flag_name == "originaldir")
233                         Com.original_dir = true;
234                 else if (flag_name == "needaux")
235                         Com.need_aux = true;
236                 else if (flag_name == "resultdir")
237                         Com.result_dir = (flag_value.empty())
238                                 ? "$$BaseName" : flag_value;
239                 else if (flag_name == "resultfile")
240                         Com.result_file = flag_value;
241                 else if (flag_name == "parselog")
242                         Com.parselog = flag_value;
243                 else if (flag_name == "disable") {
244                         while (!flag_value.empty()) {
245                                 string tmp;
246                                 flag_value = split(flag_value, tmp, '&');
247                                 Com.disable.push_back(tmp);
248                         }
249                 }
250         }
251         if (!Com.result_dir.empty() && Com.result_file.empty())
252                 Com.result_file = "index." + to;
253         //if (!contains(command, "$$FName"))
254         //      Com.latex = true;
255
256         if (Com.latex && (latex_command.empty() || to == "dvi"))
257                 latex_command = command;
258         // If we have both latex & pdflatex, we set latex_command to latex.
259         // The latex_command is used to update the .aux file when running
260         // a converter that uses it.
261
262         if (it != commands.end()) {
263                 *it = Com;
264                 return;
265         }
266         commands.push_back(Com);
267 }
268
269
270 static inline
271 bool enable(vector<Command>::iterator it, string const & from)
272 {
273         return find(it->disable.begin(), it->disable.end(), from)
274                 == it->disable.end();
275 }
276
277
278 vector<FormatPair> const
279 Converter::GetReachableTo(string const & target)
280 {
281         vector<FormatPair> result;
282
283         queue< vector<Command>::iterator > Q;
284         for (vector<Command>::iterator it = commands.begin();
285              it != commands.end(); ++it)
286                 if (it->to->name == target && it->importer) {
287                         Q.push(it);
288                         it->visited = true;
289                 } else
290                         it->visited = false;
291
292         while (!Q.empty()) {
293                 vector<Command>::iterator it = Q.front();
294                 Q.pop();
295                 result.push_back(FormatPair(it->from, 0, ""));
296                 for (vector<Command>::iterator it2 = commands.begin();
297                      it2 != commands.end(); ++it2)
298                         if (!it2->visited && it->from == it2->to &&
299                             it2->importer) {
300                                 Q.push(it2);
301                                 it2->visited = true;
302                         }
303         }
304
305         return result;
306 }
307
308
309 vector<FormatPair> const
310 Converter::GetReachable(string const & from, bool only_viewable)
311 {
312         vector<FormatPair> result;
313         Format const * format = formats.GetFormat(from);
314         if (!format)
315                 return result;
316
317         if (!only_viewable || !format->viewer.empty())
318                 result.push_back(FormatPair(format, 0, ""));
319
320         queue< vector<Command>::iterator > Q;
321         for (vector<Command>::iterator it = commands.begin();
322              it != commands.end(); ++it)
323                 if (it->from->name == from && enable(it, from) 
324                     && !it->importer) {
325                         Q.push(it);
326                         it->visited = true;
327                 } else
328                         it->visited = false;
329
330         while (!Q.empty()) {
331                 vector<Command>::iterator it = Q.front();
332                 Q.pop();
333                 if (!only_viewable || !it->to->viewer.empty())
334                         result.push_back(FormatPair(it->to, it->from,
335                                                     it->command));
336                 for (vector<Command>::iterator it2 = commands.begin();
337                      it2 != commands.end(); ++it2)
338                         if (!it2->visited && it->to == it2->from &&
339                             enable(it2, from) && !it2->importer) {
340                                 Q.push(it2);
341                                 it2->visited = true;
342                         }
343         }
344
345         return result;
346 }
347
348
349 bool Converter::IsReachable(string const & from, string const & to)
350 {
351         if (from == to)
352                 return true;
353
354         queue< vector<Command>::iterator > Q;
355         for (vector<Command>::iterator it = commands.begin();
356              it != commands.end(); ++it)
357                 if (it->from->name == from && enable(it, from)) {
358                         Q.push(it);
359                         it->visited = true;
360                 } else
361                         it->visited = false;
362
363         while (!Q.empty()) {
364                 vector<Command>::iterator it = Q.front();
365                 Q.pop();
366                 if (it->to->name == to)
367                         return true;
368                 for (vector<Command>::iterator it2 = commands.begin();
369                      it2 != commands.end(); ++it2)
370                         if (!it2->visited && it->to == it2->from &&
371                             enable(it2, from)) {
372                                 Q.push(it2);
373                                 it2->visited = true;
374                         }
375         }
376         return false;
377 }
378
379
380 bool Converter::Convert(Buffer const * buffer,
381                         string const & from_file, string const & to_file_base,
382                         string const & from_format, string const & to_format,
383                         string const & using_format, string & to_file)
384 {
385         to_file = ChangeExtension(to_file_base,
386                                   formats.Extension(to_format));
387
388         if (from_format == to_format)
389                 if (from_file != to_file)
390                         return lyx::rename(from_file, to_file);
391                 else
392                         return true;
393
394         queue< vector<Command>::iterator > Q;
395         for (vector<Command>::iterator it = commands.begin();
396              it != commands.end(); ++it)
397                 if (it->from->name == from_format && enable(it, from_format)) {
398                         Q.push(it);
399                         it->visited = true;
400                         it->previous = commands.end();
401                 } else
402                         it->visited = false;
403
404         if (Q.empty()) {
405                 WriteAlert(_("Can not convert file"),
406                            ("Unknown format ") + from_format);
407                 return false;
408         }
409
410         bool found = false;
411         vector<Command>::iterator it;
412         while (!Q.empty()) {
413                 it = Q.front();
414                 if (it->to->name == to_format &&
415                     (using_format.empty() || using_format == it->from->name)) {
416                         found = true;
417                         break;
418                 }
419                 Q.pop();
420                 for (vector<Command>::iterator it2 = commands.begin();
421                      it2 != commands.end(); ++it2)
422                         if (!it2->visited && it->to == it2->from &&
423                             enable(it2, from_format)) {
424                                 Q.push(it2);
425                                 it2->visited = true;
426                                 it2->previous = it;
427                         }
428         }
429
430         if (!found) {
431                 WriteAlert(_("Can not convert file"),
432                            _("No information for converting from ")
433                            + formats.PrettyName(from_format) + _(" to ")
434                            + formats.PrettyName(to_format));
435                 return false;
436         }
437
438         vector< vector<Command>::iterator > S;
439         while (it != commands.end()) {
440                 S.push_back(it);
441                 it = it->previous;
442         }
443
444         string path = OnlyPath(from_file);
445         Path p(path);
446
447         bool run_latex = false;
448         string from_base = ChangeExtension(from_file, "");
449         string to_base = ChangeExtension(to_file, "");
450         string infile;
451         string outfile = from_file;
452         for (vector< vector<Command>::iterator >::reverse_iterator rit =
453                      S.rbegin(); rit != S.rend(); ++rit) {
454                 it = *rit;
455                 bool dummy = it->to->dummy() && it->to->name != "program";
456                 if (!dummy)
457                         lyxerr << "Converting from  "
458                                << it->from->name << " to " << it->to->name << endl;
459                 infile = outfile;
460                 outfile = it->result_dir.empty()
461                         ? ChangeExtension(from_file, it->to->extension)
462                         : AddName(subst(it->result_dir,
463                                         "$$BaseName", from_base),
464                                   subst(it->result_file,
465                                         "$$BaseName", OnlyFilename(from_base)));
466
467                 if (it->latex) {
468                         lyxrc.pdf_mode = it->to->name == "pdf";
469                         lyxerr << "Running " << it->command << endl;
470                         run_latex = true;
471                         if (!runLaTeX(buffer, it->command))
472                                 return false;
473                 } else {
474                         if (it->need_aux && !run_latex
475                             && !latex_command.empty()) {
476                                 lyxerr << "Running " << latex_command 
477                                        << " to update aux file"<<  endl;
478                                 runLaTeX(buffer, latex_command);
479                         }
480
481                         string infile2 = (it->original_dir)
482                                 ? infile : MakeRelPath(infile, path);
483                         string outfile2 = (it->original_dir)
484                                 ? outfile : MakeRelPath(outfile, path);
485
486                         string command = it->command;
487                         command = subst(command, "$$FName", QuoteName(infile2));
488                         command = subst(command, "$$BaseName", QuoteName(from_base));
489                         command = subst(command, "$$OutName", QuoteName(outfile2));
490
491                         if (!it->parselog.empty())
492                                 command += " 2> " + QuoteName(infile2 + ".out");
493
494                         if (it->from->name == "dvi" && it->to->name == "ps")
495                                 command = add_options(command,
496                                                       dvips_options(buffer));
497
498                         lyxerr << "Calling " << command << endl;
499                         if (buffer)
500                                 ShowMessage(buffer, _("Executing command:"), command);
501
502                         Systemcalls::Starttype type = (dummy)
503                                 ? Systemcalls::SystemDontWait : Systemcalls::System;
504                         Systemcalls one;
505                         int res;
506                         if (it->original_dir && buffer) {
507                                 Path p(buffer->filepath);
508                                 res = one.startscript(type, command);
509                         } else
510                                 res = one.startscript(type, command);
511
512                         if (!it->parselog.empty()) {
513                                 string const logfile =  infile2 + ".log";
514                                 string const command2 = it->parselog +
515                                         " < " + QuoteName(infile2 + ".out") +
516                                         " > " + QuoteName(logfile);
517                                 one.startscript(Systemcalls::System, command2);
518                                 if (!scanLog(buffer, command, logfile))
519                                         return false;
520                         }
521
522                         if (res) {
523                                 if (it->to->name == "program")
524                                         WriteAlert(_("There were errors during the Build process."),
525                                                    _("You should try to fix them."));
526                                 else
527                                         WriteAlert(_("Can not convert file"),
528                                                    "Error while executing",
529                                                    command.substr(0, 50));
530                                 return false;
531                         }
532                 }
533         }
534
535         if (it->to->dummy())
536                 return true;
537
538
539         if (!it->result_dir.empty()) {
540                 to_file = AddName(subst(it->result_dir,
541                                         "$$BaseName", to_base),
542                                   subst(it->result_file,
543                                         "$$BaseName", OnlyFilename(to_base)));
544                 if (from_base != to_base) {
545                         string from = subst(it->result_dir,
546                                             "$$BaseName", from_base);
547                         string to = subst(it->result_dir,
548                                           "$$BaseName", to_base);
549                         if (!lyx::rename(from, to)) {
550                                 WriteAlert(_("Error while trying to move directory:"),
551                                            from, ("to ") + to);
552                                 return false;
553                         }
554                 }
555         } else if (outfile != to_file) {
556                 bool moved = (it->latex)
557                         ? lyx::copy(outfile, to_file)
558                         : lyx::rename(outfile, to_file);
559                 if (!moved) {
560                         WriteAlert(_("Error while trying to move file:"),
561                                    outfile, _("to ") + to_file);
562                         return false;
563                 }
564         }
565
566         return true;
567 }
568
569
570 bool Converter::Convert(Buffer const * buffer,
571                         string const & from_file, string const & to_file_base,
572                         string const & from_format, string const & to_format,
573                         string const & using_format)
574 {
575         string to_file;
576         return Convert(buffer, from_file, to_file_base, from_format, to_format,
577                 using_format, to_file);
578 }
579
580
581 string const Converter::SplitFormat(string const & str, string & format)
582 {
583         string const using_format = split(str, format, ':');
584         if (format.empty())
585                 format = "dvi";
586         return using_format;
587 }
588
589
590 bool Converter::scanLog(Buffer const * buffer, string const & command,
591                         string const & filename)
592 {
593         if (!buffer)
594                 return false;
595
596         BufferView * bv = buffer->getUser();
597         bool need_redraw = false;
598         if (bv) {
599                 ProhibitInput(bv);
600                 // Remove all error insets
601                 need_redraw = bv->removeAutoInsets();
602         }
603
604         LaTeX latex("", filename, "");
605         TeXErrors terr;
606         int result = latex.scanLogFile(terr);
607         if (bv) {
608                 if ((result & LaTeX::ERRORS)) {
609                         // Insert all errors as errors boxes
610                         bv->insertErrors(terr);
611                         need_redraw = true;
612                 }
613                 if (need_redraw) {
614                         bv->redraw();
615                         bv->fitCursor(bv->text);
616                 }
617                 AllowInput(bv);
618         }
619
620         if ((result & LaTeX::ERRORS)) {
621                 int num_errors = latex.getNumErrors();
622                 string s;
623                 string t;
624                 if (num_errors == 1) {
625                         s = _("One error detected");
626                         t = _("You should try to fix it.");
627                 } else {
628                         s = tostr(num_errors);
629                         s += _(" errors detected.");
630                         t = _("You should try to fix them.");
631                 }
632                 string head;
633                 split(command, head, ' ');
634                 WriteAlert(_("There were errors during running of ") + head,
635                            s, t);
636                 return false;
637         } else if (result & LaTeX::NO_OUTPUT) {
638                 string const s = _("The operation resulted in");
639                 string const t = _("an empty file.");
640                 WriteAlert(_("Resulting file is empty"), s, t);
641                 return false;
642         }
643         return true;
644 }
645
646
647 bool Converter::runLaTeX(Buffer const * buffer, string const & command)
648 {
649         if (!buffer)
650                 return false;
651
652         BufferView * bv = buffer->getUser();
653         string name = buffer->getLatexName();
654         bool need_redraw = false;
655
656         if (bv) {
657                 ProhibitInput(bv);
658                 bv->owner()->getMiniBuffer()->Set(_("Running LaTeX..."));
659                 // Remove all error insets
660                 need_redraw = bv->removeAutoInsets();
661         }
662
663
664         // do the LaTex run(s)
665         TeXErrors terr;
666         LaTeX latex(command, name, buffer->filepath);
667         int result = latex.run(terr,
668                                bv ? bv->owner()->getMiniBuffer() : 0);
669         
670
671         if (bv) {
672                 if ((result & LaTeX::ERRORS)) {
673                         // Insert all errors as errors boxes
674                         bv->insertErrors(terr);
675                         need_redraw = true;
676                 }
677
678                 // if we removed error insets before we ran LaTeX or if we inserted
679                 // error insets after we ran LaTeX this must be run:
680                 if (need_redraw) {
681                         bv->redraw();
682                         bv->fitCursor(bv->text);
683                 }
684         }
685
686         // check return value from latex.run().
687         if ((result & LaTeX::NO_LOGFILE)) {
688                 WriteAlert(_("LaTeX did not work!"),
689                            _("Missing log file:"), name);
690         } else if ((result & LaTeX::ERRORS)) {
691                 int num_errors = latex.getNumErrors();
692                 string s;
693                 string t;
694                 if (num_errors == 1) {
695                         s = _("One error detected");
696                         t = _("You should try to fix it.");
697                 } else {
698                         s = tostr(num_errors);
699                         s += _(" errors detected.");
700                         t = _("You should try to fix them.");
701                 }
702                 WriteAlert(_("There were errors during the LaTeX run."),
703                            s, t);
704         }  else if (result & LaTeX::NO_OUTPUT) {
705                 string const s = _("The operation resulted in");
706                 string const t = _("an empty file.");
707                 WriteAlert(_("Resulting file is empty"), s, t);
708         }
709
710         if (bv)
711                 AllowInput(bv);
712  
713         int const ERROR_MASK = 
714                         LaTeX::NO_LOGFILE |
715                         LaTeX::ERRORS |
716                         LaTeX::NO_OUTPUT;
717         
718         return (result & ERROR_MASK) == 0;
719
720 }
721
722
723 string const Converter::dvi_papersize(Buffer const * buffer)
724 {
725         char real_papersize = buffer->params.papersize;
726         if (real_papersize == BufferParams::PAPER_DEFAULT)
727                 real_papersize = lyxrc.default_papersize;
728
729         switch (real_papersize) {
730         case BufferParams::PAPER_A3PAPER:
731                 return "a3";
732         case BufferParams::PAPER_A4PAPER:
733                 return "a4";
734         case BufferParams::PAPER_A5PAPER:
735                 return "a5";
736         case BufferParams::PAPER_B5PAPER:
737                 return "b5";
738         case BufferParams::PAPER_EXECUTIVEPAPER:
739                 return "foolscap";
740         case BufferParams::PAPER_LEGALPAPER:
741                 return "legal";
742         case BufferParams::PAPER_USLETTER:
743         default:
744                 return "us";
745         }
746 }
747
748
749 string const Converter::dvips_options(Buffer const * buffer)
750 {
751         string result;
752         if (!buffer)
753                 return result;
754
755         if (buffer->params.use_geometry
756             && buffer->params.papersize2 == BufferParams::VM_PAPER_CUSTOM
757             && !lyxrc.print_paper_dimension_flag.empty()
758             && !buffer->params.paperwidth.empty()
759             && !buffer->params.paperheight.empty()) {
760                 // using a custom papersize
761                 result = lyxrc.print_paper_dimension_flag;
762                 result += ' ' + buffer->params.paperwidth;
763                 result += ',' + buffer->params.paperheight;
764         } else {
765                 string paper_option = dvi_papersize(buffer);
766                 if (paper_option == "us")
767                         paper_option = "letter";
768                 if (paper_option != "letter" ||
769                     buffer->params.orientation != BufferParams::ORIENTATION_LANDSCAPE) {
770                         // dvips won't accept -t letter -t landscape.  In all other
771                         // cases, include the paper size explicitly.
772                         result = lyxrc.print_paper_flag;
773                         result += ' ' + paper_option;
774                 }
775         }
776         if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
777                 result += ' ' + lyxrc.print_landscape_flag;
778         return result;
779 }
780
781
782 void Converter::init()
783 {
784 }
785
786 vector<Command> const Converter::GetAllCommands()
787 {
788         vector<Command> result;
789         for (vector<Command>::iterator it = commands.begin(); 
790              it != commands.end(); ++it)
791                 result.push_back(*it);
792         return result;
793 }
794
795
796 /// The global instance
797 Formats formats;
798
799 // The global copy of the system lyxrc entries (everything except preferences)
800 Formats system_formats;