1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation
19 #include "converter.h"
22 #include "bufferview_funcs.h"
25 #include "lyx_gui_misc.h"
26 #include "lyx_cb.h" // ShowMessage()
28 #include "BufferView.h"
31 #include "frontends/Alert.h"
33 #include "support/filetools.h"
34 #include "support/lyxfunctional.h"
35 #include "support/path.h"
36 #include "support/systemcall.h"
48 string const token_from("$$i");
49 string const token_base("$$b");
50 string const token_to("$$o");
52 //////////////////////////////////////////////////////////////////////////////
55 string const add_options(string const & command, string const & options)
58 string const tail = split(command, head, ' ');
59 return head + ' ' + options + ' ' + tail;
64 //////////////////////////////////////////////////////////////////////////////
66 bool Format::dummy() const
68 return extension().empty();
72 bool Format::isChildFormat() const
76 return isdigit(name_[name_.length() - 1]);
80 string const Format::parentFormat() const
82 return name_.substr(0, name_.length() - 1);
85 //////////////////////////////////////////////////////////////////////////////
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
91 FormatList::const_iterator cit =
92 find_if(formatlist.begin(), formatlist.end(),
93 lyx::compare_memfun(&Format::name, name));
94 if (cit != formatlist.end())
101 int Formats::getNumber(string const & name) const
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();
113 void Formats::add(string const & name)
115 if (!getFormat(name))
116 add(name, name, name, string());
120 void Formats::add(string const & name, string const & extension,
121 string const & prettyname, string const & shortcut)
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,
130 string viewer = it->viewer();
131 *it = Format(name, extension, prettyname, shortcut, viewer);
136 void Formats::erase(string const & name)
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);
148 std::sort(formatlist.begin(), formatlist.end());
152 void Formats::setViewer(string const & name, string const & command)
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);
163 bool Formats::view(Buffer const * buffer, string const & filename,
164 string const & format_name) const
166 if (filename.empty())
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));
180 string command = format->viewer();
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")
188 command += " " + paper_size;
189 if (buffer->params.orientation
190 == BufferParams::ORIENTATION_LANDSCAPE)
194 command += " " + QuoteName(OnlyFilename((filename)));
196 lyxerr[Debug::FILES] << "Executing command: " << command << endl;
197 ShowMessage(buffer, _("Executing command:"), command);
199 Path p(OnlyPath(filename));
201 int const res = one.startscript(Systemcall::DontWait, command);
204 Alert::alert(_("Cannot view file"),
205 _("Error while executing"),
206 command.substr(0, 50));
213 string const Formats::prettyName(string const & name) const
215 Format const * format = getFormat(name);
217 return format->prettyname();
223 string const Formats::extension(string const & name) const
225 Format const * format = getFormat(name);
227 return format->extension();
232 //////////////////////////////////////////////////////////////////////////////
234 void Converter::readFlags()
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")
243 else if (flag_name == "originaldir")
245 else if (flag_name == "needaux")
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;
255 if (!result_dir.empty() && result_file.empty())
256 result_file = "index." + formats.extension(to);
257 //if (!contains(command, token_from))
262 bool operator<(Converter const & a, Converter const & b)
264 int const i = compare_no_case(a.From->prettyname(),
265 b.From->prettyname());
267 return compare_no_case(a.To->prettyname(), b.To->prettyname())
273 //////////////////////////////////////////////////////////////////////////////
275 class compare_Converter {
277 compare_Converter(string const & f, string const & t)
279 bool operator()(Converter const & c) {
280 return c.from == from && c.to == to;
288 Converter const * Converters::getConverter(string const & from,
291 ConverterList::const_iterator cit =
292 find_if(converterlist_.begin(), converterlist_.end(),
293 compare_Converter(from, to));
294 if (cit != converterlist_.end())
301 int Converters::getNumber(string const & from, string const & to)
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();
313 void Converters::add(string const & from, string const & to,
314 string const & command, string const & flags)
318 ConverterList::iterator it = find_if(converterlist_.begin(),
319 converterlist_.end(),
320 compare_Converter(from, to));
322 Converter converter(from, to, command, flags);
323 if (it != converterlist_.end() && !flags.empty() && flags[0] == '*') {
325 converter.command = command;
326 converter.flags = flags;
328 converter.readFlags();
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.
336 if (it == converterlist_.end()) {
337 converterlist_.push_back(converter);
339 converter.From = it->From;
340 converter.To = it->To;
346 void Converters::erase(string const & from, string const & to)
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);
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)
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);
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)
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);
386 void Converters::sort()
388 std::sort(converterlist_.begin(), converterlist_.end());
392 int Converters::bfs_init(string const & start, bool clear_visited)
394 int const s = formats.getNumber(start);
400 fill(visited_.begin(), visited_.end(), false);
401 if (visited_[s] == false) {
409 vector<Format const *> const
410 Converters::getReachableTo(string const & target, bool clear_visited)
412 vector<Format const *> result;
413 int const s = bfs_init(target, clear_visited);
417 while (!Q_.empty()) {
418 int const i = Q_.front();
420 if (i != s || target != "lyx") {
421 result.push_back(&formats.get(i));
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;
438 vector<Format const *> const
439 Converters::getReachable(string const & from, bool only_viewable,
442 vector<Format const *> result;
444 if (bfs_init(from, clear_visited) < 0)
447 while (!Q_.empty()) {
448 int const i = Q_.front();
450 Format const & format = formats.get(i);
451 if (format.name() == "lyx")
453 if (!only_viewable || !format.viewer().empty() ||
454 format.isChildFormat())
455 result.push_back(&format);
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;
472 bool Converters::isReachable(string const & from, string const & to)
477 int const s = bfs_init(from);
478 int const t = formats.getNumber(to);
482 while (!Q_.empty()) {
483 int const i = Q_.front();
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;
504 Converters::EdgePath const
505 Converters::getPath(string const & from, string const & to)
511 int const s = bfs_init(from);
512 int t = formats.getNumber(to);
516 vector<int> prev_edge(formats.size());
517 vector<int> prev_vertex(formats.size());
520 while (!Q_.empty()) {
521 int const i = Q_.front();
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]) {
538 int const k = cit - beg;
539 prev_edge[j] = vertices_[i].out_edges[k];
547 path.push_back(prev_edge[t]);
550 reverse(path.begin(), path.end());
555 bool Converters::usePdflatex(EdgePath const & path)
557 for (EdgePath::const_iterator cit = path.begin();
558 cit != path.end(); ++cit) {
559 Converter const & conv = converterlist_[*cit];
561 return contains(conv.to, "pdf");
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,
572 to_file = ChangeExtension(to_file_base,
573 formats.extension(to_format));
575 if (from_format == to_format)
576 return move(from_file, to_file, false);
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));
587 string path = OnlyPath(from_file);
590 bool run_latex = false;
591 string from_base = ChangeExtension(from_file, "");
592 string to_base = ChangeExtension(to_file, "");
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";
600 lyxerr[Debug::FILES] << "Converting from "
601 << conv.from << " to " << conv.to << endl;
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)));
612 string command = subst(conv.command, token_from, "");
613 lyxerr[Debug::FILES] << "Running " << command << endl;
614 if (!runLaTeX(buffer, command))
617 if (conv.need_aux && !run_latex
618 && !latex_command_.empty()) {
620 << "Running " << latex_command_
621 << " to update aux file"<< endl;
622 runLaTeX(buffer, latex_command_);
625 string infile2 = (conv.original_dir)
626 ? infile : MakeRelPath(infile, path);
627 string outfile2 = (conv.original_dir)
628 ? outfile : MakeRelPath(outfile, path);
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));
635 if (!conv.parselog.empty())
636 command += " 2> " + QuoteName(infile2 + ".out");
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));
645 lyxerr[Debug::FILES] << "Calling " << command << endl;
647 ShowMessage(buffer, _("Executing command:"), command);
649 Systemcall::Starttype type = (dummy)
650 ? Systemcall::DontWait : Systemcall::Wait;
653 if (conv.original_dir && buffer) {
654 Path p(buffer->filePath());
655 res = one.startscript(type, command);
657 res = one.startscript(type, command);
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))
670 if (conv.to == "program")
671 Alert::alert(_("There were errors during the Build process."),
672 _("You should try to fix them."));
674 Alert::alert(_("Cannot convert file"),
675 "Error while executing",
676 command.substr(0, 50));
682 Converter const & conv = converterlist_[edgepath.back()];
683 if (conv.To->dummy())
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:"),
704 return move(outfile, to_file, conv.latex);
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)
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);
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;
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);
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)
747 return convert(buffer, from_file, to_file_base, from_format, to_format,
752 void Converters::buildGraph()
754 vertices_ = vector<Vertex>(formats.size());
755 visited_.resize(formats.size());
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());
768 bool Converters::formatIsUsed(string const & format)
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)
780 bool Converters::scanLog(Buffer const * buffer, string const & command,
781 string const & filename)
786 BufferView * bv = buffer->getUser();
787 bool need_redraw = false;
789 bv->owner()->prohibitInput();
790 // Remove all error insets
791 need_redraw = bv->removeAutoInsets();
794 LaTeX latex("", filename, "");
796 int result = latex.scanLogFile(terr);
798 if ((result & LaTeX::ERRORS)) {
799 // Insert all errors as errors boxes
800 bv->insertErrors(terr);
807 bv->owner()->allowInput();
810 if ((result & LaTeX::ERRORS)) {
811 int num_errors = latex.getNumErrors();
814 if (num_errors == 1) {
815 s = _("One error detected");
816 t = _("You should try to fix it.");
818 s = tostr(num_errors);
819 s += _(" errors detected.");
820 t = _("You should try to fix them.");
823 split(command, head, ' ');
824 Alert::alert(_("There were errors during running of ") + head,
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);
837 bool Converters::runLaTeX(Buffer const * buffer, string const & command)
842 BufferView * bv = buffer->getUser();
843 string name = buffer->getLatexName();
844 bool need_redraw = false;
847 bv->owner()->prohibitInput();
848 bv->owner()->message(_("Running LaTeX..."));
849 // Remove all error insets
850 need_redraw = bv->removeAutoInsets();
854 // do the LaTex run(s)
856 LaTeX latex(command, name, buffer->filePath());
857 int result = latex.run(terr,
858 bv ? bv->owner()->getLyXFunc() : 0);
862 if ((result & LaTeX::ERRORS)) {
863 // Insert all errors as errors boxes
864 bv->insertErrors(terr);
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:
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();
884 if (num_errors == 1) {
885 s = _("One error detected");
886 t = _("You should try to fix it.");
888 s = tostr(num_errors);
889 s += _(" errors detected.");
890 t = _("You should try to fix them.");
892 Alert::alert(_("There were errors during the LaTeX run."),
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);
901 bv->owner()->allowInput();
903 int const ERROR_MASK =
908 return (result & ERROR_MASK) == 0;
913 string const Converters::papersize(Buffer const * buffer)
915 char real_papersize = buffer->params.papersize;
916 if (real_papersize == BufferParams::PAPER_DEFAULT)
917 real_papersize = lyxrc.default_papersize;
919 switch (real_papersize) {
920 case BufferParams::PAPER_A3PAPER:
922 case BufferParams::PAPER_A4PAPER:
924 case BufferParams::PAPER_A5PAPER:
926 case BufferParams::PAPER_B5PAPER:
928 case BufferParams::PAPER_EXECUTIVEPAPER:
930 case BufferParams::PAPER_LEGALPAPER:
932 case BufferParams::PAPER_USLETTER:
939 string const Converters::dvips_options(Buffer const * buffer)
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;
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;
964 if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE &&
965 buffer->params.papersize2 != BufferParams::VM_PAPER_CUSTOM)
966 result += ' ' + lyxrc.print_landscape_flag;
971 string const Converters::dvipdfm_options(Buffer const * buffer)
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;
982 if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
990 vector<Converters::Vertex> Converters::vertices_;
993 /// The global instance
995 Converters converters;
997 // The global copy after reading lyxrc.defaults
998 Formats system_formats;
999 Converters system_converters;