]> git.lyx.org Git - lyx.git/blob - src/insets/ExternalSupport.C
Fix error message positioning
[lyx.git] / src / insets / ExternalSupport.C
1 /**
2  * \file ExternalSupport.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup Nielsen
7  * \author Angus Leeming
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "ExternalSupport.h"
15 #include "ExternalTemplate.h"
16 #include "ExternalTransforms.h"
17 #include "insetexternal.h"
18
19 #include "buffer.h"
20 #include "converter.h"
21 #include "debug.h"
22 #include "exporter.h"
23 #include "format.h"
24 #include "mover.h"
25
26 #include "support/filetools.h"
27 #include "support/forkedcall.h"
28 #include "support/lstrings.h"
29 #include "support/lyxalgo.h"
30 #include "support/lyxlib.h"
31 #include "support/package.h"
32 #include "support/path.h"
33
34 #include "support/std_ostream.h"
35
36 namespace support = lyx::support;
37
38 using std::endl;
39
40 using std::ostream;
41 using std::string;
42 using std::vector;
43
44
45 namespace lyx {
46 namespace external {
47
48 Template const * getTemplatePtr(InsetExternalParams const & params)
49 {
50         TemplateManager const & etm = TemplateManager::get();
51         return etm.getTemplateByName(params.templatename());
52 }
53
54
55 void editExternal(InsetExternalParams const & params, Buffer const & buffer)
56 {
57         string const file_with_path = params.filename.absFilename();
58         formats.edit(buffer, file_with_path,
59                      formats.getFormatFromFile(file_with_path));
60 }
61
62
63 string const doSubstitution(InsetExternalParams const & params,
64                             Buffer const & buffer, string const & s,
65                             bool external_in_tmpdir,
66                             Substitute what)
67 {
68         Buffer const * m_buffer = buffer.getMasterBuffer();
69         string const parentpath = external_in_tmpdir ?
70                 m_buffer->temppath() :
71                 buffer.filePath();
72         string const filename = external_in_tmpdir ?
73                 params.filename.mangledFilename() :
74                 params.filename.outputFilename(parentpath);
75         string const basename = support::ChangeExtension(
76                         support::OnlyFilename(filename), string());
77         string const absname = support::MakeAbsPath(filename, parentpath);
78
79         string result = s;
80         if (what != ALL_BUT_PATHS) {
81                 string const filepath = support::OnlyPath(filename);
82                 string const abspath = support::OnlyPath(absname);
83                 string const masterpath = external_in_tmpdir ?
84                         m_buffer->temppath() :
85                         m_buffer->filePath();
86                 string relToMasterPath = support::OnlyPath(
87                                 support::MakeRelPath(absname, masterpath));
88                 if (relToMasterPath == "./")
89                         relToMasterPath.clear();
90                 string relToParentPath = support::OnlyPath(
91                                 support::MakeRelPath(absname, parentpath));
92                 if (relToParentPath == "./")
93                         relToParentPath.clear();
94
95                 result = support::subst(result, "$$FPath", filepath);
96                 result = support::subst(result, "$$AbsPath", abspath);
97                 result = support::subst(result, "$$RelPathMaster",
98                                         relToMasterPath);
99                 result = support::subst(result, "$$RelPathParent",
100                                         relToParentPath);
101                 if (support::AbsolutePath(filename)) {
102                         result = support::subst(result, "$$AbsOrRelPathMaster",
103                                                 abspath);
104                         result = support::subst(result, "$$AbsOrRelPathParent",
105                                                 abspath);
106                 } else {
107                         result = support::subst(result, "$$AbsOrRelPathMaster",
108                                                 relToMasterPath);
109                         result = support::subst(result, "$$AbsOrRelPathParent",
110                                                 relToParentPath);
111                 }
112         }
113
114         if (what == PATHS)
115                 return result;
116
117         result = support::subst(result, "$$FName", filename);
118         result = support::subst(result, "$$Basename", basename);
119         result = support::subst(result, "$$Extension",
120                         '.' + support::GetExtension(filename));
121         result = support::subst(result, "$$Tempname", params.tempname());
122         result = support::subst(result, "$$Sysdir",
123                                 support::package().system_support());
124
125         // Handle the $$Contents(filename) syntax
126         if (support::contains(result, "$$Contents(\"")) {
127
128                 string::size_type const pos = result.find("$$Contents(\"");
129                 string::size_type const end = result.find("\")", pos);
130                 string const file = result.substr(pos + 12, end - (pos + 12));
131                 string contents;
132
133                 string const filepath = support::IsFileReadable(file) ?
134                         buffer.filePath() : m_buffer->temppath();
135                 support::Path p(filepath);
136
137                 if (support::IsFileReadable(file))
138                         contents = support::GetFileContents(file);
139
140                 result = support::subst(result,
141                                         ("$$Contents(\"" + file + "\")").c_str(),
142                                         contents);
143         }
144
145         return result;
146 }
147
148
149 namespace {
150
151 /** update the file represented by the template.
152     If \p external_in_tmpdir == true, then the generated file is
153     placed in the buffer's temporary directory.
154 */
155 void updateExternal(InsetExternalParams const & params,
156                     string const & format,
157                     Buffer const & buffer,
158                     ExportData & exportdata,
159                     bool external_in_tmpdir)
160 {
161         Template const * const et_ptr = getTemplatePtr(params);
162         if (!et_ptr)
163                 return; // FAILURE
164         Template const & et = *et_ptr;
165
166         if (!et.automaticProduction)
167                 return; // NOT_NEEDED
168
169         Template::Formats::const_iterator cit = et.formats.find(format);
170         if (cit == et.formats.end())
171                 return; // FAILURE
172
173         Template::Format const & outputFormat = cit->second;
174         if (outputFormat.updateResult.empty())
175                 return; // NOT_NEEDED
176
177         string from_format = et.inputFormat;
178         if (from_format.empty())
179                 return; // NOT_NEEDED
180
181         string abs_from_file = params.filename.absFilename();
182
183         if (from_format == "*") {
184                 if (abs_from_file.empty())
185                         return; // NOT_NEEDED
186
187                 // Try and ascertain the file format from its contents.
188                 from_format = formats.getFormatFromFile(abs_from_file);
189                 if (from_format.empty())
190                         return; // FAILURE
191
192         }
193
194         string const to_format = outputFormat.updateFormat;
195         if (to_format.empty())
196                 return; // NOT_NEEDED
197
198         if (!converters.isReachable(from_format, to_format)) {
199                 lyxerr[Debug::EXTERNAL]
200                         << "external::updateExternal. "
201                         << "Unable to convert from "
202                         << from_format << " to " << to_format << endl;
203                 return; // FAILURE
204         }
205
206         // The master buffer. This is useful when there are multiple levels
207         // of include files
208         Buffer const * m_buffer = buffer.getMasterBuffer();
209
210         // We copy the source file to the temp dir and do the conversion
211         // there if necessary
212         string const temp_file =
213                 support::MakeAbsPath(params.filename.mangledFilename(),
214                                      m_buffer->temppath());
215         if (!abs_from_file.empty()) {
216                 unsigned long const from_checksum = support::sum(abs_from_file);
217                 unsigned long const temp_checksum = support::sum(temp_file);
218
219                 if (from_checksum != temp_checksum) {
220                         Mover const & mover = movers(from_format);
221                         if (!mover.copy(abs_from_file, temp_file)) {
222                                 lyxerr[Debug::EXTERNAL]
223                                         << "external::updateExternal. "
224                                         << "Unable to copy "
225                                         << abs_from_file << " to " << temp_file << endl;
226                                 return; // FAILURE
227                         }
228                 }
229         }
230
231         // the generated file (always in the temp dir)
232         string const to_file = doSubstitution(params, buffer,
233                                               outputFormat.updateResult,
234                                               true);
235         string const abs_to_file =
236                 support::MakeAbsPath(to_file, m_buffer->temppath());
237
238         // Record the referenced files for the exporter.
239         // The exporter will copy them to the export dir.
240         typedef Template::Format::FileMap FileMap;
241         FileMap::const_iterator rit  = outputFormat.referencedFiles.begin();
242         FileMap::const_iterator rend = outputFormat.referencedFiles.end();
243         for (; rit != rend; ++rit) {
244                 vector<string>::const_iterator fit  = rit->second.begin();
245                 vector<string>::const_iterator fend = rit->second.end();
246                 for (; fit != fend; ++fit) {
247                         string const source = support::MakeAbsPath(
248                                         doSubstitution(params, buffer, *fit,
249                                                        true),
250                                         m_buffer->temppath());
251                         // The path of the referenced file is never the
252                         // temp path, but the filename may be the mangled
253                         // or the real name. Therefore we substitute the
254                         // paths and names separately.
255                         string file = support::subst(*fit, "$$FName",
256                                         "$$FPath$$Basename$$Extension");
257                         file = doSubstitution(params, buffer, file, false,
258                                               PATHS);
259                         file = doSubstitution(params, buffer, file,
260                                               external_in_tmpdir,
261                                               ALL_BUT_PATHS);
262                         // if file is a relative name, it is interpreted
263                         // relative to the master document.
264                         exportdata.addExternalFile(rit->first, source, file);
265                 }
266         }
267
268         // Do we need to perform the conversion?
269         // Yes if to_file does not exist or if from_file is newer than to_file
270         if (support::compare_timestamps(temp_file, abs_to_file) < 0)
271                 return; // SUCCESS
272         string const to_file_base =
273                 support::ChangeExtension(to_file, string());
274         /* bool const success = */
275                 converters.convert(&buffer, temp_file, to_file_base,
276                                    from_format, to_format, true);
277         // return success
278 }
279
280
281 string const substituteCommands(InsetExternalParams const & params,
282                                 string const & input, string const & format);
283
284 string const substituteOptions(InsetExternalParams const & params,
285                                string const & input, string const & format);
286
287 } // namespace anon
288
289
290 int writeExternal(InsetExternalParams const & params,
291                   string const & format,
292                   Buffer const & buffer, ostream & os,
293                   ExportData & exportdata,
294                   bool external_in_tmpdir)
295 {
296         Template const * const et_ptr = getTemplatePtr(params);
297         if (!et_ptr)
298                 return 0;
299         Template const & et = *et_ptr;
300
301         Template::Formats::const_iterator cit = et.formats.find(format);
302         if (cit == et.formats.end()) {
303                 lyxerr[Debug::EXTERNAL]
304                         << "External template format '" << format
305                         << "' not specified in template "
306                         << params.templatename() << endl;
307                 return 0;
308         }
309
310         updateExternal(params, format, buffer, exportdata, external_in_tmpdir);
311
312         string str = doSubstitution(params, buffer, cit->second.product,
313                                     external_in_tmpdir);
314         str = substituteCommands(params, str, format);
315         str = substituteOptions(params, str, format);
316         os << str;
317         return int(lyx::count(str.begin(), str.end(),'\n'));
318 }
319
320 namespace {
321
322 // Empty template, specialised below.
323 template <typename TransformType>
324 string const substituteIt(string const &,
325                           TransformID,
326                           string const &,
327                           Template::Format const &,
328                           InsetExternalParams const &);
329
330
331 template <>
332 string const substituteIt<TransformCommand>(string const & input,
333                                             TransformID id,
334                                             string const & /* formatname */,
335                                             Template::Format const & format,
336                                             InsetExternalParams const & params)
337 {
338         typedef std::map<TransformID, TransformStore> Transformers;
339         Transformers::const_iterator it = format.command_transformers.find(id);
340         if (it == format.command_transformers.end())
341                 return input;
342
343         TransformStore const & store = it->second;
344
345         TransformCommand::ptr_type ptr;
346         if (id == Rotate)
347                 ptr = store.getCommandTransformer(params.rotationdata);
348         else if (id == Resize)
349                 ptr = store.getCommandTransformer(params.resizedata);
350
351         if (!ptr.get())
352                 return input;
353
354         string result =
355                 support::subst(input, ptr->front_placeholder(), ptr->front());
356         return support::subst(result, ptr->back_placeholder(),  ptr->back());
357 }
358
359
360 template <>
361 string const substituteIt<TransformOption>(string const & input,
362                                            TransformID id,
363                                            string const & fname,
364                                            Template::Format const & format,
365                                            InsetExternalParams const & params)
366 {
367         typedef std::map<TransformID, TransformStore> Transformers;
368         Transformers::const_iterator it = format.option_transformers.find(id);
369         if (it == format.option_transformers.end())
370                 return input;
371
372         TransformStore const & store = it->second;
373
374         TransformOption::ptr_type ptr;
375         switch (id) {
376         case Clip:
377                 ptr = store.getOptionTransformer(params.clipdata);
378                 break;
379         case Extra:
380                 ptr = store.getOptionTransformer(params.extradata.get(fname));
381                 break;
382         case Rotate:
383                 ptr = store.getOptionTransformer(params.rotationdata);
384                 break;
385         case Resize:
386                 ptr = store.getOptionTransformer(params.resizedata);
387                 break;
388         }
389
390         if (!ptr.get())
391                 return input;
392
393         return support::subst(input, ptr->placeholder(), ptr->option());
394 }
395
396
397 template <typename TransformerType>
398 string const transformIt(InsetExternalParams const & params,
399                          string const & s, string const & formatname)
400 {
401         Template const * const et = getTemplatePtr(params);
402         if (!et || et->transformIds.empty())
403                 return s;
404
405         Template::Formats::const_iterator fit = et->formats.find(formatname);
406         if (fit == et->formats.end())
407                 return s;
408
409         string result = s;
410         Template::Format const & format =  fit->second;
411
412         typedef vector<TransformID> TransformsIDs;
413         TransformsIDs::const_iterator it  = et->transformIds.begin();
414         TransformsIDs::const_iterator end = et->transformIds.end();
415         for (; it != end; ++it) {
416                 result = substituteIt<TransformerType>(result, *it, formatname,
417                                                        format, params);
418         }
419         return result;
420 }
421
422
423 string const substituteCommands(InsetExternalParams const & params,
424                                 string const & input, string const & format)
425 {
426         return transformIt<TransformCommand>(params, input, format);
427 }
428
429
430 string const substituteOption(InsetExternalParams const & params,
431                               string const & input, string const & format)
432 {
433         string opt = transformIt<TransformOption>(params, input, format);
434
435         if (format == "LaTeX" || format == "PDFLaTeX")
436                 return sanitizeLatexOption(opt);
437         if (format == "DocBook")
438                 return sanitizeDocBookOption(opt);
439         if (format == "LinuxDoc")
440                 return sanitizeLinuxDocOption(opt);
441         return opt;
442 }
443
444
445 string const substituteOptions(InsetExternalParams const & params,
446                                string const & input, string const & format)
447 {
448         string output = input;
449
450         Template const * const et = getTemplatePtr(params);
451         if (!et || et->transformIds.empty())
452                 return output;
453
454         Template::Formats::const_iterator fit = et->formats.find(format);
455         if (fit == et->formats.end() || fit->second.options.empty())
456                 return output;
457
458         typedef vector<Template::Option> Options;
459         Options const & options = fit->second.options;
460         Options::const_iterator it  = options.begin();
461         Options::const_iterator end = options.end();
462         for (; it != end; ++it) {
463                 string const opt = substituteOption(params, it->option, format);
464                 string const placeholder = "$$" + it->name;
465                 output = support::subst(output, placeholder, opt);
466         }
467
468         return output;
469  }
470
471 } // namespace anon
472
473 } // namespace external
474 } // namespace lyx