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