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"
24 #include "bufferview_funcs.h"
27 #include "minibuffer.h"
28 #include "lyx_gui_misc.h"
29 #include "lyx_cb.h" // ShowMessage()
39 //////////////////////////////////////////////////////////////////////////////
41 map<string, Format> Formats::formats;
42 vector<Command> Converter::commands;
43 string Converter::latex_command;
46 string const add_options(string const & command, string const & options)
49 string tail = split(command, head, ' ');
50 return head + ' ' + options + ' ' + tail;
53 //////////////////////////////////////////////////////////////////////////////
55 void Formats::Add(string const & name)
57 if (formats.find(name) == formats.end())
58 formats[name] = Format(name, name, name, string());
62 void Formats::Add(string const & name, string const & extension,
63 string const & prettyname, string const & shortcut)
65 formats[name] = Format(name, extension, prettyname, shortcut);
69 void Formats::SetViewer(string const & name, string const & command)
72 string command2 = command;
73 if (!contains(command2,"$$FName"))
74 command2 += " $$FName";
77 GetFormat(name)->viewer = command2;
81 bool Formats::View(Buffer const * buffer, string const & filename,
82 string const & format_name)
87 Format const * format = GetFormat(format_name);
88 if (!format || format->viewer.empty()) {
89 WriteAlert(_("Can not view file"),
90 _("No information for viewing ")
91 + Formats::PrettyName(format_name));
95 string command = format->viewer;
97 if (format_name == "dvi" &&
98 !lyxrc.view_dvi_paper_option.empty()) {
99 string options = lyxrc.view_dvi_paper_option;
100 options += " " + Converter::dvi_papersize(buffer);
101 if (buffer->params.orientation
102 == BufferParams::ORIENTATION_LANDSCAPE)
104 command = add_options(command, options);
107 string command2 = subst(command, "$$FName", OnlyFilename(filename));
108 lyxerr << "Executing command: " << command2 << endl;
109 ShowMessage(buffer, _("Executing command:"), command2);
111 command = subst(command, "$$FName", QuoteName(filename));
113 int res = one.startscript(Systemcalls::SystemDontWait, command);
116 WriteAlert(_("Can not view file"),
117 _("Error while executing"),
118 command.substr(0, 50));
125 Format * Formats::GetFormat(string const & name)
127 map<string, Format>::iterator it = formats.find(name);
128 if (it != formats.end())
129 return &(*it).second;
135 string const Formats::PrettyName(string const & name)
137 Format const * format = GetFormat(name);
139 return format->prettyname;
145 string const Formats::Extension(string const & name)
147 Format const * format = GetFormat(name);
149 return format->extension;
154 //////////////////////////////////////////////////////////////////////////////
156 class compare_Command {
158 compare_Command(Command const & c) : com(c) {}
159 bool operator()(Command const & c) {
160 return c.from == com.from && c.to == com.to;
167 void Converter::Add(string const & from, string const & to,
168 string const & command, string const & flags)
172 Command Com(Formats::GetFormat(from), Formats::GetFormat(to), command);
173 vector<Command>::iterator it = find_if(commands.begin(),
175 compare_Command(Com));
177 if (command.empty() || command == "none") {
178 if (it != commands.end())
185 string flag_list(flags);
186 while (!flag_list.empty()) {
187 string flag_name, flag_value;
188 flag_list = split(flag_list, flag_value, ',');
189 flag_value = split(flag_value, flag_name, '=');
190 if (flag_name == "*") {
191 if (it != commands.end()) {
193 Com.command = command;
196 else if (flag_name == "latex")
198 else if (flag_name == "originaldir")
199 Com.original_dir = true;
200 else if (flag_name == "needaux")
202 else if (flag_name == "resultdir")
203 Com.result_dir = (flag_value.empty())
204 ? "$$BaseName" : flag_value;
205 else if (flag_name == "resultfile")
206 Com.result_file = flag_value;
207 else if (flag_name == "parselog")
208 Com.parselog = flag_value;
209 else if (flag_name == "disable") {
210 while (!flag_value.empty()) {
212 flag_value = split(flag_value, tmp, '&');
213 Com.disable.push_back(tmp);
217 if (!Com.result_dir.empty() && Com.result_file.empty())
218 Com.result_file = "index." + to;
219 //if (!contains(command, "$$FName"))
222 if (Com.latex && (latex_command.empty() || to == "dvi"))
223 latex_command = command;
224 // If we have both latex & pdflatex, we set latex_command to latex.
225 // The latex_command is used to update the .aux file when running
226 // a converter that uses it.
228 if (it != commands.end()) {
232 commands.push_back(Com);
237 bool enable(vector<Command>::iterator it, string const & from)
239 return find((*it).disable.begin(), (*it).disable.end(), from)
240 == (*it).disable.end();
244 vector<FormatPair> const
245 Converter::GetReachable(string const & from, string const & stop_format,
248 vector<FormatPair> result;
249 Format const * format = Formats::GetFormat(from);
253 if (!only_viewable || !format->viewer.empty())
254 result.push_back(FormatPair(format, 0, ""));
256 queue< vector<Command>::iterator > Q;
257 for (vector<Command>::iterator it = commands.begin();
258 it != commands.end(); ++it)
259 if ((*it).from->name == from && enable(it, from)
260 && (*it).to->name != stop_format) {
262 (*it).visited = true;
264 (*it).visited = false;
267 vector<Command>::iterator it = Q.front();
269 if (!only_viewable || !(*it).to->viewer.empty())
270 result.push_back(FormatPair((*it).to, (*it).from,
272 for (vector<Command>::iterator it2 = commands.begin();
273 it2 != commands.end(); ++it2)
274 if (!(*it2).visited && (*it).to == (*it2).from &&
276 (*it2).to->name != stop_format) {
278 (*it2).visited = true;
286 bool Converter::IsReachable(string const & from, string const & to)
291 queue< vector<Command>::iterator > Q;
292 for (vector<Command>::iterator it = commands.begin();
293 it != commands.end(); ++it)
294 if ((*it).from->name == from && enable(it, from)) {
296 (*it).visited = true;
298 (*it).visited = false;
301 vector<Command>::iterator it = Q.front();
303 if ((*it).to->name == to)
305 for (vector<Command>::iterator it2 = commands.begin();
306 it2 != commands.end(); ++it2)
307 if (!(*it2).visited && (*it).to == (*it2).from &&
310 (*it2).visited = true;
317 bool Converter::Convert(Buffer const * buffer,
318 string const & from_file, string const & to_file_base,
319 string const & from_format, string const & to_format,
320 string const & using_format, string & to_file)
322 to_file = ChangeExtension(to_file_base,
323 Formats::Extension(to_format));
325 if (from_format == to_format)
326 if (from_file != to_file)
327 return lyx::rename(from_file, to_file);
331 queue< vector<Command>::iterator > Q;
332 for (vector<Command>::iterator it = commands.begin();
333 it != commands.end(); ++it)
334 if ((*it).from->name == from_format && enable(it, from_format)) {
336 (*it).visited = true;
337 (*it).previous = commands.end();
339 (*it).visited = false;
342 WriteAlert(_("Can not convert file"),
343 ("Unknown format ") + from_format);
348 vector<Command>::iterator it;
351 if ((*it).to->name == to_format &&
352 (using_format.empty() || using_format == (*it).from->name)) {
357 for (vector<Command>::iterator it2 = commands.begin();
358 it2 != commands.end(); ++it2)
359 if (!(*it2).visited && (*it).to == (*it2).from &&
360 enable(it2, from_format)) {
362 (*it2).visited = true;
363 (*it2).previous = it;
368 WriteAlert(_("Can not convert file"),
369 _("No information for converting from ")
370 + Formats::PrettyName(from_format) + _(" to ")
371 + Formats::PrettyName(to_format));
375 vector< vector<Command>::iterator > S;
376 while (it != commands.end()) {
381 string path = OnlyPath(from_file);
384 bool run_latex = false;
385 string from_base = ChangeExtension(from_file, "");
386 string to_base = ChangeExtension(to_file, "");
388 string outfile = from_file;
389 for (vector< vector<Command>::iterator >::reverse_iterator rit =
390 S.rbegin(); rit != S.rend(); ++rit) {
392 lyxerr << "Converting from "
393 << (*it).from->name << " to " << (*it).to->name << endl;
395 outfile = (*it).result_dir.empty()
396 ? ChangeExtension(from_file, (*it).to->extension)
397 : AddName(subst((*it).result_dir,
398 "$$BaseName", from_base),
399 subst((*it).result_file,
400 "$$BaseName", OnlyFilename(from_base)));
403 lyxrc.pdf_mode = (*it).to->name == "pdf";
404 lyxerr << "Running " << (*it).command << endl;
406 if (!runLaTeX(buffer, (*it).command))
409 if ((*it).need_aux && !run_latex
410 && !latex_command.empty()) {
411 lyxerr << "Running " << latex_command
412 << " to update aux file"<< endl;
413 runLaTeX(buffer, latex_command);
416 string infile2 = ((*it).original_dir)
417 ? infile : MakeRelPath(infile, path);
418 string outfile2 = ((*it).original_dir)
419 ? outfile : MakeRelPath(outfile, path);
421 string command = (*it).command;
422 command = subst(command, "$$FName", QuoteName(infile2));
423 command = subst(command, "$$BaseName", QuoteName(from_base));
424 command = subst(command, "$$OutName", QuoteName(outfile2));
426 if (!(*it).parselog.empty())
427 command += " 2> " + QuoteName(infile2 + ".out");
429 if ((*it).from->name == "dvi" && (*it).to->name == "ps")
430 command = add_options(command,
431 dvips_options(buffer));
433 lyxerr << "Calling " << command << endl;
435 ShowMessage(buffer, _("Executing command:"), command);
439 if ((*it).original_dir && buffer) {
440 Path p(buffer->filepath);
441 res = one.startscript(Systemcalls::System, command);
443 res = one.startscript(Systemcalls::System, command);
445 if (!(*it).parselog.empty()) {
446 string const logfile = infile2 + ".log";
447 string const command2 = (*it).parselog +
448 " < " + QuoteName(infile2 + ".out") +
449 " > " + QuoteName(logfile);
450 one.startscript(Systemcalls::System, command2);
451 if (!scanLog(buffer, command, logfile))
456 if ((*it).to->name == "program")
457 WriteAlert(_("There were errors during the Build process."),
458 _("You should try to fix them."));
460 WriteAlert(_("Can not convert file"),
461 "Error while executing",
462 command.substr(0, 50));
468 if (!(*it).result_dir.empty()) {
469 to_file = AddName(subst((*it).result_dir,
470 "$$BaseName", to_base),
471 subst((*it).result_file,
472 "$$BaseName", OnlyFilename(to_base)));
473 if (from_base != to_base) {
474 string from = subst((*it).result_dir,
475 "$$BaseName", from_base);
476 string to = subst((*it).result_dir,
477 "$$BaseName", to_base);
478 return lyx::rename(from, to);
480 } else if (outfile != to_file)
482 return lyx::copy(outfile, to_file);
484 return lyx::rename(outfile, to_file);
490 bool Converter::Convert(Buffer const * buffer,
491 string const & from_file, string const & to_file_base,
492 string const & from_format, string const & to_format,
493 string const & using_format)
496 return Convert(buffer, from_file, to_file_base, from_format, to_format,
497 using_format, to_file);
501 string const Converter::SplitFormat(string const & str, string & format)
503 string using_format = split(str, format, ':');
510 bool Converter::scanLog(Buffer const * buffer, string const & command,
511 string const & filename)
516 BufferView * bv = buffer->getUser();
517 bool need_redraw = false;
520 // Remove all error insets
521 need_redraw = bv->removeAutoInsets();
524 LaTeX latex("", filename, "");
526 int result = latex.scanLogFile(terr);
528 if ((result & LaTeX::ERRORS)) {
529 // Insert all errors as errors boxes
530 bv->insertErrors(terr);
535 bv->fitCursor(bv->text);
540 if ((result & LaTeX::ERRORS)) {
541 int num_errors = latex.getNumErrors();
544 if (num_errors == 1) {
545 s = _("One error detected");
546 t = _("You should try to fix it.");
548 s = tostr(num_errors);
549 s += _(" errors detected.");
550 t = _("You should try to fix them.");
553 split(command, head, ' ');
554 WriteAlert(_("There were errors during running of ") + head,
557 } else if (result & LaTeX::NO_OUTPUT) {
558 string const s = _("The operation resulted in");
559 string const t = _("an empty file.");
560 WriteAlert(_("Resulting file is empty"), s, t);
567 bool Converter::runLaTeX(Buffer const * buffer, string const & command)
572 BufferView * bv = buffer->getUser();
573 string name = buffer->getLatexName();
574 bool need_redraw = false;
578 bv->owner()->getMiniBuffer()->Set(_("Running LaTeX..."));
579 // Remove all error insets
580 need_redraw = bv->removeAutoInsets();
584 // do the LaTex run(s)
586 LaTeX latex(command, name, buffer->filepath);
587 int result = latex.run(terr,
588 bv ? bv->owner()->getMiniBuffer() : 0);
592 if ((result & LaTeX::ERRORS)) {
593 // Insert all errors as errors boxes
594 bv->insertErrors(terr);
598 // if we removed error insets before we ran LaTeX or if we inserted
599 // error insets after we ran LaTeX this must be run:
602 bv->fitCursor(bv->text);
606 // check return value from latex.run().
607 if ((result & LaTeX::NO_LOGFILE)) {
608 WriteAlert(_("LaTeX did not work!"),
609 _("Missing log file:"), name);
610 } else if ((result & LaTeX::ERRORS)) {
611 int num_errors = latex.getNumErrors();
614 if (num_errors == 1) {
615 s = _("One error detected");
616 t = _("You should try to fix it.");
618 s = tostr(num_errors);
619 s += _(" errors detected.");
620 t = _("You should try to fix them.");
622 WriteAlert(_("There were errors during the LaTeX run."),
624 } else if (result & LaTeX::NO_OUTPUT) {
625 string const s = _("The operation resulted in");
626 string const t = _("an empty file.");
627 WriteAlert(_("Resulting file is empty"), s, t);
633 int const ERROR_MASK =
638 return (result & ERROR_MASK) == 0;
643 string const Converter::dvi_papersize(Buffer const * buffer)
645 char real_papersize = buffer->params.papersize;
646 if (real_papersize == BufferParams::PAPER_DEFAULT)
647 real_papersize = lyxrc.default_papersize;
649 switch (real_papersize) {
650 case BufferParams::PAPER_A3PAPER:
652 case BufferParams::PAPER_A4PAPER:
654 case BufferParams::PAPER_A5PAPER:
656 case BufferParams::PAPER_B5PAPER:
658 case BufferParams::PAPER_EXECUTIVEPAPER:
660 case BufferParams::PAPER_LEGALPAPER:
662 case BufferParams::PAPER_USLETTER:
669 string const Converter::dvips_options(Buffer const * buffer)
675 if (buffer->params.use_geometry
676 && buffer->params.papersize2 == BufferParams::VM_PAPER_CUSTOM
677 && !lyxrc.print_paper_dimension_flag.empty()
678 && !buffer->params.paperwidth.empty()
679 && !buffer->params.paperheight.empty()) {
680 // using a custom papersize
681 result = lyxrc.print_paper_dimension_flag;
682 result += ' ' + buffer->params.paperwidth;
683 result += ',' + buffer->params.paperheight;
685 string paper_option = dvi_papersize(buffer);
686 if (paper_option == "us")
687 paper_option = "letter";
688 if (paper_option != "letter" ||
689 buffer->params.orientation != BufferParams::ORIENTATION_LANDSCAPE) {
690 // dvips won't accept -t letter -t landscape. In all other
691 // cases, include the paper size explicitly.
692 result = lyxrc.print_paper_flag;
693 result += ' ' + paper_option;
696 if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
697 result += ' ' + lyxrc.print_landscape_flag;
702 void Converter::init()