]> git.lyx.org Git - features.git/blob - src/converter.C
patch from dekel and patch from angus
[features.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 <stack>
19 #include <algorithm>
20 #include <stdio.h>
21
22 #include "converter.h"
23 #include "lyxrc.h"
24 #include "support/syscall.h"
25 #include "support/path.h"
26 #include "debug.h"
27 #include "buffer.h"
28
29 #include "bufferview_funcs.h"
30 #include "LaTeX.h"
31 #include "LyXView.h"
32 #include "minibuffer.h"
33 #include "lyx_gui_misc.h"
34 #include "lyx_cb.h"
35
36 using std::map;
37 using std::vector;
38 using std::queue;
39 using std::pair;
40 using std::sort;
41 using std::stack;
42 using std::endl;
43
44 //////////////////////////////////////////////////////////////////////////////
45
46 map<string, Format> Formats::formats;
47 vector<Command> Converter::commands;
48
49 //////////////////////////////////////////////////////////////////////////////
50
51 Format::Format(string const & n)
52         : name(n), in_degree(0)
53 {
54         struct Item {
55                 char const * name;
56                 char const * prettyname;
57         };
58         Item items[] = {
59                 { "tex", "LaTeX" },
60                 { "dvi", "DVI" },
61                 { "ps", "PostScript" },
62                 { "txt", "Ascii" },
63                 { "html", "HTML" },
64                 { "pdf", "PDF" },
65                 { 0, 0}
66         };
67
68         prettyname = n;
69         for (int i = 0; items[i].name != 0; ++i)
70                 if (items[i].name == n) {
71                         prettyname = items[i].prettyname;
72                         break;
73                 }
74 }
75
76
77 void Formats::Add(string const & name)
78 {
79         if (formats.find(name) == formats.end())
80                 formats[name] = Format(name);
81 }
82
83
84 void Formats::SetViewer(string const & name, string const & command)
85 {
86         Add(name);
87         Format * f = GetFormat(name);
88         if (!f->viewer.empty())
89                 lyxerr << "Error: a viewer for " << name
90                        << " is already defined!" << endl;
91         else
92                 f->viewer = command;
93 }
94
95
96 bool Formats::View(string const & filename)
97 {
98         string extension = GetExtension(filename);
99         Format * format = GetFormat(extension);
100         if (!format || format->viewer.empty()) {
101                 WriteAlert(_("Can not view file"),
102                            _("No information for viewing ")
103                            + Formats::PrettyName(extension));
104                            return false;
105         }
106
107         string command = format->viewer;
108         command = subst(command, "$$FName", filename);
109 #ifndef __EMX__
110         command += " &";
111 #else
112         // OS/2 cmd.exe has another use for '&'
113         // This is not NLS safe, but it's OK, I think.
114         string sh = OnlyFilename(GetEnvPath("EMXSHELL"));
115         if (sh.empty()) {
116                 // COMSPEC is set, unless user unsets 
117                 sh = OnlyFilename(GetEnvPath("COMSPEC"));
118                 if (sh.empty())
119                         sh = "cmd.exe";
120         }
121         sh = lowercase(sh);
122         if (contains(sh, "cmd.exe")
123             || contains(sh, "4os2.exe"))
124                 command = "start /min/n " + command;
125         else
126                 command += " &";
127 #endif
128         lyxerr << "Executing command: " << command << endl;
129         //ShowMessage(buffer, _("Executing command:"), command);
130         Systemcalls one;
131         int res = one.startscript(Systemcalls::System, 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 string const Formats::PrettyName(string const & name)
153 {
154         string format;
155         Converter::SplitFormat(name, format);
156         Format * f = GetFormat(format);
157         if (f)
158                 return f->prettyname;
159         else
160                 return format;
161 }
162
163
164 //////////////////////////////////////////////////////////////////////////////
165 void Converter::Add(string const & from, string const & to,
166                     string const & command, string const & flags)
167 {
168         for (vector<Command>::const_iterator cit = commands.begin();
169              cit != commands.end(); ++cit)
170                 if ((*cit).from == from && (*cit).to == to) {
171                         lyxerr << "Error: Convertor from " << from
172                                << " to " << to
173                                << " already exists!" << endl;
174                         return;
175                 }
176         bool original_dir = flags == "origdir";
177         commands.push_back(Command(from, to, command, original_dir));
178
179         Formats::Add(from);
180         Formats::Add(to);
181         ++Formats::GetFormat(to)->in_degree;
182 }
183
184
185 vector< pair<string,string> > const
186 Converter::GetReachable(string const & from, bool only_viewable)
187 {
188         vector< pair<string,string> > result;
189         Format * format = Formats::GetFormat(from);
190         if (!format)
191                 return result;
192
193         int sort_start = 0;
194         if (!only_viewable || !format->viewer.empty()) {
195                 result.push_back(pair<string,string>(from, format->prettyname));
196                 sort_start = 1;
197         }
198
199         queue< vector<Command>::iterator > Q;
200         for (vector<Command>::iterator it = commands.begin();
201              it != commands.end(); ++it)
202                 if ((*it).from == from) {
203                         Q.push(it);
204                         (*it).visited = true;
205                 } else
206                         (*it).visited = false;
207
208         while (!Q.empty()) {
209                 vector<Command>::iterator it = Q.front();
210                 format = Formats::GetFormat((*it).to);
211                 string name = format->name;
212                 string prettyname = format->prettyname;
213                 if (format->in_degree > 1) {
214                         name += ":" + (*it).from;
215                         string tmp;
216                         split((*it).command, tmp, ' ');
217                         prettyname  += _("(using ") + tmp + ")";        
218                 }
219                 if (!only_viewable || !format->viewer.empty())
220                         result.push_back(pair<string,string>(name, prettyname));
221                 Q.pop();
222                 for (vector<Command>::iterator it2 = commands.begin();
223                      it2 != commands.end(); ++it2)
224                         if (!(*it2).visited && (*it).to == (*it2).from) {
225                                 Q.push(it2);
226                                 (*it2).visited = true;
227                         }
228         }
229
230         sort(result.begin() + sort_start, result.end());
231         return result;
232 }
233
234
235 bool Converter::convert(Buffer * buffer, string const & from_file,
236                         string const & to_format)
237
238 {
239         string format;
240         string using_format = SplitFormat(to_format, format);
241         string from_format = GetExtension(from_file);
242         if (from_format == format)
243                 return true;
244
245         queue< vector<Command>::iterator > Q;
246         for (vector<Command>::iterator it = commands.begin();
247              it != commands.end(); ++it)
248                 if ((*it).from == from_format) {
249                         Q.push(it);
250                         (*it).visited = true;
251                         (*it).previous = commands.end();
252                 } else
253                         (*it).visited = false;
254
255         if (Q.empty()) {
256                 WriteAlert(_("Can not convert file"),
257                            ("Unknown format ") + from_format);
258                 return false;
259         }
260
261         bool found = false;
262         vector<Command>::iterator it;
263         while (!Q.empty()) {
264                 it = Q.front();
265                 if ((*it).to == format &&
266                     (using_format.empty() || using_format == (*it).from)) {
267                         found = true;
268                         break;
269                 }
270                 Q.pop();
271                 for (vector<Command>::iterator it2 = commands.begin();
272                      it2 != commands.end(); ++it2)
273                         if (!(*it2).visited && (*it).to == (*it2).from) {
274                                 Q.push(it2);
275                                 (*it2).visited = true;
276                                 (*it2).previous = it;
277                         }
278         }
279
280         if (!found) {
281                 WriteAlert(_("Can not convert file"),
282                            _("No information for converting from ")
283                            + Formats::PrettyName(from_format) + _(" to ")
284                            + Formats::PrettyName(to_format));
285                 return false;
286         }
287
288         stack< vector<Command>::iterator > S;
289         while (it != commands.end()) {
290                 S.push(it);
291                 it = (*it).previous;
292         }
293
294         //Path p(OnlyPath(buffer->fileName()));
295         Path p(OnlyPath(from_file));
296
297         string basename = ChangeExtension(from_file, "");
298         while (!S.empty()) {
299                 it = S.top();
300                 S.pop();
301                 lyxerr << "Converting from  "
302                        << (*it).from << " to " << (*it).to << endl;
303
304                 if ((*it).from == "tex" &&
305                     ( (*it).to == "dvi" || (*it).to == "pdf") ) {
306                         lyxrc.pdf_mode = (*it).to == "pdf";
307                         if (!runLaTeX(buffer, (*it).command))
308                                 return false;
309                 } else {
310                         string infile = ChangeExtension(from_file, (*it).from);
311                         if (!(*it).original_dir)
312                                 infile = OnlyFilename(infile);
313                         string outfile = ChangeExtension(infile, (*it).to);
314
315                         string command = (*it).command;
316                         command = subst(command, "$$FName", infile);
317                         command = subst(command, "$$BaseName", basename);
318                         command = subst(command, "$$OutName", outfile);
319                         lyxerr << "Calling " << command << endl;
320                         ShowMessage(buffer, _("Executing command:"), command);
321                         
322                         Systemcalls one;
323                         int res;
324                         if ((*it).original_dir) {
325                                 Path p(buffer->filepath);
326                                 res = one.startscript(Systemcalls::System, command);
327                         } else
328                                 res = one.startscript(Systemcalls::System, command);
329                         if (res) {
330                                 WriteAlert(_("Can not convert file"),
331                                            "Error while executing",
332                                            command.substr(0, 50));
333                                 return false;
334                         }
335                 }
336         }
337
338         return true;
339 }
340
341
342 string const Converter::SplitFormat(string const & str, string & format)
343 {
344         string using_format = split(str, format, ':');
345         if (format.empty())
346                 format = "dvi";
347         return using_format;
348 }
349
350
351 bool Converter::runLaTeX(Buffer * buffer, string const & command)
352 {
353         
354         BufferView * bv = buffer->getUser();
355
356         if (!bv->text) return 0;
357
358         ProhibitInput(bv);
359
360         string name = buffer->getLatexName();
361
362         bv->owner()->getMiniBuffer()->Set(_("Running LaTeX..."));   
363
364         // Remove all error insets
365         bool a = bv->removeAutoInsets();
366
367         // do the LaTex run(s)
368         TeXErrors terr;
369         LaTeX latex(command, name, buffer->filepath);
370         int result = latex.run(terr,
371                             bv->owner()->getMiniBuffer()); // running latex
372
373         if ((result & LaTeX::ERRORS)) {
374                 // Insert all errors as errors boxes
375                 bv->insertErrors(terr);
376         }
377
378         // if we removed error insets before we ran LaTeX or if we inserted
379         // error insets after we ran LaTeX this must be run:
380         if (a || (result & LaTeX::ERRORS)){
381                 bv->redraw();
382                 bv->fitCursor();
383                 //bv->updateScrollbar();
384         }
385
386         // check return value from latex.run().
387         if ((result & LaTeX::NO_LOGFILE)) {
388                 WriteAlert(_("LaTeX did not work!"),
389                            _("Missing log file:"), name);
390         } else if ((result & LaTeX::ERRORS)) {
391                 int num_errors = latex.getNumErrors();
392                 string s;
393                 string t;
394                 if (num_errors == 1) {
395                         s = _("One error detected");
396                         t = _("You should try to fix it.");
397                 } else {
398                         s = tostr(num_errors);
399                         s += _(" errors detected.");
400                         t = _("You should try to fix them.");
401                 }
402                 WriteAlert(_("There were errors during the LaTeX run."),
403                            s, t);
404         }
405         AllowInput(bv);
406  
407         return (result & (LaTeX::NO_LOGFILE | LaTeX::ERRORS)) == 0;
408
409 }
410