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