]> git.lyx.org Git - lyx.git/blob - src/converter.C
don't copy if a reference is fine
[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-2001 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include <cctype>
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 "lyx_gui_misc.h"
29 #include "lyx_cb.h" // ShowMessage()
30 #include "support/lyxfunctional.h"
31 #include "frontends/Alert.h"
32 #include "gettext.h"
33 #include "BufferView.h"
34 #include "debug.h"
35
36 using std::vector;
37 using std::queue;
38 using std::endl;
39 using std::fill;
40 using std::find_if;
41 using std::reverse;
42 using std::sort;
43
44 namespace {
45
46 string const token_from("$$i");
47 string const token_base("$$b");
48 string const token_to("$$o");
49
50 //////////////////////////////////////////////////////////////////////////////
51
52 inline
53 string const add_options(string const & command, string const & options)
54 {
55         string head;
56         string const tail = split(command, head, ' ');
57         return head + ' ' + options + ' ' + tail;
58 }
59
60 } // namespace anon
61
62 //////////////////////////////////////////////////////////////////////////////
63
64 bool Format::dummy() const
65 {
66         return extension().empty();
67 }
68
69
70 bool Format::isChildFormat() const
71 {
72         if (name_.empty())
73                 return false;
74         return isdigit(name_[name_.length() - 1]);
75 }
76
77
78 string const Format::parentFormat() const
79 {
80         return name_.substr(0, name_.length() - 1);
81 }
82
83 //////////////////////////////////////////////////////////////////////////////
84
85 // This method should return a reference, and throw an exception
86 // if the format named name cannot be found (Lgb)
87 Format const * Formats::getFormat(string const & name) const
88 {
89         FormatList::const_iterator cit =
90                 find_if(formatlist.begin(), formatlist.end(),
91                         lyx::compare_memfun(&Format::name, name));
92         if (cit != formatlist.end())
93                 return &(*cit);
94         else
95                 return 0;
96 }
97
98
99 int Formats::getNumber(string const & name) const
100 {
101         FormatList::const_iterator cit =
102                 find_if(formatlist.begin(), formatlist.end(),
103                         lyx::compare_memfun(&Format::name, name));
104         if (cit != formatlist.end())
105                 return cit - formatlist.begin();
106         else
107                 return -1;
108 }
109
110
111 void Formats::add(string const & name)
112 {
113         if (!getFormat(name))
114                 add(name, name, name, string());
115 }
116
117
118 void Formats::add(string const & name, string const & extension, 
119                   string const & prettyname, string const & shortcut)
120 {
121         FormatList::iterator it = 
122                 find_if(formatlist.begin(), formatlist.end(),
123                         lyx::compare_memfun(&Format::name, name));
124         if (it == formatlist.end())
125                 formatlist.push_back(Format(name, extension, prettyname,
126                                             shortcut, ""));
127         else {
128                 string viewer = it->viewer();
129                 *it = Format(name, extension, prettyname, shortcut, viewer);
130         }
131 }
132
133
134 void Formats::erase(string const & name)
135 {
136         FormatList::iterator it = 
137                 find_if(formatlist.begin(), formatlist.end(),
138                         lyx::compare_memfun(&Format::name, name));
139         if (it != formatlist.end())
140                 formatlist.erase(it);
141 }
142
143
144 void Formats::sort()
145 {
146         std::sort(formatlist.begin(), formatlist.end());
147 }
148
149
150 void Formats::setViewer(string const & name, string const & command)
151 {
152         add(name);
153         FormatList::iterator it =
154                 find_if(formatlist.begin(), formatlist.end(),
155                         lyx::compare_memfun(&Format::name, name));
156         if (it != formatlist.end())
157                 it->setViewer(command);
158 }
159
160
161 bool Formats::view(Buffer const * buffer, string const & filename,
162                    string const & format_name) const
163 {
164         if (filename.empty())
165                 return false;
166
167         Format const * format = getFormat(format_name);
168         if (format && format->viewer().empty() &&
169             format->isChildFormat())
170                 format = getFormat(format->parentFormat());
171         if (!format || format->viewer().empty()) {
172                 Alert::alert(_("Cannot view file"),
173                            _("No information for viewing ")
174                            + prettyName(format_name));
175                            return false;
176         }
177
178         string command = format->viewer();
179
180         if (format_name == "dvi" &&
181             !lyxrc.view_dvi_paper_option.empty()) {
182                 command += " " + lyxrc.view_dvi_paper_option;
183                 string paper_size = converters.papersize(buffer);
184                 if (paper_size == "letter")
185                         paper_size = "us";
186                 command += " " + paper_size;
187                 if (buffer->params.orientation 
188                     == BufferParams::ORIENTATION_LANDSCAPE)
189                         command += 'r';
190         }
191
192         command += " " + QuoteName(OnlyFilename((filename)));
193
194         lyxerr[Debug::FILES] << "Executing command: " << command << endl;
195         ShowMessage(buffer, _("Executing command:"), command);
196
197         Path p(OnlyPath(filename));
198         Systemcalls one;
199         int const res = one.startscript(Systemcalls::SystemDontWait, command);
200
201         if (res) {
202                 Alert::alert(_("Cannot view file"),
203                            _("Error while executing"),
204                            command.substr(0, 50));
205                 return false;
206         }
207         return true;
208 }
209
210
211 string const Formats::prettyName(string const & name) const
212 {
213         Format const * format = getFormat(name);
214         if (format)
215                 return format->prettyname();
216         else
217                 return name;
218 }
219
220
221 string const Formats::extension(string const & name) const
222 {
223         Format const * format = getFormat(name);
224         if (format)
225                 return format->extension();
226         else
227                 return name;
228 }
229
230 //////////////////////////////////////////////////////////////////////////////
231
232 void Converter::readFlags()
233 {
234         string flag_list(flags);
235         while (!flag_list.empty()) {
236                 string flag_name, flag_value;
237                 flag_list = split(flag_list, flag_value, ',');
238                 flag_value = split(flag_value, flag_name, '=');
239                 if (flag_name == "latex")
240                         latex = true;
241                 else if (flag_name == "originaldir")
242                         original_dir = true;
243                 else if (flag_name == "needaux")
244                         need_aux = true;
245                 else if (flag_name == "resultdir")
246                         result_dir = (flag_value.empty())
247                                 ? token_base : flag_value;
248                 else if (flag_name == "resultfile")
249                         result_file = flag_value;
250                 else if (flag_name == "parselog")
251                         parselog = flag_value;
252         }
253         if (!result_dir.empty() && result_file.empty())
254                 result_file = "index." + formats.extension(to);
255         //if (!contains(command, token_from))
256         //      latex = true;
257 }
258
259
260 bool operator<(Converter const & a, Converter const & b)
261 {
262         int const i = compare_no_case(a.From->prettyname(),
263                                       b.From->prettyname());
264         if (i == 0)
265                 return compare_no_case(a.To->prettyname(), b.To->prettyname())
266                         < 0;
267         else
268                 return i < 0;
269 }
270
271 //////////////////////////////////////////////////////////////////////////////
272
273 class compare_Converter {
274 public:
275         compare_Converter(string const & f, string const & t)
276                 : from(f), to(t) {}
277         bool operator()(Converter const & c) {
278                 return c.from == from && c.to == to;
279         }
280 private:
281         string const & from;
282         string const & to;
283 };
284
285
286 Converter const * Converters::getConverter(string const & from,
287                                             string const & to)
288 {
289         ConverterList::const_iterator cit =
290                 find_if(converterlist_.begin(), converterlist_.end(),
291                         compare_Converter(from, to));
292         if (cit != converterlist_.end())
293                 return &(*cit);
294         else
295                 return 0;
296 }
297
298
299 int Converters::getNumber(string const & from, string const & to)
300 {
301         ConverterList::const_iterator cit =
302                 find_if(converterlist_.begin(), converterlist_.end(),
303                         compare_Converter(from, to));
304         if (cit != converterlist_.end())
305                 return cit - converterlist_.begin();
306         else
307                 return -1;
308 }
309
310
311 void Converters::add(string const & from, string const & to,
312                      string const & command, string const & flags)
313 {
314         formats.add(from);
315         formats.add(to);
316         ConverterList::iterator it = find_if(converterlist_.begin(),
317                                              converterlist_.end(),
318                                              compare_Converter(from, to));
319
320         Converter converter(from, to, command, flags);
321         if (it != converterlist_.end() && !flags.empty() && flags[0] == '*') {
322                 converter = *it;
323                 converter.command = command;
324                 converter.flags = flags;
325         }
326         converter.readFlags();
327         
328         if (converter.latex && (latex_command_.empty() || to == "dvi"))
329                 latex_command_ = subst(command, token_from, "");
330         // If we have both latex & pdflatex, we set latex_command to latex.
331         // The latex_command is used to update the .aux file when running
332         // a converter that uses it.
333
334         if (it == converterlist_.end()) {
335                 converterlist_.push_back(converter);
336         } else {
337                 converter.From = it->From;
338                 converter.To = it->To;
339                 *it = converter;
340         }
341 }
342
343
344 void Converters::erase(string const & from, string const & to)
345 {
346         ConverterList::iterator it = find_if(converterlist_.begin(),
347                                              converterlist_.end(),
348                                              compare_Converter(from, to));
349         if (it != converterlist_.end())
350                 converterlist_.erase(it);
351 }
352
353
354 // This method updates the pointers From and To in all the converters.
355 // The code is not very efficient, but it doesn't matter as the number
356 // of formats and converters is small.
357 // Furthermore, this method is called only on startup, or after 
358 // adding/deleting a format in FormPreferences (the latter calls can be
359 // eliminated if the formats in the Formats class are stored using a map or
360 // a list (instead of a vector), but this will cause other problems). 
361 void Converters::update(Formats const & formats)
362 {
363         ConverterList::iterator it = converterlist_.begin();
364         ConverterList::iterator end = converterlist_.end();
365         for (; it != end; ++it) {
366                 it->From = formats.getFormat(it->from);
367                 it->To = formats.getFormat(it->to);
368         }
369 }
370
371
372 // This method updates the pointers From and To in the last converter.
373 // It is called when adding a new converter in FormPreferences
374 void Converters::updateLast(Formats const & formats)
375 {
376         if (converterlist_.begin() != converterlist_.end()) {
377                 ConverterList::iterator it = converterlist_.end() - 1;
378                 it->From = formats.getFormat(it->from);
379                 it->To = formats.getFormat(it->to);
380         }
381 }
382
383
384 void Converters::sort()
385 {
386         std::sort(converterlist_.begin(), converterlist_.end());
387 }
388
389
390 int Converters::bfs_init(string const & start, bool clear_visited)
391 {
392         int const s = formats.getNumber(start);
393         if (s < 0)
394                 return s;
395
396         Q_ = queue<int>();
397         if (clear_visited)
398                 fill(visited_.begin(), visited_.end(), false);
399         if (visited_[s] == false) {
400                 Q_.push(s);
401                 visited_[s] = true;
402         }
403         return s;
404 }
405
406
407 vector<Format const *> const
408 Converters::getReachableTo(string const & target, bool clear_visited)
409 {
410         vector<Format const *> result;
411         int const s = bfs_init(target, clear_visited);
412         if (s < 0)
413                 return result;
414
415         while (!Q_.empty()) {
416                 int const i = Q_.front();
417                 Q_.pop();
418                 if (i != s || target != "lyx") {
419                         result.push_back(&formats.get(i));
420                 }
421                 
422                 vector<int>::iterator it = vertices_[i].in_vertices.begin();
423                 vector<int>::iterator end = vertices_[i].in_vertices.end();
424                 for (; it != end; ++it) {
425                         if (!visited_[*it]) {
426                                 visited_[*it] = true;
427                                 Q_.push(*it);
428                         }
429                 }
430         }
431
432         return result;
433 }
434
435
436 vector<Format const *> const
437 Converters::getReachable(string const & from, bool only_viewable,
438                          bool clear_visited)
439 {
440         vector<Format const *> result;
441
442         if (bfs_init(from, clear_visited) < 0)
443                 return result;
444
445         while (!Q_.empty()) {
446                 int const i = Q_.front();
447                 Q_.pop();
448                 Format const & format = formats.get(i);
449                 if (format.name() == "lyx")
450                         continue;
451                 if (!only_viewable || !format.viewer().empty() ||
452                     format.isChildFormat())
453                         result.push_back(&format);
454
455                 vector<int>::const_iterator cit =
456                         vertices_[i].out_vertices.begin();
457                 vector<int>::const_iterator end =
458                         vertices_[i].out_vertices.end();
459                 for (; cit != end; ++cit)
460                         if (!visited_[*cit]) {
461                                 visited_[*cit] = true;
462                                 Q_.push(*cit);
463                         }
464         }
465
466         return result;
467 }
468
469
470 bool Converters::isReachable(string const & from, string const & to)
471 {
472         if (from == to)
473                 return true;
474
475         int const s = bfs_init(from);
476         int const t = formats.getNumber(to);
477         if (s < 0 || t < 0)
478                 return false;
479
480         while (!Q_.empty()) {
481                 int const i = Q_.front();
482                 Q_.pop();
483                 if (i == t)
484                         return true;
485
486                 vector<int>::const_iterator cit =
487                         vertices_[i].out_vertices.begin();
488                 vector<int>::const_iterator end =
489                         vertices_[i].out_vertices.end();
490                 for (; cit != end; ++cit) {
491                         if (!visited_[*cit]) {
492                                 visited_[*cit] = true;
493                                 Q_.push(*cit);
494                         }
495                 }
496         }
497
498         return false;
499 }
500
501
502 Converters::EdgePath const
503 Converters::getPath(string const & from, string const & to)
504 {
505         EdgePath path;
506         if (from == to)
507                 return path;
508
509         int const s = bfs_init(from);
510         int t = formats.getNumber(to);
511         if (s < 0 || t < 0)
512                 return path;
513
514         vector<int> prev_edge(formats.size());
515         vector<int> prev_vertex(formats.size());
516
517         bool found = false;
518         while (!Q_.empty()) {
519                 int const i = Q_.front();
520                 Q_.pop();
521                 if (i == t) {
522                         found = true;
523                         break;
524                 }
525                 
526                 vector<int>::const_iterator beg =
527                         vertices_[i].out_vertices.begin();
528                 vector<int>::const_iterator cit = beg;
529                 vector<int>::const_iterator end =
530                         vertices_[i].out_vertices.end();
531                 for (; cit != end; ++cit)
532                         if (!visited_[*cit]) {
533                                 int const j = *cit;
534                                 visited_[j] = true;
535                                 Q_.push(j);
536                                 int const k = cit - beg;
537                                 prev_edge[j] = vertices_[i].out_edges[k];
538                                 prev_vertex[j] = i;
539                         }
540         }
541         if (!found)
542                 return path;
543
544         while (t != s) {
545                 path.push_back(prev_edge[t]);
546                 t = prev_vertex[t];
547         }
548         reverse(path.begin(), path.end());
549         return path;
550 }
551
552
553 bool Converters::usePdflatex(EdgePath const & path)
554 {
555         for (EdgePath::const_iterator cit = path.begin();
556              cit != path.end(); ++cit) {
557                 Converter const & conv = converterlist_[*cit];
558                 if (conv.latex)
559                         return contains(conv.to, "pdf");
560         }
561         return false;
562 }
563
564
565 bool Converters::convert(Buffer const * buffer,
566                          string const & from_file, string const & to_file_base,
567                          string const & from_format, string const & to_format,
568                          string & to_file)
569 {
570         to_file = ChangeExtension(to_file_base,
571                                   formats.extension(to_format));
572
573         if (from_format == to_format)
574                 return move(from_file, to_file, false);
575
576         EdgePath edgepath = getPath(from_format, to_format);
577         if (edgepath.empty()) {
578                 Alert::alert(_("Cannot convert file"),
579                            _("No information for converting from ")
580                            + formats.prettyName(from_format) + _(" to ")
581                            + formats.prettyName(to_format));
582                 return false;
583         }
584
585         string path = OnlyPath(from_file);
586         Path p(path);
587
588         bool run_latex = false;
589         string from_base = ChangeExtension(from_file, "");
590         string to_base = ChangeExtension(to_file, "");
591         string infile;
592         string outfile = from_file;
593         for (EdgePath::const_iterator cit = edgepath.begin();
594              cit != edgepath.end(); ++cit) {
595                 Converter const & conv = converterlist_[*cit];
596                 bool dummy = conv.To->dummy() && conv.to != "program";
597                 if (!dummy)
598                         lyxerr[Debug::FILES] << "Converting from  "
599                                << conv.from << " to " << conv.to << endl;
600                 infile = outfile;
601                 outfile = conv.result_dir.empty()
602                         ? ChangeExtension(from_file, conv.To->extension())
603                         : AddName(subst(conv.result_dir,
604                                         token_base, from_base),
605                                   subst(conv.result_file,
606                                         token_base, OnlyFilename(from_base)));
607
608                 if (conv.latex) {
609                         run_latex = true;
610                         string command = subst(conv.command, token_from, "");
611                         lyxerr[Debug::FILES] << "Running " << command << endl;
612                         if (!runLaTeX(buffer, command))
613                                 return false;
614                 } else {
615                         if (conv.need_aux && !run_latex
616                             && !latex_command_.empty()) {
617                                 lyxerr[Debug::FILES] 
618                                         << "Running " << latex_command_ 
619                                         << " to update aux file"<<  endl;
620                                 runLaTeX(buffer, latex_command_);
621                         }
622
623                         string infile2 = (conv.original_dir)
624                                 ? infile : MakeRelPath(infile, path);
625                         string outfile2 = (conv.original_dir)
626                                 ? outfile : MakeRelPath(outfile, path);
627
628                         string command = conv.command;
629                         command = subst(command, token_from, QuoteName(infile2));
630                         command = subst(command, token_base, QuoteName(from_base));
631                         command = subst(command, token_to, QuoteName(outfile2));
632
633                         if (!conv.parselog.empty())
634                                 command += " 2> " + QuoteName(infile2 + ".out");
635
636                         if (conv.from == "dvi" && conv.to == "ps")
637                                 command = add_options(command,
638                                                       dvips_options(buffer));
639                         else if (conv.from == "dvi" && prefixIs(conv.to, "pdf"))
640                                 command = add_options(command,
641                                                       dvipdfm_options(buffer));
642
643                         lyxerr[Debug::FILES] << "Calling " << command << endl;
644                         if (buffer)
645                                 ShowMessage(buffer, _("Executing command:"), command);
646
647                         Systemcalls::Starttype type = (dummy)
648                                 ? Systemcalls::SystemDontWait : Systemcalls::System;
649                         Systemcalls one;
650                         int res;
651                         if (conv.original_dir && buffer) {
652                                 Path p(buffer->filePath());
653                                 res = one.startscript(type, command);
654                         } else
655                                 res = one.startscript(type, command);
656
657                         if (!conv.parselog.empty()) {
658                                 string const logfile =  infile2 + ".log";
659                                 string const command2 = conv.parselog +
660                                         " < " + QuoteName(infile2 + ".out") +
661                                         " > " + QuoteName(logfile);
662                                 one.startscript(Systemcalls::System, command2);
663                                 if (!scanLog(buffer, command, logfile))
664                                         return false;
665                         }
666
667                         if (res) {
668                                 if (conv.to == "program")
669                                         Alert::alert(_("There were errors during the Build process."),
670                                                    _("You should try to fix them."));
671                                 else
672                                         Alert::alert(_("Cannot convert file"),
673                                                    "Error while executing",
674                                                    command.substr(0, 50));
675                                 return false;
676                         }
677                 }
678         }
679
680         Converter const & conv = converterlist_[edgepath.back()];
681         if (conv.To->dummy())
682                 return true;
683
684
685         if (!conv.result_dir.empty()) {
686                 to_file = AddName(subst(conv.result_dir, token_base, to_base),
687                                   subst(conv.result_file,
688                                         token_base, OnlyFilename(to_base)));
689                 if (from_base != to_base) {
690                         string from = subst(conv.result_dir,
691                                             token_base, from_base);
692                         string to = subst(conv.result_dir,
693                                           token_base, to_base);
694                         if (!lyx::rename(from, to)) {
695                                 Alert::alert(_("Error while trying to move directory:"),
696                                            from, ("to ") + to);
697                                 return false;
698                         }
699                 }
700                 return true;
701         } else 
702                 return move(outfile, to_file, conv.latex);
703 }
704
705 // If from = /path/file.ext and to = /path2/file2.ext2 then this method 
706 // moves each /path/file*.ext file to /path2/file2*.ext2'
707 bool Converters::move(string const & from, string const & to, bool copy)
708 {
709         if (from == to)
710                 return true;
711
712         bool no_errors = true;
713         string const path = OnlyPath(from);
714         string const base = OnlyFilename(ChangeExtension(from, ""));
715         string const to_base = ChangeExtension(to, "");
716         string const to_extension = GetExtension(to);
717
718         vector<string> files = DirList(OnlyPath(from), GetExtension(from));
719         for (vector<string>::const_iterator it = files.begin();
720              it != files.end(); ++it)
721                 if (prefixIs(*it, base)) {
722                         string from2 = path + *it;
723                         string to2 = to_base + it->substr(base.length());
724                         to2 = ChangeExtension(to2, to_extension);
725                         lyxerr[Debug::FILES] << "moving " << from2 
726                                              << " to " << to2 << endl;
727                         bool moved = (copy)
728                                 ? lyx::copy(from2, to2)
729                                 : lyx::rename(from2, to2);
730                         if (!moved && no_errors) {
731                                 Alert::alert(_("Error while trying to move file:"),
732                                            from2, _("to ") + to2);
733                                 no_errors = false;
734                         }
735                 }
736         return no_errors;
737 }
738
739
740 bool Converters::convert(Buffer const * buffer,
741                         string const & from_file, string const & to_file_base,
742                         string const & from_format, string const & to_format)
743 {
744         string to_file;
745         return convert(buffer, from_file, to_file_base, from_format, to_format,
746                        to_file);
747 }
748
749
750 void Converters::buildGraph()
751 {
752         vertices_ = vector<Vertex>(formats.size());
753         visited_.resize(formats.size());
754
755         for (ConverterList::iterator it = converterlist_.begin();
756              it != converterlist_.end(); ++it) {
757                 int const s = formats.getNumber(it->from);
758                 int const t = formats.getNumber(it->to);
759                 vertices_[t].in_vertices.push_back(s);
760                 vertices_[s].out_vertices.push_back(t);
761                 vertices_[s].out_edges.push_back(it - converterlist_.begin());
762         }
763 }
764
765
766 bool Converters::formatIsUsed(string const & format)
767 {
768         ConverterList::const_iterator cit = converterlist_.begin();
769         ConverterList::const_iterator end = converterlist_.end();
770         for (; cit != end; ++cit) {
771                 if (cit->from == format || cit->to == format)
772                         return true;
773         }
774         return false;
775 }
776
777
778 bool Converters::scanLog(Buffer const * buffer, string const & command,
779                         string const & filename)
780 {
781         if (!buffer)
782                 return false;
783
784         BufferView * bv = buffer->getUser();
785         bool need_redraw = false;
786         if (bv) {
787                 bv->owner()->prohibitInput();
788                 // Remove all error insets
789                 need_redraw = bv->removeAutoInsets();
790         }
791
792         LaTeX latex("", filename, "");
793         TeXErrors terr;
794         int result = latex.scanLogFile(terr);
795         if (bv) {
796                 if ((result & LaTeX::ERRORS)) {
797                         // Insert all errors as errors boxes
798                         bv->insertErrors(terr);
799                         need_redraw = true;
800                 }
801                 if (need_redraw) {
802                         bv->redraw();
803                         bv->fitCursor();
804                 }
805                 bv->owner()->allowInput();
806         }
807
808         if ((result & LaTeX::ERRORS)) {
809                 int num_errors = latex.getNumErrors();
810                 string s;
811                 string t;
812                 if (num_errors == 1) {
813                         s = _("One error detected");
814                         t = _("You should try to fix it.");
815                 } else {
816                         s = tostr(num_errors);
817                         s += _(" errors detected.");
818                         t = _("You should try to fix them.");
819                 }
820                 string head;
821                 split(command, head, ' ');
822                 Alert::alert(_("There were errors during running of ") + head,
823                            s, t);
824                 return false;
825         } else if (result & LaTeX::NO_OUTPUT) {
826                 string const s = _("The operation resulted in");
827                 string const t = _("an empty file.");
828                 Alert::alert(_("Resulting file is empty"), s, t);
829                 return false;
830         }
831         return true;
832 }
833
834
835 bool Converters::runLaTeX(Buffer const * buffer, string const & command)
836 {
837         if (!buffer)
838                 return false;
839
840         BufferView * bv = buffer->getUser();
841         string name = buffer->getLatexName();
842         bool need_redraw = false;
843
844         if (bv) {
845                 bv->owner()->prohibitInput();
846                 bv->owner()->message(_("Running LaTeX..."));
847                 // Remove all error insets
848                 need_redraw = bv->removeAutoInsets();
849         }
850
851
852         // do the LaTex run(s)
853         TeXErrors terr;
854         LaTeX latex(command, name, buffer->filePath());
855         int result = latex.run(terr,
856                                bv ? bv->owner()->getLyXFunc() : 0);
857         
858
859         if (bv) {
860                 if ((result & LaTeX::ERRORS)) {
861                         // Insert all errors as errors boxes
862                         bv->insertErrors(terr);
863                         need_redraw = true;
864                 }
865
866                 // if we removed error insets before we ran LaTeX or if we inserted
867                 // error insets after we ran LaTeX this must be run:
868                 if (need_redraw) {
869                         bv->redraw();
870                         bv->fitCursor();
871                 }
872         }
873
874         // check return value from latex.run().
875         if ((result & LaTeX::NO_LOGFILE)) {
876                 Alert::alert(_("LaTeX did not work!"),
877                            _("Missing log file:"), name);
878         } else if ((result & LaTeX::ERRORS)) {
879                 int num_errors = latex.getNumErrors();
880                 string s;
881                 string t;
882                 if (num_errors == 1) {
883                         s = _("One error detected");
884                         t = _("You should try to fix it.");
885                 } else {
886                         s = tostr(num_errors);
887                         s += _(" errors detected.");
888                         t = _("You should try to fix them.");
889                 }
890                 Alert::alert(_("There were errors during the LaTeX run."),
891                            s, t);
892         }  else if (result & LaTeX::NO_OUTPUT) {
893                 string const s = _("The operation resulted in");
894                 string const t = _("an empty file.");
895                 Alert::alert(_("Resulting file is empty"), s, t);
896         }
897
898         if (bv)
899                 bv->owner()->allowInput();
900  
901         int const ERROR_MASK = 
902                         LaTeX::NO_LOGFILE |
903                         LaTeX::ERRORS |
904                         LaTeX::NO_OUTPUT;
905         
906         return (result & ERROR_MASK) == 0;
907
908 }
909
910
911 string const Converters::papersize(Buffer const * buffer)
912 {
913         char real_papersize = buffer->params.papersize;
914         if (real_papersize == BufferParams::PAPER_DEFAULT)
915                 real_papersize = lyxrc.default_papersize;
916
917         switch (real_papersize) {
918         case BufferParams::PAPER_A3PAPER:
919                 return "a3";
920         case BufferParams::PAPER_A4PAPER:
921                 return "a4";
922         case BufferParams::PAPER_A5PAPER:
923                 return "a5";
924         case BufferParams::PAPER_B5PAPER:
925                 return "b5";
926         case BufferParams::PAPER_EXECUTIVEPAPER:
927                 return "foolscap";
928         case BufferParams::PAPER_LEGALPAPER:
929                 return "legal";
930         case BufferParams::PAPER_USLETTER:
931         default:
932                 return "letter";
933         }
934 }
935
936
937 string const Converters::dvips_options(Buffer const * buffer)
938 {
939         string result;
940         if (!buffer)
941                 return result;
942
943         if (buffer->params.use_geometry
944             && buffer->params.papersize2 == BufferParams::VM_PAPER_CUSTOM
945             && !lyxrc.print_paper_dimension_flag.empty()
946             && !buffer->params.paperwidth.empty()
947             && !buffer->params.paperheight.empty()) {
948                 // using a custom papersize
949                 result = lyxrc.print_paper_dimension_flag;
950                 result += ' ' + buffer->params.paperwidth;
951                 result += ',' + buffer->params.paperheight;
952         } else {
953                 string const paper_option = papersize(buffer);
954                 if (paper_option != "letter" ||
955                     buffer->params.orientation != BufferParams::ORIENTATION_LANDSCAPE) {
956                         // dvips won't accept -t letter -t landscape.  In all other
957                         // cases, include the paper size explicitly.
958                         result = lyxrc.print_paper_flag;
959                         result += ' ' + paper_option;
960                 }
961         }
962         if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE &&
963             buffer->params.papersize2 != BufferParams::VM_PAPER_CUSTOM)
964                 result += ' ' + lyxrc.print_landscape_flag;
965         return result;
966 }
967
968
969 string const Converters::dvipdfm_options(Buffer const * buffer)
970 {
971         string result;
972         if (!buffer)
973                 return result;
974
975         if (buffer->params.papersize2 != BufferParams::VM_PAPER_CUSTOM) {
976                 string const paper_size = papersize(buffer);
977                 if (paper_size != "b5" && paper_size != "foolscap")
978                         result = "-p "+ paper_size;
979
980                 if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
981                         result += " -l";
982         }
983
984         return result;
985 }
986
987
988 vector<Converters::Vertex> Converters::vertices_;
989
990
991 /// The global instance
992 Formats formats;
993 Converters converters;
994
995 // The global copy after reading lyxrc.defaults
996 Formats system_formats;
997 Converters system_converters;