1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2000 The LyX Team.
9 * ====================================================== */
14 #pragma implementation
19 #include "converter.h"
21 #include "support/syscall.h"
22 #include "support/path.h"
23 #include "support/filetools.h"
25 #include "bufferview_funcs.h"
28 #include "minibuffer.h"
29 #include "lyx_gui_misc.h"
30 #include "lyx_cb.h" // ShowMessage()
40 //////////////////////////////////////////////////////////////////////////////
42 vector<Command> Converter::commands;
43 string Converter::latex_command;
46 string const add_options(string const & command, string const & options)
49 string const tail = split(command, head, ' ');
50 return head + ' ' + options + ' ' + tail;
53 //////////////////////////////////////////////////////////////////////////////
56 bool Format::dummy() const
58 return extension.empty();
62 void Formats::Add(string const & name)
64 if (formats.find(name) == formats.end())
65 formats[name] = Format(name, name, name, "", "");
69 void Formats::Add(string const & name, string const & extension,
70 string const & prettyname, string const & shortcut)
73 if (prettyname.empty()) {
74 FormatList::iterator it = formats.find(name);
75 if (it != formats.end())
80 string const old_viewer = formats[name].viewer;
81 formats[name] = Format(name, extension, prettyname, shortcut,
86 void Formats::SetViewer(string const & name, string const & command)
88 string command2 = command;
89 if (!command2.empty() && !contains(command2,"$$FName"))
90 command2 += " $$FName";
93 GetFormat(name)->viewer = command2;
97 bool Formats::View(Buffer const * buffer, string const & filename,
98 string const & format_name)
100 if (filename.empty())
103 Format const * format = GetFormat(format_name);
104 if (!format || format->viewer.empty()) {
105 WriteAlert(_("Can not view file"),
106 _("No information for viewing ")
107 + PrettyName(format_name));
111 string command = format->viewer;
113 if (format_name == "dvi" &&
114 !lyxrc.view_dvi_paper_option.empty()) {
115 string options = lyxrc.view_dvi_paper_option;
116 options += " " + Converter::dvi_papersize(buffer);
117 if (buffer->params.orientation
118 == BufferParams::ORIENTATION_LANDSCAPE)
120 command = add_options(command, options);
123 string command2 = subst(command, "$$FName", OnlyFilename(filename));
124 lyxerr << "Executing command: " << command2 << endl;
125 ShowMessage(buffer, _("Executing command:"), command2);
127 command = subst(command, "$$FName", QuoteName(filename));
129 int res = one.startscript(Systemcalls::SystemDontWait, command);
132 WriteAlert(_("Can not view file"),
133 _("Error while executing"),
134 command.substr(0, 50));
141 // This method should return a reference, and throw an exception
142 // if the format named name cannot be found (Lgb)
143 Format * Formats::GetFormat(string const & name)
145 FormatList::iterator it = formats.find(name);
146 if (it != formats.end())
153 string const Formats::PrettyName(string const & name)
155 Format const * format = GetFormat(name);
157 return format->prettyname;
163 string const Formats::Extension(string const & name)
165 Format const * format = GetFormat(name);
167 return format->extension;
174 const Formats::GetAllFormats() const
176 vector<Format> result;
177 for (FormatList::const_iterator cit = formats.begin();
178 cit != formats.end(); ++cit)
179 result.push_back(cit->second);
184 //////////////////////////////////////////////////////////////////////////////
186 // Instead of storing an object we could just store an const reference.
187 // _but_ that is not guaranteed to work in all cases. (Lgb)
188 class compare_Command {
190 compare_Command(Command const & c) : com(c) {}
191 bool operator()(Command const & c) {
192 return c.from == com.from && c.to == com.to;
199 void Converter::Add(string const & from, string const & to,
200 string const & command, string const & flags)
204 Command Com(formats.GetFormat(from), formats.GetFormat(to), command);
205 vector<Command>::iterator it = find_if(commands.begin(),
207 compare_Command(Com));
209 if (command.empty() || command == "none") {
210 if (it != commands.end())
217 string flag_list(flags);
218 while (!flag_list.empty()) {
219 string flag_name, flag_value;
220 flag_list = split(flag_list, flag_value, ',');
221 flag_value = split(flag_value, flag_name, '=');
222 if (flag_name == "*") {
223 if (it != commands.end()) {
225 Com.command = command;
228 else if (flag_name == "importer")
230 else if (flag_name == "latex")
232 else if (flag_name == "originaldir")
233 Com.original_dir = true;
234 else if (flag_name == "needaux")
236 else if (flag_name == "resultdir")
237 Com.result_dir = (flag_value.empty())
238 ? "$$BaseName" : flag_value;
239 else if (flag_name == "resultfile")
240 Com.result_file = flag_value;
241 else if (flag_name == "parselog")
242 Com.parselog = flag_value;
243 else if (flag_name == "disable") {
244 while (!flag_value.empty()) {
246 flag_value = split(flag_value, tmp, '&');
247 Com.disable.push_back(tmp);
251 if (!Com.result_dir.empty() && Com.result_file.empty())
252 Com.result_file = "index." + to;
253 //if (!contains(command, "$$FName"))
256 if (Com.latex && (latex_command.empty() || to == "dvi"))
257 latex_command = command;
258 // If we have both latex & pdflatex, we set latex_command to latex.
259 // The latex_command is used to update the .aux file when running
260 // a converter that uses it.
262 if (it != commands.end()) {
266 commands.push_back(Com);
271 bool enable(vector<Command>::iterator it, string const & from)
273 return find(it->disable.begin(), it->disable.end(), from)
274 == it->disable.end();
278 vector<FormatPair> const
279 Converter::GetReachableTo(string const & target)
281 vector<FormatPair> result;
283 queue< vector<Command>::iterator > Q;
284 for (vector<Command>::iterator it = commands.begin();
285 it != commands.end(); ++it)
286 if (it->to->name == target && it->importer) {
293 vector<Command>::iterator it = Q.front();
295 result.push_back(FormatPair(it->from, 0, ""));
296 for (vector<Command>::iterator it2 = commands.begin();
297 it2 != commands.end(); ++it2)
298 if (!it2->visited && it->from == it2->to &&
309 vector<FormatPair> const
310 Converter::GetReachable(string const & from, bool only_viewable)
312 vector<FormatPair> result;
313 Format const * format = formats.GetFormat(from);
317 if (!only_viewable || !format->viewer.empty())
318 result.push_back(FormatPair(format, 0, ""));
320 queue< vector<Command>::iterator > Q;
321 for (vector<Command>::iterator it = commands.begin();
322 it != commands.end(); ++it)
323 if (it->from->name == from && enable(it, from)
331 vector<Command>::iterator it = Q.front();
333 if (!only_viewable || !it->to->viewer.empty())
334 result.push_back(FormatPair(it->to, it->from,
336 for (vector<Command>::iterator it2 = commands.begin();
337 it2 != commands.end(); ++it2)
338 if (!it2->visited && it->to == it2->from &&
339 enable(it2, from) && !it2->importer) {
349 bool Converter::IsReachable(string const & from, string const & to)
354 queue< vector<Command>::iterator > Q;
355 for (vector<Command>::iterator it = commands.begin();
356 it != commands.end(); ++it)
357 if (it->from->name == from && enable(it, from)) {
364 vector<Command>::iterator it = Q.front();
366 if (it->to->name == to)
368 for (vector<Command>::iterator it2 = commands.begin();
369 it2 != commands.end(); ++it2)
370 if (!it2->visited && it->to == it2->from &&
380 bool Converter::Convert(Buffer const * buffer,
381 string const & from_file, string const & to_file_base,
382 string const & from_format, string const & to_format,
383 string const & using_format, string & to_file)
385 to_file = ChangeExtension(to_file_base,
386 formats.Extension(to_format));
388 if (from_format == to_format)
389 if (from_file != to_file)
390 return lyx::rename(from_file, to_file);
394 queue< vector<Command>::iterator > Q;
395 for (vector<Command>::iterator it = commands.begin();
396 it != commands.end(); ++it)
397 if (it->from->name == from_format && enable(it, from_format)) {
400 it->previous = commands.end();
405 WriteAlert(_("Can not convert file"),
406 ("Unknown format ") + from_format);
411 vector<Command>::iterator it;
414 if (it->to->name == to_format &&
415 (using_format.empty() || using_format == it->from->name)) {
420 for (vector<Command>::iterator it2 = commands.begin();
421 it2 != commands.end(); ++it2)
422 if (!it2->visited && it->to == it2->from &&
423 enable(it2, from_format)) {
431 WriteAlert(_("Can not convert file"),
432 _("No information for converting from ")
433 + formats.PrettyName(from_format) + _(" to ")
434 + formats.PrettyName(to_format));
438 vector< vector<Command>::iterator > S;
439 while (it != commands.end()) {
444 string path = OnlyPath(from_file);
447 bool run_latex = false;
448 string from_base = ChangeExtension(from_file, "");
449 string to_base = ChangeExtension(to_file, "");
451 string outfile = from_file;
452 for (vector< vector<Command>::iterator >::reverse_iterator rit =
453 S.rbegin(); rit != S.rend(); ++rit) {
455 bool dummy = it->to->dummy() && it->to->name != "program";
457 lyxerr << "Converting from "
458 << it->from->name << " to " << it->to->name << endl;
460 outfile = it->result_dir.empty()
461 ? ChangeExtension(from_file, it->to->extension)
462 : AddName(subst(it->result_dir,
463 "$$BaseName", from_base),
464 subst(it->result_file,
465 "$$BaseName", OnlyFilename(from_base)));
468 lyxrc.pdf_mode = it->to->name == "pdf";
469 lyxerr << "Running " << it->command << endl;
471 if (!runLaTeX(buffer, it->command))
474 if (it->need_aux && !run_latex
475 && !latex_command.empty()) {
476 lyxerr << "Running " << latex_command
477 << " to update aux file"<< endl;
478 runLaTeX(buffer, latex_command);
481 string infile2 = (it->original_dir)
482 ? infile : MakeRelPath(infile, path);
483 string outfile2 = (it->original_dir)
484 ? outfile : MakeRelPath(outfile, path);
486 string command = it->command;
487 command = subst(command, "$$FName", QuoteName(infile2));
488 command = subst(command, "$$BaseName", QuoteName(from_base));
489 command = subst(command, "$$OutName", QuoteName(outfile2));
491 if (!it->parselog.empty())
492 command += " 2> " + QuoteName(infile2 + ".out");
494 if (it->from->name == "dvi" && it->to->name == "ps")
495 command = add_options(command,
496 dvips_options(buffer));
498 lyxerr << "Calling " << command << endl;
500 ShowMessage(buffer, _("Executing command:"), command);
502 Systemcalls::Starttype type = (dummy)
503 ? Systemcalls::SystemDontWait : Systemcalls::System;
506 if (it->original_dir && buffer) {
507 Path p(buffer->filepath);
508 res = one.startscript(type, command);
510 res = one.startscript(type, command);
512 if (!it->parselog.empty()) {
513 string const logfile = infile2 + ".log";
514 string const command2 = it->parselog +
515 " < " + QuoteName(infile2 + ".out") +
516 " > " + QuoteName(logfile);
517 one.startscript(Systemcalls::System, command2);
518 if (!scanLog(buffer, command, logfile))
523 if (it->to->name == "program")
524 WriteAlert(_("There were errors during the Build process."),
525 _("You should try to fix them."));
527 WriteAlert(_("Can not convert file"),
528 "Error while executing",
529 command.substr(0, 50));
539 if (!it->result_dir.empty()) {
540 to_file = AddName(subst(it->result_dir,
541 "$$BaseName", to_base),
542 subst(it->result_file,
543 "$$BaseName", OnlyFilename(to_base)));
544 if (from_base != to_base) {
545 string from = subst(it->result_dir,
546 "$$BaseName", from_base);
547 string to = subst(it->result_dir,
548 "$$BaseName", to_base);
549 if (!lyx::rename(from, to)) {
550 WriteAlert(_("Error while trying to move directory:"),
555 } else if (outfile != to_file) {
556 bool moved = (it->latex)
557 ? lyx::copy(outfile, to_file)
558 : lyx::rename(outfile, to_file);
560 WriteAlert(_("Error while trying to move file:"),
561 outfile, _("to ") + to_file);
570 bool Converter::Convert(Buffer const * buffer,
571 string const & from_file, string const & to_file_base,
572 string const & from_format, string const & to_format,
573 string const & using_format)
576 return Convert(buffer, from_file, to_file_base, from_format, to_format,
577 using_format, to_file);
581 string const Converter::SplitFormat(string const & str, string & format)
583 string const using_format = split(str, format, ':');
590 bool Converter::scanLog(Buffer const * buffer, string const & command,
591 string const & filename)
596 BufferView * bv = buffer->getUser();
597 bool need_redraw = false;
600 // Remove all error insets
601 need_redraw = bv->removeAutoInsets();
604 LaTeX latex("", filename, "");
606 int result = latex.scanLogFile(terr);
608 if ((result & LaTeX::ERRORS)) {
609 // Insert all errors as errors boxes
610 bv->insertErrors(terr);
615 bv->fitCursor(bv->text);
620 if ((result & LaTeX::ERRORS)) {
621 int num_errors = latex.getNumErrors();
624 if (num_errors == 1) {
625 s = _("One error detected");
626 t = _("You should try to fix it.");
628 s = tostr(num_errors);
629 s += _(" errors detected.");
630 t = _("You should try to fix them.");
633 split(command, head, ' ');
634 WriteAlert(_("There were errors during running of ") + head,
637 } else if (result & LaTeX::NO_OUTPUT) {
638 string const s = _("The operation resulted in");
639 string const t = _("an empty file.");
640 WriteAlert(_("Resulting file is empty"), s, t);
647 bool Converter::runLaTeX(Buffer const * buffer, string const & command)
652 BufferView * bv = buffer->getUser();
653 string name = buffer->getLatexName();
654 bool need_redraw = false;
658 bv->owner()->getMiniBuffer()->Set(_("Running LaTeX..."));
659 // Remove all error insets
660 need_redraw = bv->removeAutoInsets();
664 // do the LaTex run(s)
666 LaTeX latex(command, name, buffer->filepath);
667 int result = latex.run(terr,
668 bv ? bv->owner()->getMiniBuffer() : 0);
672 if ((result & LaTeX::ERRORS)) {
673 // Insert all errors as errors boxes
674 bv->insertErrors(terr);
678 // if we removed error insets before we ran LaTeX or if we inserted
679 // error insets after we ran LaTeX this must be run:
682 bv->fitCursor(bv->text);
686 // check return value from latex.run().
687 if ((result & LaTeX::NO_LOGFILE)) {
688 WriteAlert(_("LaTeX did not work!"),
689 _("Missing log file:"), name);
690 } else if ((result & LaTeX::ERRORS)) {
691 int num_errors = latex.getNumErrors();
694 if (num_errors == 1) {
695 s = _("One error detected");
696 t = _("You should try to fix it.");
698 s = tostr(num_errors);
699 s += _(" errors detected.");
700 t = _("You should try to fix them.");
702 WriteAlert(_("There were errors during the LaTeX run."),
704 } else if (result & LaTeX::NO_OUTPUT) {
705 string const s = _("The operation resulted in");
706 string const t = _("an empty file.");
707 WriteAlert(_("Resulting file is empty"), s, t);
713 int const ERROR_MASK =
718 return (result & ERROR_MASK) == 0;
723 string const Converter::dvi_papersize(Buffer const * buffer)
725 char real_papersize = buffer->params.papersize;
726 if (real_papersize == BufferParams::PAPER_DEFAULT)
727 real_papersize = lyxrc.default_papersize;
729 switch (real_papersize) {
730 case BufferParams::PAPER_A3PAPER:
732 case BufferParams::PAPER_A4PAPER:
734 case BufferParams::PAPER_A5PAPER:
736 case BufferParams::PAPER_B5PAPER:
738 case BufferParams::PAPER_EXECUTIVEPAPER:
740 case BufferParams::PAPER_LEGALPAPER:
742 case BufferParams::PAPER_USLETTER:
749 string const Converter::dvips_options(Buffer const * buffer)
755 if (buffer->params.use_geometry
756 && buffer->params.papersize2 == BufferParams::VM_PAPER_CUSTOM
757 && !lyxrc.print_paper_dimension_flag.empty()
758 && !buffer->params.paperwidth.empty()
759 && !buffer->params.paperheight.empty()) {
760 // using a custom papersize
761 result = lyxrc.print_paper_dimension_flag;
762 result += ' ' + buffer->params.paperwidth;
763 result += ',' + buffer->params.paperheight;
765 string paper_option = dvi_papersize(buffer);
766 if (paper_option == "us")
767 paper_option = "letter";
768 if (paper_option != "letter" ||
769 buffer->params.orientation != BufferParams::ORIENTATION_LANDSCAPE) {
770 // dvips won't accept -t letter -t landscape. In all other
771 // cases, include the paper size explicitly.
772 result = lyxrc.print_paper_flag;
773 result += ' ' + paper_option;
776 if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
777 result += ' ' + lyxrc.print_landscape_flag;
782 void Converter::init()
786 vector<Command> const Converter::GetAllCommands()
788 vector<Command> result;
789 for (vector<Command>::iterator it = commands.begin();
790 it != commands.end(); ++it)
791 result.push_back(*it);
796 /// The global instance
799 // The global copy of the system lyxrc entries (everything except preferences)
800 Formats system_formats;