]> git.lyx.org Git - lyx.git/blob - src/converter.C
9773b5fbcb74e37f5271fb73306e8aae8d81d413
[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
37 //////////////////////////////////////////////////////////////////////////////
38
39 map<string, Format> Formats::formats;
40 vector<Command> Converter::commands;
41 string Converter::latex_command;
42
43 inline
44 string const add_options(string const & command, string const & options)
45 {
46         string head;
47         string tail = split(command, head, ' ');
48         return head + ' ' + options + ' ' + tail;
49 }
50
51 //////////////////////////////////////////////////////////////////////////////
52
53 Format::Format(string const & n)
54         : name(n)
55 {
56         struct Item {
57                 char const * name;
58                 char const * prettyname;
59         };
60         Item items[] = {
61                 { "tex", "LaTeX" },
62                 { "dvi", "DVI" },
63                 { "ps", "PostScript" },
64                 { "txt", "Ascii" },
65                 { "html", "HTML" },
66                 { "pdf", "PDF" },
67                 { "nw", "NoWeb/LaTeX"},
68                 { 0, 0}
69         };
70
71         prettyname = n;
72         for (int i = 0; items[i].name != 0; ++i)
73                 if (items[i].name == n) {
74                         prettyname = items[i].prettyname;
75                         break;
76                 }
77 }
78
79
80 void Formats::Add(string const & name)
81 {
82         if (formats.find(name) == formats.end())
83                 formats[name] = Format(name);
84 }
85
86
87 void Formats::SetViewer(string const & name, string const & command)
88 {
89
90         string command2 = command;
91         if (!contains(command2,"$$FName"))
92                 command2 += " $$FName";
93
94         Add(name);
95         GetFormat(name)->viewer = command2;
96 }
97
98
99 bool Formats::View(Buffer const * buffer, string const & filename)
100 {
101         if (filename.empty())
102                 return false;
103
104         string extension = GetExtension(filename);
105         Format * format = GetFormat(extension);
106         if (!format || format->viewer.empty()) {
107                 WriteAlert(_("Can not view file"),
108                            _("No information for viewing ")
109                            + Formats::PrettyName(extension));
110                            return false;
111         }
112
113         string command = format->viewer;
114
115         if (extension == "dvi" &&
116             !lyxrc.view_dvi_paper_option.empty()) {
117                 string options = lyxrc.view_dvi_paper_option;
118                 options += " " + Converter::dvi_papersize(buffer);
119                 if (buffer->params.orientation 
120                     == BufferParams::ORIENTATION_LANDSCAPE)
121                         options += 'r';
122                 command = add_options(command, options);
123         }
124
125         string command2 = subst(command, "$$FName", OnlyFilename(filename));
126         lyxerr << "Executing command: " << command2 << endl;
127         ShowMessage(buffer, _("Executing command:"), command2);
128
129         command = subst(command, "$$FName", QuoteName(filename));
130         Systemcalls one;
131         int res = one.startscript(Systemcalls::SystemDontWait, command);
132
133         if (res) {
134                 WriteAlert(_("Can not view file"),
135                            _("Error while executing"),
136                            command.substr(0, 50));
137                 return false;
138         }
139         return true;
140 }
141
142
143 Format * Formats::GetFormat(string const & name)
144 {
145         map<string, Format>::iterator it = formats.find(name);
146         if (it != formats.end())
147                 return &(*it).second;
148         else
149                 return 0;
150 }
151
152
153 string const Formats::PrettyName(string const & name)
154 {
155         string format;
156         Converter::SplitFormat(name, format);
157         Format * f = GetFormat(format);
158         if (f)
159                 return f->prettyname;
160         else
161                 return format;
162 }
163
164
165 //////////////////////////////////////////////////////////////////////////////
166
167 void Converter::Add(string const & from, string const & to,
168                     string const & command, string const & flags)
169 {
170         if (command == "none")
171                 return;
172
173         Command Com(from, to, command);
174
175         if (from == "tex" &&
176             (to == "dvi" ||
177              (to == "pdf" && latex_command.empty())))
178                 latex_command = command;
179
180         // Read the flags
181         string flag_name,flag_value;
182         string flag_list(flags);
183         while (!flag_list.empty()) {
184                 flag_list = split(flag_list, flag_value,',');
185                 flag_value = split(flag_value, flag_name, '=');
186                 if (flag_name == "originaldir")
187                         Com.original_dir = true;
188                 else if (flag_name == "needaux")
189                         Com.need_aux = true;
190                 else if (flag_name == "resultdir")
191                         Com.result_dir = (flag_value.empty())
192                                 ? "$$BaseName" : flag_value;
193                 else if (flag_name == "resultfile")
194                         Com.result_file = flag_value;
195                 else if (flag_name == "parselog")
196                         Com.parselog = flag_value;
197         }
198         if (!Com.result_dir.empty() && Com.result_file.empty())
199                 Com.result_file = "index." + to;
200
201         for (vector<Command>::iterator it = commands.begin();
202              it != commands.end(); ++it)
203                 if ((*it).from == from && (*it).to == to) {
204                         *it = Com;
205                         return;
206                 }
207         commands.push_back(Com);
208         Formats::Add(from);
209         Formats::Add(to);
210 }
211
212
213 vector<FormatPair> const
214 Converter::GetReachable(string const & from, bool only_viewable)
215 {
216         vector<FormatPair> result;
217         Format * format = Formats::GetFormat(from);
218         if (!format)
219                 return result;
220
221         if (!only_viewable || !format->viewer.empty())
222                 result.push_back(FormatPair(format, 0, ""));
223
224         queue< vector<Command>::iterator > Q;
225         for (vector<Command>::iterator it = commands.begin();
226              it != commands.end(); ++it)
227                 if ((*it).from == from) {
228                         Q.push(it);
229                         (*it).visited = true;
230                 } else
231                         (*it).visited = false;
232
233         while (!Q.empty()) {
234                 vector<Command>::iterator it = Q.front();
235                 Q.pop();
236                 format = Formats::GetFormat((*it).to);
237                 if (!only_viewable || !format->viewer.empty())
238                         result.push_back(FormatPair(format,
239                                                     Formats::GetFormat((*it).from),
240                                                     (*it).command));
241                 for (vector<Command>::iterator it2 = commands.begin();
242                      it2 != commands.end(); ++it2)
243                         if (!(*it2).visited && (*it).to == (*it2).from) {
244                                 Q.push(it2);
245                                 (*it2).visited = true;
246                         }
247         }
248
249         return result;
250 }
251
252 bool Converter::IsReachable(string const & from, string const & target_format)
253 {
254         Format * format = Formats::GetFormat(from);
255         if (!format)
256                 return false;
257         else if (format->name == target_format)
258                 return true;
259
260         queue< vector<Command>::iterator > Q;
261         for (vector<Command>::iterator it = commands.begin();
262              it != commands.end(); ++it)
263                 if ((*it).from == from) {
264                         Q.push(it);
265                         (*it).visited = true;
266                 } else
267                         (*it).visited = false;
268
269         while (!Q.empty()) {
270                 vector<Command>::iterator it = Q.front();
271                 Q.pop();
272                 format = Formats::GetFormat((*it).to);
273                 if (format->name == target_format)
274                         return true;
275                 for (vector<Command>::iterator it2 = commands.begin();
276                      it2 != commands.end(); ++it2)
277                         if (!(*it2).visited && (*it).to == (*it2).from) {
278                                 Q.push(it2);
279                                 (*it2).visited = true;
280                         }
281         }
282         return false;
283 }
284
285
286 bool Converter::Convert(Buffer const * buffer, string const & from_file,
287                         string const & to_file, string const & using_format,
288                         string * view_file)
289 {
290         if (view_file)
291                 *view_file = to_file;
292
293         string from_format = GetExtension(from_file);
294         string to_format = GetExtension(to_file);
295         if (from_format == to_format)
296                 if (from_file != to_file)
297                         return lyx::rename(from_file, to_file);
298                 else
299                         return true;
300
301         queue< vector<Command>::iterator > Q;
302         for (vector<Command>::iterator it = commands.begin();
303              it != commands.end(); ++it)
304                 if ((*it).from == from_format) {
305                         Q.push(it);
306                         (*it).visited = true;
307                         (*it).previous = commands.end();
308                 } else
309                         (*it).visited = false;
310
311         if (Q.empty()) {
312                 WriteAlert(_("Can not convert file"),
313                            ("Unknown format ") + from_format);
314                 return false;
315         }
316
317         bool found = false;
318         vector<Command>::iterator it;
319         while (!Q.empty()) {
320                 it = Q.front();
321                 if ((*it).to == to_format &&
322                     (using_format.empty() || using_format == (*it).from)) {
323                         found = true;
324                         break;
325                 }
326                 Q.pop();
327                 for (vector<Command>::iterator it2 = commands.begin();
328                      it2 != commands.end(); ++it2)
329                         if (!(*it2).visited && (*it).to == (*it2).from) {
330                                 Q.push(it2);
331                                 (*it2).visited = true;
332                                 (*it2).previous = it;
333                         }
334         }
335
336         if (!found) {
337                 WriteAlert(_("Can not convert file"),
338                            _("No information for converting from ")
339                            + Formats::PrettyName(from_format) + _(" to ")
340                            + Formats::PrettyName(to_format));
341                 return false;
342         }
343
344         vector< vector<Command>::iterator > S;
345         while (it != commands.end()) {
346                 S.push_back(it);
347                 it = (*it).previous;
348         }
349
350         string path = OnlyPath(from_file);
351         Path p(path);
352
353         bool run_latex = false;
354         string from_base = ChangeExtension(from_file, "");
355         string to_base = ChangeExtension(to_file, "");
356         string infile;
357         string outfile = from_file;
358         for (vector< vector<Command>::iterator >::reverse_iterator rit =
359                      S.rbegin(); rit != S.rend(); ++rit) {
360                 it = *rit;
361                 lyxerr << "Converting from  "
362                        << (*it).from << " to " << (*it).to << endl;
363                 infile = outfile;
364                 outfile = (*it).result_dir.empty()
365                         ? ChangeExtension(from_file, (*it).to)
366                         : AddName(subst((*it).result_dir,
367                                         "$$BaseName", from_base),
368                                   subst((*it).result_file,
369                                         "$$BaseName", OnlyFilename(from_base)));
370
371                 if ((*it).from == "tex" &&
372                     ( (*it).to == "dvi" || (*it).to == "pdf") ) {
373                         lyxrc.pdf_mode = (*it).to == "pdf";
374                         lyxerr << "Running " << (*it).command << endl;
375                         run_latex = true;
376                         if (!runLaTeX(buffer, (*it).command))
377                                 return false;
378                 } else {
379                         if ((*it).need_aux && !run_latex
380                             && !latex_command.empty()) {
381                                 lyxerr << "Running " << latex_command 
382                                        << " to update aux file"<<  endl;
383                                 runLaTeX(buffer, latex_command);
384                         }
385
386                         string infile2 = ((*it).original_dir)
387                                 ? infile : MakeRelPath(infile, path);
388                         string outfile2 = ((*it).original_dir)
389                                 ? outfile : MakeRelPath(outfile, path);
390
391                         string command = (*it).command;
392                         command = subst(command, "$$FName", QuoteName(infile2));
393                         command = subst(command, "$$BaseName", QuoteName(from_base));
394                         command = subst(command, "$$OutName", QuoteName(outfile2));
395
396                         if (!(*it).parselog.empty())
397                                 command += " 2> " + QuoteName(infile2 + ".out");
398
399                         if ((*it).from == "dvi" && (*it).to == "ps")
400                                 command = add_options(command,
401                                                       dvips_options(buffer));
402
403                         lyxerr << "Calling " << command << endl;
404                         ShowMessage(buffer, _("Executing command:"), command);
405                         
406                         Systemcalls one;
407                         int res;
408                         if ((*it).original_dir) {
409                                 Path p(buffer->filepath);
410                                 res = one.startscript(Systemcalls::System, command);
411                         } else
412                                 res = one.startscript(Systemcalls::System, command);
413
414                         if (!(*it).parselog.empty()) {
415                                 string const logfile =  infile2 + ".log";
416                                 string const command2 = (*it).parselog +
417                                         " < " + QuoteName(infile2 + ".out") +
418                                         " > " + QuoteName(logfile);
419                                 one.startscript(Systemcalls::System, command2);
420                                 if (!scanLog(buffer, command, logfile))
421                                         return false;
422                         }
423
424                         if (res) {
425                                 if ((*it).to == "Program")
426                                         WriteAlert(_("There were errors during the Build process."),
427                                                    _("You should try to fix them."));
428                                 else
429                                         WriteAlert(_("Can not convert file"),
430                                                    "Error while executing",
431                                                    command.substr(0, 50));
432                                 return false;
433                         }
434                 }
435         }
436
437         if (!(*it).result_dir.empty()) {
438                 if (view_file)
439                         *view_file = AddName(subst((*it).result_dir,
440                                                    "$$BaseName", to_base),
441                                              subst((*it).result_file,
442                                                    "$$BaseName", OnlyFilename(to_base)));
443                 if (from_base != to_base) {
444                         string from = subst((*it).result_dir,
445                                             "$$BaseName", from_base);
446                         string to = subst((*it).result_dir,
447                                           "$$BaseName", to_base);
448                         return lyx::rename(from, to);
449                 }
450
451         } else if (outfile != to_file)
452                 if ((*it).from == "tex" &&
453                     ( (*it).to == "dvi" || (*it).to == "pdf") )
454                         return lyx::copy(outfile, to_file);
455                 else
456                         return lyx::rename(outfile, to_file);
457
458         return true;
459 }
460
461
462 string const Converter::SplitFormat(string const & str, string & format)
463 {
464         string using_format = split(str, format, ':');
465         if (format.empty())
466                 format = "dvi";
467         return using_format;
468 }
469
470 bool Converter::scanLog(Buffer const * buffer, string const & command,
471                         string const & filename)
472 {
473         BufferView * bv = buffer->getUser();
474         bool need_redraw = false;
475         if (bv) {
476                 ProhibitInput(bv);
477                 // Remove all error insets
478                 need_redraw = bv->removeAutoInsets();
479         }
480
481         LaTeX latex("", filename, "");
482         TeXErrors terr;
483         int result = latex.scanLogFile(terr);
484         if (bv) {
485                 if ((result & LaTeX::ERRORS)) {
486                         // Insert all errors as errors boxes
487                         bv->insertErrors(terr);
488                         need_redraw = true;
489                 }
490                 if (need_redraw) {
491                         bv->redraw();
492                         bv->fitCursor(bv->text);
493                 }
494         if (result & LaTeX::NO_OUTPUT) {
495                 string const s = _("The operation resulted in");
496                 string const t = _("an empty file.");
497                 WriteAlert(_("Resulting file is empty"), s, t);
498                 return false;
499         }
500                 AllowInput(bv);
501         }
502         if ((result & LaTeX::ERRORS)) {
503                 int num_errors = latex.getNumErrors();
504                 string s;
505                 string t;
506                 if (num_errors == 1) {
507                         s = _("One error detected");
508                         t = _("You should try to fix it.");
509                 } else {
510                         s = tostr(num_errors);
511                         s += _(" errors detected.");
512                         t = _("You should try to fix them.");
513                 }
514                 string head;
515                 split(command, head, ' ');
516                 WriteAlert(_("There were errors during running of ") + head,
517                            s, t);
518                 return false;
519         }
520         return true;
521 }
522
523 bool Converter::runLaTeX(Buffer const * buffer, string const & command)
524 {
525         
526         BufferView * bv = buffer->getUser();
527         string name = buffer->getLatexName();
528         bool need_redraw = false;
529
530         if (bv) {
531                 ProhibitInput(bv);
532                 bv->owner()->getMiniBuffer()->Set(_("Running LaTeX..."));
533                 // Remove all error insets
534                 need_redraw = bv->removeAutoInsets();
535         }
536
537
538         // do the LaTex run(s)
539         TeXErrors terr;
540         LaTeX latex(command, name, buffer->filepath);
541         int result = latex.run(terr,
542                                bv ? bv->owner()->getMiniBuffer() : 0);
543         
544
545         if (bv) {
546                 if ((result & LaTeX::ERRORS)) {
547                         // Insert all errors as errors boxes
548                         bv->insertErrors(terr);
549                         need_redraw = true;
550                 }
551
552                 // if we removed error insets before we ran LaTeX or if we inserted
553                 // error insets after we ran LaTeX this must be run:
554                 if (need_redraw) {
555                         bv->redraw();
556                         bv->fitCursor(bv->text);
557                 }
558         } else if (result & LaTeX::NO_OUTPUT) {
559                 string const s = _("The operation resulted in");
560                 string const t = _("an empty file.");
561                 WriteAlert(_("Resulting file is empty"), s, t);
562         }
563
564         // check return value from latex.run().
565         if ((result & LaTeX::NO_LOGFILE)) {
566                 WriteAlert(_("LaTeX did not work!"),
567                            _("Missing log file:"), name);
568         } else if ((result & LaTeX::ERRORS)) {
569                 int num_errors = latex.getNumErrors();
570                 string s;
571                 string t;
572                 if (num_errors == 1) {
573                         s = _("One error detected");
574                         t = _("You should try to fix it.");
575                 } else {
576                         s = tostr(num_errors);
577                         s += _(" errors detected.");
578                         t = _("You should try to fix them.");
579                 }
580                 WriteAlert(_("There were errors during the LaTeX run."),
581                            s, t);
582         }
583
584         if (bv)
585                 AllowInput(bv);
586  
587         int const ERROR_MASK = 
588                         LaTeX::NO_LOGFILE |
589                         LaTeX::ERRORS |
590                         LaTeX::NO_OUTPUT;
591         
592         return (result & ERROR_MASK) == 0;
593
594 }
595
596
597 string const Converter::dvi_papersize(Buffer const * buffer)
598 {
599         char real_papersize = buffer->params.papersize;
600         if (real_papersize == BufferParams::PAPER_DEFAULT)
601                 real_papersize = lyxrc.default_papersize;
602
603         switch (real_papersize) {
604         case BufferParams::PAPER_A3PAPER:
605                 return "a3";
606         case BufferParams::PAPER_A4PAPER:
607                 return "a4";
608         case BufferParams::PAPER_A5PAPER:
609                 return "a5";
610         case BufferParams::PAPER_B5PAPER:
611                 return "b5";
612         case BufferParams::PAPER_EXECUTIVEPAPER:
613                 return "foolscap";
614         case BufferParams::PAPER_LEGALPAPER:
615                 return "legal";
616         case BufferParams::PAPER_USLETTER:
617         default:
618                 return "us";
619         }
620 }
621
622
623 string const Converter::dvips_options(Buffer const * buffer)
624 {
625         string result;
626         if (buffer->params.use_geometry
627             && buffer->params.papersize2 == BufferParams::VM_PAPER_CUSTOM
628             && !lyxrc.print_paper_dimension_flag.empty()
629             && !buffer->params.paperwidth.empty()
630             && !buffer->params.paperheight.empty()) {
631                 // using a custom papersize
632                 result = lyxrc.print_paper_dimension_flag;
633                 result += ' ' + buffer->params.paperwidth;
634                 result += ',' + buffer->params.paperheight;
635         } else {
636                 string paper_option = dvi_papersize(buffer);
637                 if (paper_option == "us")
638                         paper_option = "letter";
639                 if (paper_option != "letter" ||
640                     buffer->params.orientation != BufferParams::ORIENTATION_LANDSCAPE) {
641                         // dvips won't accept -t letter -t landscape.  In all other
642                         // cases, include the paper size explicitly.
643                         result = lyxrc.print_paper_flag;
644                         result += ' ' + paper_option;
645                 }
646         }
647         if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
648                 result += ' ' + lyxrc.print_landscape_flag;
649         return result;
650 }
651
652 void Converter::init()
653 {
654         Formats::Add("txt");    
655 }