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