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"
24 #include "frontends/LyXView.h"
25 #include "lyx_cb.h" // ShowMessage()
27 #include "BufferView.h"
30 #include "frontends/Alert.h"
32 #include "support/filetools.h"
33 #include "support/lyxfunctional.h"
34 #include "support/path.h"
35 #include "support/systemcall.h"
37 #ifndef CXX_GLOBAL_CSTD
51 string const token_from("$$i");
52 string const token_base("$$b");
53 string const token_to("$$o");
54 string const token_path("$$p");
56 //////////////////////////////////////////////////////////////////////////////
59 string const add_options(string const & command, string const & options)
62 string const tail = split(command, head, ' ');
63 return head + ' ' + options + ' ' + tail;
68 //////////////////////////////////////////////////////////////////////////////
70 bool Format::dummy() const
72 return extension().empty();
76 bool Format::isChildFormat() const
80 return isdigit(name_[name_.length() - 1]);
84 string const Format::parentFormat() const
86 return name_.substr(0, name_.length() - 1);
89 //////////////////////////////////////////////////////////////////////////////
91 // This method should return a reference, and throw an exception
92 // if the format named name cannot be found (Lgb)
93 Format const * Formats::getFormat(string const & name) const
95 FormatList::const_iterator cit =
96 find_if(formatlist.begin(), formatlist.end(),
97 lyx::compare_memfun(&Format::name, name));
98 if (cit != formatlist.end())
105 int Formats::getNumber(string const & name) const
107 FormatList::const_iterator cit =
108 find_if(formatlist.begin(), formatlist.end(),
109 lyx::compare_memfun(&Format::name, name));
110 if (cit != formatlist.end())
111 return cit - formatlist.begin();
117 void Formats::add(string const & name)
119 if (!getFormat(name))
120 add(name, name, name, string());
124 void Formats::add(string const & name, string const & extension,
125 string const & prettyname, string const & shortcut)
127 FormatList::iterator it =
128 find_if(formatlist.begin(), formatlist.end(),
129 lyx::compare_memfun(&Format::name, name));
130 if (it == formatlist.end())
131 formatlist.push_back(Format(name, extension, prettyname,
134 string viewer = it->viewer();
135 *it = Format(name, extension, prettyname, shortcut, viewer);
140 void Formats::erase(string const & name)
142 FormatList::iterator it =
143 find_if(formatlist.begin(), formatlist.end(),
144 lyx::compare_memfun(&Format::name, name));
145 if (it != formatlist.end())
146 formatlist.erase(it);
152 std::sort(formatlist.begin(), formatlist.end());
156 void Formats::setViewer(string const & name, string const & command)
159 FormatList::iterator it =
160 find_if(formatlist.begin(), formatlist.end(),
161 lyx::compare_memfun(&Format::name, name));
162 if (it != formatlist.end())
163 it->setViewer(command);
167 bool Formats::view(Buffer const * buffer, string const & filename,
168 string const & format_name) const
170 if (filename.empty())
173 Format const * format = getFormat(format_name);
174 if (format && format->viewer().empty() &&
175 format->isChildFormat())
176 format = getFormat(format->parentFormat());
177 if (!format || format->viewer().empty()) {
178 Alert::alert(_("Cannot view file"),
179 _("No information for viewing ")
180 + prettyName(format_name));
184 string command = format->viewer();
186 if (format_name == "dvi" &&
187 !lyxrc.view_dvi_paper_option.empty()) {
188 command += " " + lyxrc.view_dvi_paper_option;
189 string paper_size = converters.papersize(buffer);
190 if (paper_size == "letter")
192 command += " " + paper_size;
193 if (buffer->params.orientation
194 == BufferParams::ORIENTATION_LANDSCAPE)
198 if (!contains(command, token_from))
199 command += " " + token_from;
201 command = subst(command, token_from,
202 QuoteName(OnlyFilename(filename)));
203 command = subst(command, token_path, QuoteName(OnlyPath(filename)));
205 lyxerr[Debug::FILES] << "Executing command: " << command << endl;
206 ShowMessage(buffer, _("Executing command:"), command);
208 Path p(OnlyPath(filename));
210 int const res = one.startscript(Systemcall::DontWait, command);
213 Alert::alert(_("Cannot view file"),
214 _("Error while executing"),
215 command.substr(0, 50));
222 string const Formats::prettyName(string const & name) const
224 Format const * format = getFormat(name);
226 return format->prettyname();
232 string const Formats::extension(string const & name) const
234 Format const * format = getFormat(name);
236 return format->extension();
241 //////////////////////////////////////////////////////////////////////////////
243 void Converter::readFlags()
245 string flag_list(flags);
246 while (!flag_list.empty()) {
247 string flag_name, flag_value;
248 flag_list = split(flag_list, flag_value, ',');
249 flag_value = split(flag_value, flag_name, '=');
250 if (flag_name == "latex")
252 else if (flag_name == "originaldir")
254 else if (flag_name == "needaux")
256 else if (flag_name == "resultdir")
257 result_dir = (flag_value.empty())
258 ? token_base : flag_value;
259 else if (flag_name == "resultfile")
260 result_file = flag_value;
261 else if (flag_name == "parselog")
262 parselog = flag_value;
264 if (!result_dir.empty() && result_file.empty())
265 result_file = "index." + formats.extension(to);
266 //if (!contains(command, token_from))
271 bool operator<(Converter const & a, Converter const & b)
273 // use the compare_ascii_no_case instead of compare_no_case,
274 // because in turkish, 'i' is not the lowercase version of 'I',
275 // and thus turkish locale breaks parsing of tags.
276 int const i = compare_ascii_no_case(a.From->prettyname(),
277 b.From->prettyname());
279 return compare_ascii_no_case(a.To->prettyname(), b.To->prettyname())
285 //////////////////////////////////////////////////////////////////////////////
287 class compare_Converter {
289 compare_Converter(string const & f, string const & t)
291 bool operator()(Converter const & c) {
292 return c.from == from && c.to == to;
300 Converter const * Converters::getConverter(string const & from,
303 ConverterList::const_iterator cit =
304 find_if(converterlist_.begin(), converterlist_.end(),
305 compare_Converter(from, to));
306 if (cit != converterlist_.end())
313 int Converters::getNumber(string const & from, string const & to)
315 ConverterList::const_iterator cit =
316 find_if(converterlist_.begin(), converterlist_.end(),
317 compare_Converter(from, to));
318 if (cit != converterlist_.end())
319 return cit - converterlist_.begin();
325 void Converters::add(string const & from, string const & to,
326 string const & command, string const & flags)
330 ConverterList::iterator it = find_if(converterlist_.begin(),
331 converterlist_.end(),
332 compare_Converter(from, to));
334 Converter converter(from, to, command, flags);
335 if (it != converterlist_.end() && !flags.empty() && flags[0] == '*') {
337 converter.command = command;
338 converter.flags = flags;
340 converter.readFlags();
342 if (converter.latex && (latex_command_.empty() || to == "dvi"))
343 latex_command_ = subst(command, token_from, "");
344 // If we have both latex & pdflatex, we set latex_command to latex.
345 // The latex_command is used to update the .aux file when running
346 // a converter that uses it.
348 if (it == converterlist_.end()) {
349 converterlist_.push_back(converter);
351 converter.From = it->From;
352 converter.To = it->To;
358 void Converters::erase(string const & from, string const & to)
360 ConverterList::iterator it = find_if(converterlist_.begin(),
361 converterlist_.end(),
362 compare_Converter(from, to));
363 if (it != converterlist_.end())
364 converterlist_.erase(it);
368 // This method updates the pointers From and To in all the converters.
369 // The code is not very efficient, but it doesn't matter as the number
370 // of formats and converters is small.
371 // Furthermore, this method is called only on startup, or after
372 // adding/deleting a format in FormPreferences (the latter calls can be
373 // eliminated if the formats in the Formats class are stored using a map or
374 // a list (instead of a vector), but this will cause other problems).
375 void Converters::update(Formats const & formats)
377 ConverterList::iterator it = converterlist_.begin();
378 ConverterList::iterator end = converterlist_.end();
379 for (; it != end; ++it) {
380 it->From = formats.getFormat(it->from);
381 it->To = formats.getFormat(it->to);
386 // This method updates the pointers From and To in the last converter.
387 // It is called when adding a new converter in FormPreferences
388 void Converters::updateLast(Formats const & formats)
390 if (converterlist_.begin() != converterlist_.end()) {
391 ConverterList::iterator it = converterlist_.end() - 1;
392 it->From = formats.getFormat(it->from);
393 it->To = formats.getFormat(it->to);
398 void Converters::sort()
400 std::sort(converterlist_.begin(), converterlist_.end());
404 int Converters::bfs_init(string const & start, bool clear_visited)
406 int const s = formats.getNumber(start);
412 fill(visited_.begin(), visited_.end(), false);
413 if (visited_[s] == false) {
421 vector<Format const *> const
422 Converters::getReachableTo(string const & target, bool clear_visited)
424 vector<Format const *> result;
425 int const s = bfs_init(target, clear_visited);
429 while (!Q_.empty()) {
430 int const i = Q_.front();
432 if (i != s || target != "lyx") {
433 result.push_back(&formats.get(i));
436 vector<int>::iterator it = vertices_[i].in_vertices.begin();
437 vector<int>::iterator end = vertices_[i].in_vertices.end();
438 for (; it != end; ++it) {
439 if (!visited_[*it]) {
440 visited_[*it] = true;
450 vector<Format const *> const
451 Converters::getReachable(string const & from, bool only_viewable,
454 vector<Format const *> result;
456 if (bfs_init(from, clear_visited) < 0)
459 while (!Q_.empty()) {
460 int const i = Q_.front();
462 Format const & format = formats.get(i);
463 if (format.name() == "lyx")
465 if (!only_viewable || !format.viewer().empty() ||
466 format.isChildFormat())
467 result.push_back(&format);
469 vector<int>::const_iterator cit =
470 vertices_[i].out_vertices.begin();
471 vector<int>::const_iterator end =
472 vertices_[i].out_vertices.end();
473 for (; cit != end; ++cit)
474 if (!visited_[*cit]) {
475 visited_[*cit] = true;
484 bool Converters::isReachable(string const & from, string const & to)
489 int const s = bfs_init(from);
490 int const t = formats.getNumber(to);
494 while (!Q_.empty()) {
495 int const i = Q_.front();
500 vector<int>::const_iterator cit =
501 vertices_[i].out_vertices.begin();
502 vector<int>::const_iterator end =
503 vertices_[i].out_vertices.end();
504 for (; cit != end; ++cit) {
505 if (!visited_[*cit]) {
506 visited_[*cit] = true;
516 Converters::EdgePath const
517 Converters::getPath(string const & from, string const & to)
523 int const s = bfs_init(from);
524 int t = formats.getNumber(to);
528 vector<int> prev_edge(formats.size());
529 vector<int> prev_vertex(formats.size());
532 while (!Q_.empty()) {
533 int const i = Q_.front();
540 vector<int>::const_iterator beg =
541 vertices_[i].out_vertices.begin();
542 vector<int>::const_iterator cit = beg;
543 vector<int>::const_iterator end =
544 vertices_[i].out_vertices.end();
545 for (; cit != end; ++cit)
546 if (!visited_[*cit]) {
550 int const k = cit - beg;
551 prev_edge[j] = vertices_[i].out_edges[k];
559 path.push_back(prev_edge[t]);
562 reverse(path.begin(), path.end());
567 bool Converters::usePdflatex(EdgePath const & path)
569 for (EdgePath::const_iterator cit = path.begin();
570 cit != path.end(); ++cit) {
571 Converter const & conv = converterlist_[*cit];
573 return contains(conv.to, "pdf");
579 bool Converters::convert(Buffer const * buffer,
580 string const & from_file, string const & to_file_base,
581 string const & from_format, string const & to_format,
584 to_file = ChangeExtension(to_file_base,
585 formats.extension(to_format));
587 if (from_format == to_format)
588 return move(from_file, to_file, false);
590 EdgePath edgepath = getPath(from_format, to_format);
591 if (edgepath.empty()) {
595 string path = OnlyPath(from_file);
598 bool run_latex = false;
599 string from_base = ChangeExtension(from_file, "");
600 string to_base = ChangeExtension(to_file, "");
602 string outfile = from_file;
603 for (EdgePath::const_iterator cit = edgepath.begin();
604 cit != edgepath.end(); ++cit) {
605 Converter const & conv = converterlist_[*cit];
606 bool dummy = conv.To->dummy() && conv.to != "program";
608 lyxerr[Debug::FILES] << "Converting from "
609 << conv.from << " to " << conv.to << endl;
611 outfile = conv.result_dir.empty()
612 ? ChangeExtension(from_file, conv.To->extension())
613 : AddName(subst(conv.result_dir,
614 token_base, from_base),
615 subst(conv.result_file,
616 token_base, OnlyFilename(from_base)));
620 string command = subst(conv.command, token_from, "");
621 lyxerr[Debug::FILES] << "Running " << command << endl;
622 if (!runLaTeX(buffer, command))
625 if (conv.need_aux && !run_latex
626 && !latex_command_.empty()) {
628 << "Running " << latex_command_
629 << " to update aux file"<< endl;
630 runLaTeX(buffer, latex_command_);
633 string infile2 = (conv.original_dir)
634 ? infile : MakeRelPath(infile, path);
635 string outfile2 = (conv.original_dir)
636 ? outfile : MakeRelPath(outfile, path);
638 string command = conv.command;
639 command = subst(command, token_from, QuoteName(infile2));
640 command = subst(command, token_base, QuoteName(from_base));
641 command = subst(command, token_to, QuoteName(outfile2));
642 command = LibScriptSearch(command);
644 if (!conv.parselog.empty())
645 command += " 2> " + QuoteName(infile2 + ".out");
647 if (conv.from == "dvi" && conv.to == "ps")
648 command = add_options(command,
649 dvips_options(buffer));
650 else if (conv.from == "dvi" && prefixIs(conv.to, "pdf"))
651 command = add_options(command,
652 dvipdfm_options(buffer));
654 lyxerr[Debug::FILES] << "Calling " << command << endl;
656 ShowMessage(buffer, _("Executing command:"), command);
658 Systemcall::Starttype type = (dummy)
659 ? Systemcall::DontWait : Systemcall::Wait;
662 if (conv.original_dir && buffer) {
663 Path p(buffer->filePath());
664 res = one.startscript(type, command);
666 res = one.startscript(type, command);
668 if (!conv.parselog.empty()) {
669 string const logfile = infile2 + ".log";
670 string const script = LibScriptSearch(conv.parselog);
671 string const command2 = script +
672 " < " + QuoteName(infile2 + ".out") +
673 " > " + QuoteName(logfile);
674 one.startscript(Systemcall::Wait, command2);
675 if (!scanLog(buffer, command, logfile))
680 if (conv.to == "program")
681 Alert::alert(_("There were errors during the Build process."),
682 _("You should try to fix them."));
684 Alert::alert(_("Cannot convert file"),
685 "Error while executing",
686 command.substr(0, 50));
692 Converter const & conv = converterlist_[edgepath.back()];
693 if (conv.To->dummy())
697 if (!conv.result_dir.empty()) {
698 to_file = AddName(subst(conv.result_dir, token_base, to_base),
699 subst(conv.result_file,
700 token_base, OnlyFilename(to_base)));
701 if (from_base != to_base) {
702 string from = subst(conv.result_dir,
703 token_base, from_base);
704 string to = subst(conv.result_dir,
705 token_base, to_base);
706 if (!lyx::rename(from, to)) {
707 Alert::alert(_("Error while trying to move directory:"),
714 return move(outfile, to_file, conv.latex);
717 // If from = /path/file.ext and to = /path2/file2.ext2 then this method
718 // moves each /path/file*.ext file to /path2/file2*.ext2'
719 bool Converters::move(string const & from, string const & to, bool copy)
724 bool no_errors = true;
725 string const path = OnlyPath(from);
726 string const base = OnlyFilename(ChangeExtension(from, ""));
727 string const to_base = ChangeExtension(to, "");
728 string const to_extension = GetExtension(to);
730 vector<string> files = DirList(OnlyPath(from), GetExtension(from));
731 for (vector<string>::const_iterator it = files.begin();
732 it != files.end(); ++it)
733 if (prefixIs(*it, base)) {
734 string const from2 = path + *it;
735 string to2 = to_base + it->substr(base.length());
736 to2 = ChangeExtension(to2, to_extension);
737 lyxerr[Debug::FILES] << "moving " << from2
738 << " to " << to2 << endl;
739 bool const moved = (copy)
740 ? lyx::copy(from2, to2)
741 : lyx::rename(from2, to2);
742 if (!moved && no_errors) {
743 Alert::alert(_("Error while trying to move file:"),
744 from2, _("to ") + to2);
752 bool Converters::convert(Buffer const * buffer,
753 string const & from_file, string const & to_file_base,
754 string const & from_format, string const & to_format)
757 return convert(buffer, from_file, to_file_base, from_format, to_format,
762 void Converters::buildGraph()
764 vertices_ = vector<Vertex>(formats.size());
765 visited_.resize(formats.size());
767 for (ConverterList::iterator it = converterlist_.begin();
768 it != converterlist_.end(); ++it) {
769 int const s = formats.getNumber(it->from);
770 int const t = formats.getNumber(it->to);
771 vertices_[t].in_vertices.push_back(s);
772 vertices_[s].out_vertices.push_back(t);
773 vertices_[s].out_edges.push_back(it - converterlist_.begin());
778 bool Converters::formatIsUsed(string const & format)
780 ConverterList::const_iterator cit = converterlist_.begin();
781 ConverterList::const_iterator end = converterlist_.end();
782 for (; cit != end; ++cit) {
783 if (cit->from == format || cit->to == format)
790 bool Converters::scanLog(Buffer const * buffer, string const & command,
791 string const & filename)
796 BufferView * bv = buffer->getUser();
798 bv->owner()->prohibitInput();
799 // all error insets should have been removed by now
802 LaTeX latex("", filename, "");
804 int result = latex.scanLogFile(terr);
806 if ((result & LaTeX::ERRORS)) {
807 // Insert all errors as errors boxes
808 bv->insertErrors(terr);
809 #warning repaint() or update() or nothing ?
813 bv->owner()->allowInput();
816 if ((result & LaTeX::ERRORS)) {
817 int num_errors = latex.getNumErrors();
820 if (num_errors == 1) {
821 s = _("One error detected");
822 t = _("You should try to fix it.");
824 s = tostr(num_errors);
825 s += _(" errors detected.");
826 t = _("You should try to fix them.");
829 split(command, head, ' ');
830 Alert::alert(_("There were errors during running of ") + head,
833 } else if (result & LaTeX::NO_OUTPUT) {
834 string const s = _("The operation resulted in");
835 string const t = _("an empty file.");
836 Alert::alert(_("Resulting file is empty"), s, t);
843 bool Converters::runLaTeX(Buffer const * buffer, string const & command)
848 BufferView * bv = buffer->getUser();
851 bv->owner()->prohibitInput();
852 bv->owner()->message(_("Running LaTeX..."));
853 // all the autoinsets have already been removed
856 // do the LaTeX run(s)
857 string name = buffer->getLatexName();
858 LaTeX latex(command, name, buffer->filePath());
860 int result = latex.run(terr,
861 bv ? &bv->owner()->getLyXFunc() : 0);
864 if ((result & LaTeX::ERRORS)) {
865 // Insert all errors as errors boxes
866 bv->insertErrors(terr);
867 #warning repaint() or update() or nothing ?
873 // check return value from latex.run().
874 if ((result & LaTeX::NO_LOGFILE)) {
875 Alert::alert(_("LaTeX did not work!"),
876 _("Missing log file:"), name);
877 } else if ((result & LaTeX::ERRORS)) {
878 int num_errors = latex.getNumErrors();
881 if (num_errors == 1) {
882 s = _("One error detected");
883 t = _("You should try to fix it.");
885 s = tostr(num_errors);
886 s += _(" errors detected.");
887 t = _("You should try to fix them.");
889 Alert::alert(_("There were errors during the LaTeX run."),
891 } else if (result & LaTeX::NO_OUTPUT) {
892 string const s = _("The operation resulted in");
893 string const t = _("an empty file.");
894 Alert::alert(_("Resulting file is empty"), s, t);
898 bv->owner()->allowInput();
900 int const ERROR_MASK =
905 return (result & ERROR_MASK) == 0;
910 string const Converters::papersize(Buffer const * buffer)
912 char real_papersize = buffer->params.papersize;
913 if (real_papersize == BufferParams::PAPER_DEFAULT)
914 real_papersize = lyxrc.default_papersize;
916 switch (real_papersize) {
917 case BufferParams::PAPER_A3PAPER:
919 case BufferParams::PAPER_A4PAPER:
921 case BufferParams::PAPER_A5PAPER:
923 case BufferParams::PAPER_B5PAPER:
925 case BufferParams::PAPER_EXECUTIVEPAPER:
927 case BufferParams::PAPER_LEGALPAPER:
929 case BufferParams::PAPER_USLETTER:
936 string const Converters::dvips_options(Buffer const * buffer)
942 if (buffer->params.use_geometry
943 && buffer->params.papersize2 == BufferParams::VM_PAPER_CUSTOM
944 && !lyxrc.print_paper_dimension_flag.empty()
945 && !buffer->params.paperwidth.empty()
946 && !buffer->params.paperheight.empty()) {
947 // using a custom papersize
948 result = lyxrc.print_paper_dimension_flag;
949 result += ' ' + buffer->params.paperwidth;
950 result += ',' + buffer->params.paperheight;
952 string const paper_option = papersize(buffer);
953 if (paper_option != "letter" ||
954 buffer->params.orientation != BufferParams::ORIENTATION_LANDSCAPE) {
955 // dvips won't accept -t letter -t landscape. In all other
956 // cases, include the paper size explicitly.
957 result = lyxrc.print_paper_flag;
958 result += ' ' + paper_option;
961 if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE &&
962 buffer->params.papersize2 != BufferParams::VM_PAPER_CUSTOM)
963 result += ' ' + lyxrc.print_landscape_flag;
968 string const Converters::dvipdfm_options(Buffer const * buffer)
974 if (buffer->params.papersize2 != BufferParams::VM_PAPER_CUSTOM) {
975 string const paper_size = papersize(buffer);
976 if (paper_size != "b5" && paper_size != "foolscap")
977 result = "-p "+ paper_size;
979 if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
987 vector<Converters::Vertex> Converters::vertices_;
990 /// The global instance
992 Converters converters;
994 // The global copy after reading lyxrc.defaults
995 Formats system_formats;
996 Converters system_converters;