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