]> git.lyx.org Git - lyx.git/blob - src/converter.C
Dekel's import/export patch
[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-2000 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include <queue>
18
19 #include "converter.h"
20 #include "lyxrc.h"
21 #include "support/syscall.h"
22 #include "support/path.h"
23 #include "buffer.h"
24 #include "bufferview_funcs.h"
25 #include "LaTeX.h"
26 #include "LyXView.h"
27 #include "minibuffer.h"
28 #include "lyx_gui_misc.h"
29 #include "lyx_cb.h" // ShowMessage()
30
31 using std::map;
32 using std::vector;
33 using std::queue;
34 using std::pair;
35 using std::endl;
36 using std::find;
37 using std::find_if;
38
39 //////////////////////////////////////////////////////////////////////////////
40
41 map<string, Format> Formats::formats;
42 vector<Command> Converter::commands;
43 string Converter::latex_command;
44
45 inline
46 string const add_options(string const & command, string const & options)
47 {
48         string head;
49         string tail = split(command, head, ' ');
50         return head + ' ' + options + ' ' + tail;
51 }
52
53 //////////////////////////////////////////////////////////////////////////////
54
55 void Formats::Add(string const & name)
56 {
57         if (formats.find(name) == formats.end())
58                 formats[name] = Format(name, name, name, string());
59 }
60
61
62 void Formats::Add(string const & name, string const & extension, 
63                   string const & prettyname, string const & shortcut)
64 {
65         formats[name] = Format(name, extension, prettyname, shortcut);
66 }
67
68
69 void Formats::SetViewer(string const & name, string const & command)
70 {
71
72         string command2 = command;
73         if (!contains(command2,"$$FName"))
74                 command2 += " $$FName";
75
76         Add(name);
77         GetFormat(name)->viewer = command2;
78 }
79
80
81 bool Formats::View(Buffer const * buffer, string const & filename,
82                    string const & format_name)
83 {
84         if (filename.empty())
85                 return false;
86
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));
92                            return false;
93         }
94
95         string command = format->viewer;
96
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)
103                         options += 'r';
104                 command = add_options(command, options);
105         }
106
107         string command2 = subst(command, "$$FName", OnlyFilename(filename));
108         lyxerr << "Executing command: " << command2 << endl;
109         ShowMessage(buffer, _("Executing command:"), command2);
110
111         command = subst(command, "$$FName", QuoteName(filename));
112         Systemcalls one;
113         int res = one.startscript(Systemcalls::SystemDontWait, command);
114
115         if (res) {
116                 WriteAlert(_("Can not view file"),
117                            _("Error while executing"),
118                            command.substr(0, 50));
119                 return false;
120         }
121         return true;
122 }
123
124
125 Format * Formats::GetFormat(string const & name)
126 {
127         map<string, Format>::iterator it = formats.find(name);
128         if (it != formats.end())
129                 return &(*it).second;
130         else
131                 return 0;
132 }
133
134
135 string const Formats::PrettyName(string const & name)
136 {
137         Format const * format = GetFormat(name);
138         if (format)
139                 return format->prettyname;
140         else
141                 return name;
142 }
143
144
145 string const Formats::Extension(string const & name)
146 {
147         Format const * format = GetFormat(name);
148         if (format)
149                 return format->extension;
150         else
151                 return name;
152 }
153
154 //////////////////////////////////////////////////////////////////////////////
155
156 class compare_Command {
157 public:
158         compare_Command(Command const & c) : com(c) {}
159         bool operator()(Command const & c) {
160                 return c.from == com.from && c.to == com.to;
161         }
162 private:
163         Command com;
164 };
165
166
167 void Converter::Add(string const & from, string const & to,
168                     string const & command, string const & flags)
169 {
170         Formats::Add(from);
171         Formats::Add(to);
172         Command Com(Formats::GetFormat(from), Formats::GetFormat(to), command);
173         vector<Command>::iterator it = find_if(commands.begin(),
174                                                commands.end(),
175                                                compare_Command(Com));
176
177         if (command.empty() || command == "none") {
178                 if (it != commands.end())
179                         commands.erase(it);
180                 return;
181         }
182
183
184         // Read the flags
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()) {
192                                 Com = *it;
193                                 Com.command = command;
194                         }
195                 } 
196                 else if (flag_name == "latex")
197                         Com.latex = true;
198                 else if (flag_name == "originaldir")
199                         Com.original_dir = true;
200                 else if (flag_name == "needaux")
201                         Com.need_aux = true;
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()) {
211                                 string tmp;
212                                 flag_value = split(flag_value, tmp, '&');
213                                 Com.disable.push_back(tmp);
214                         }
215                 }
216         }
217         if (!Com.result_dir.empty() && Com.result_file.empty())
218                 Com.result_file = "index." + to;
219         //if (!contains(command, "$$FName"))
220         //      Com.latex = true;
221
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.
227
228         if (it != commands.end()) {
229                 *it = Com;
230                 return;
231         }
232         commands.push_back(Com);
233 }
234
235
236 inline
237 bool enable(vector<Command>::iterator it, string const & from)
238 {
239         return find((*it).disable.begin(), (*it).disable.end(), from)
240                 == (*it).disable.end();
241 }
242
243
244 vector<FormatPair> const
245 Converter::GetReachable(string const & from, string const & stop_format,
246                         bool only_viewable)
247 {
248         vector<FormatPair> result;
249         Format const * format = Formats::GetFormat(from);
250         if (!format)
251                 return result;
252
253         if (!only_viewable || !format->viewer.empty())
254                 result.push_back(FormatPair(format, 0, ""));
255
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) {
261                         Q.push(it);
262                         (*it).visited = true;
263                 } else
264                         (*it).visited = false;
265
266         while (!Q.empty()) {
267                 vector<Command>::iterator it = Q.front();
268                 Q.pop();
269                 if (!only_viewable || !(*it).to->viewer.empty())
270                         result.push_back(FormatPair((*it).to, (*it).from,
271                                                     (*it).command));
272                 for (vector<Command>::iterator it2 = commands.begin();
273                      it2 != commands.end(); ++it2)
274                         if (!(*it2).visited && (*it).to == (*it2).from &&
275                             enable(it2, from) &&
276                             (*it2).to->name != stop_format) {
277                                 Q.push(it2);
278                                 (*it2).visited = true;
279                         }
280         }
281
282         return result;
283 }
284
285
286 bool Converter::IsReachable(string const & from, string const & to)
287 {
288         if (from == to)
289                 return true;
290
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)) {
295                         Q.push(it);
296                         (*it).visited = true;
297                 } else
298                         (*it).visited = false;
299
300         while (!Q.empty()) {
301                 vector<Command>::iterator it = Q.front();
302                 Q.pop();
303                 if ((*it).to->name == to)
304                         return true;
305                 for (vector<Command>::iterator it2 = commands.begin();
306                      it2 != commands.end(); ++it2)
307                         if (!(*it2).visited && (*it).to == (*it2).from &&
308                             enable(it2, from)) {
309                                 Q.push(it2);
310                                 (*it2).visited = true;
311                         }
312         }
313         return false;
314 }
315
316
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)
321 {
322         to_file = ChangeExtension(to_file_base,
323                                   Formats::Extension(to_format));
324
325         if (from_format == to_format)
326                 if (from_file != to_file)
327                         return lyx::rename(from_file, to_file);
328                 else
329                         return true;
330
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)) {
335                         Q.push(it);
336                         (*it).visited = true;
337                         (*it).previous = commands.end();
338                 } else
339                         (*it).visited = false;
340
341         if (Q.empty()) {
342                 WriteAlert(_("Can not convert file"),
343                            ("Unknown format ") + from_format);
344                 return false;
345         }
346
347         bool found = false;
348         vector<Command>::iterator it;
349         while (!Q.empty()) {
350                 it = Q.front();
351                 if ((*it).to->name == to_format &&
352                     (using_format.empty() || using_format == (*it).from->name)) {
353                         found = true;
354                         break;
355                 }
356                 Q.pop();
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)) {
361                                 Q.push(it2);
362                                 (*it2).visited = true;
363                                 (*it2).previous = it;
364                         }
365         }
366
367         if (!found) {
368                 WriteAlert(_("Can not convert file"),
369                            _("No information for converting from ")
370                            + Formats::PrettyName(from_format) + _(" to ")
371                            + Formats::PrettyName(to_format));
372                 return false;
373         }
374
375         vector< vector<Command>::iterator > S;
376         while (it != commands.end()) {
377                 S.push_back(it);
378                 it = (*it).previous;
379         }
380
381         string path = OnlyPath(from_file);
382         Path p(path);
383
384         bool run_latex = false;
385         string from_base = ChangeExtension(from_file, "");
386         string to_base = ChangeExtension(to_file, "");
387         string infile;
388         string outfile = from_file;
389         for (vector< vector<Command>::iterator >::reverse_iterator rit =
390                      S.rbegin(); rit != S.rend(); ++rit) {
391                 it = *rit;
392                 lyxerr << "Converting from  "
393                        << (*it).from->name << " to " << (*it).to->name << endl;
394                 infile = outfile;
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)));
401
402                 if ((*it).latex) {
403                         lyxrc.pdf_mode = (*it).to->name == "pdf";
404                         lyxerr << "Running " << (*it).command << endl;
405                         run_latex = true;
406                         if (!runLaTeX(buffer, (*it).command))
407                                 return false;
408                 } else {
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);
414                         }
415
416                         string infile2 = ((*it).original_dir)
417                                 ? infile : MakeRelPath(infile, path);
418                         string outfile2 = ((*it).original_dir)
419                                 ? outfile : MakeRelPath(outfile, path);
420
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));
425
426                         if (!(*it).parselog.empty())
427                                 command += " 2> " + QuoteName(infile2 + ".out");
428
429                         if ((*it).from->name == "dvi" && (*it).to->name == "ps")
430                                 command = add_options(command,
431                                                       dvips_options(buffer));
432
433                         lyxerr << "Calling " << command << endl;
434                         if (buffer)
435                                 ShowMessage(buffer, _("Executing command:"), command);
436                         
437                         Systemcalls one;
438                         int res;
439                         if ((*it).original_dir && buffer) {
440                                 Path p(buffer->filepath);
441                                 res = one.startscript(Systemcalls::System, command);
442                         } else
443                                 res = one.startscript(Systemcalls::System, command);
444
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))
452                                         return false;
453                         }
454
455                         if (res) {
456                                 if ((*it).to->name == "program")
457                                         WriteAlert(_("There were errors during the Build process."),
458                                                    _("You should try to fix them."));
459                                 else
460                                         WriteAlert(_("Can not convert file"),
461                                                    "Error while executing",
462                                                    command.substr(0, 50));
463                                 return false;
464                         }
465                 }
466         }
467
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);
479                 }
480         } else if (outfile != to_file)
481                 if ((*it).latex)
482                         return lyx::copy(outfile, to_file);
483                 else
484                         return lyx::rename(outfile, to_file);
485
486         return true;
487 }
488
489
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)
494 {
495         string to_file;
496         return Convert(buffer, from_file, to_file_base, from_format, to_format,
497                 using_format, to_file);
498 }
499
500
501 string const Converter::SplitFormat(string const & str, string & format)
502 {
503         string using_format = split(str, format, ':');
504         if (format.empty())
505                 format = "dvi";
506         return using_format;
507 }
508
509
510 bool Converter::scanLog(Buffer const * buffer, string const & command,
511                         string const & filename)
512 {
513         if (!buffer)
514                 return false;
515
516         BufferView * bv = buffer->getUser();
517         bool need_redraw = false;
518         if (bv) {
519                 ProhibitInput(bv);
520                 // Remove all error insets
521                 need_redraw = bv->removeAutoInsets();
522         }
523
524         LaTeX latex("", filename, "");
525         TeXErrors terr;
526         int result = latex.scanLogFile(terr);
527         if (bv) {
528                 if ((result & LaTeX::ERRORS)) {
529                         // Insert all errors as errors boxes
530                         bv->insertErrors(terr);
531                         need_redraw = true;
532                 }
533                 if (need_redraw) {
534                         bv->redraw();
535                         bv->fitCursor(bv->text);
536                 }
537                 AllowInput(bv);
538         }
539
540         if ((result & LaTeX::ERRORS)) {
541                 int num_errors = latex.getNumErrors();
542                 string s;
543                 string t;
544                 if (num_errors == 1) {
545                         s = _("One error detected");
546                         t = _("You should try to fix it.");
547                 } else {
548                         s = tostr(num_errors);
549                         s += _(" errors detected.");
550                         t = _("You should try to fix them.");
551                 }
552                 string head;
553                 split(command, head, ' ');
554                 WriteAlert(_("There were errors during running of ") + head,
555                            s, t);
556                 return false;
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);
561                 return false;
562         }
563         return true;
564 }
565
566
567 bool Converter::runLaTeX(Buffer const * buffer, string const & command)
568 {
569         if (!buffer)
570                 return false;
571
572         BufferView * bv = buffer->getUser();
573         string name = buffer->getLatexName();
574         bool need_redraw = false;
575
576         if (bv) {
577                 ProhibitInput(bv);
578                 bv->owner()->getMiniBuffer()->Set(_("Running LaTeX..."));
579                 // Remove all error insets
580                 need_redraw = bv->removeAutoInsets();
581         }
582
583
584         // do the LaTex run(s)
585         TeXErrors terr;
586         LaTeX latex(command, name, buffer->filepath);
587         int result = latex.run(terr,
588                                bv ? bv->owner()->getMiniBuffer() : 0);
589         
590
591         if (bv) {
592                 if ((result & LaTeX::ERRORS)) {
593                         // Insert all errors as errors boxes
594                         bv->insertErrors(terr);
595                         need_redraw = true;
596                 }
597
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:
600                 if (need_redraw) {
601                         bv->redraw();
602                         bv->fitCursor(bv->text);
603                 }
604         }
605
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();
612                 string s;
613                 string t;
614                 if (num_errors == 1) {
615                         s = _("One error detected");
616                         t = _("You should try to fix it.");
617                 } else {
618                         s = tostr(num_errors);
619                         s += _(" errors detected.");
620                         t = _("You should try to fix them.");
621                 }
622                 WriteAlert(_("There were errors during the LaTeX run."),
623                            s, t);
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);
628         }
629
630         if (bv)
631                 AllowInput(bv);
632  
633         int const ERROR_MASK = 
634                         LaTeX::NO_LOGFILE |
635                         LaTeX::ERRORS |
636                         LaTeX::NO_OUTPUT;
637         
638         return (result & ERROR_MASK) == 0;
639
640 }
641
642
643 string const Converter::dvi_papersize(Buffer const * buffer)
644 {
645         char real_papersize = buffer->params.papersize;
646         if (real_papersize == BufferParams::PAPER_DEFAULT)
647                 real_papersize = lyxrc.default_papersize;
648
649         switch (real_papersize) {
650         case BufferParams::PAPER_A3PAPER:
651                 return "a3";
652         case BufferParams::PAPER_A4PAPER:
653                 return "a4";
654         case BufferParams::PAPER_A5PAPER:
655                 return "a5";
656         case BufferParams::PAPER_B5PAPER:
657                 return "b5";
658         case BufferParams::PAPER_EXECUTIVEPAPER:
659                 return "foolscap";
660         case BufferParams::PAPER_LEGALPAPER:
661                 return "legal";
662         case BufferParams::PAPER_USLETTER:
663         default:
664                 return "us";
665         }
666 }
667
668
669 string const Converter::dvips_options(Buffer const * buffer)
670 {
671         string result;
672         if (!buffer)
673                 return result;
674
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;
684         } else {
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;
694                 }
695         }
696         if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
697                 result += ' ' + lyxrc.print_landscape_flag;
698         return result;
699 }
700
701
702 void Converter::init()
703 {
704 }