]> git.lyx.org Git - lyx.git/blob - src/insets/ExternalSupport.C
use the movers also for copying from temp dir -> export dir
[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::getFormatFromContents(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::getFormatFromContents(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         // We copy the source file to the temp dir and do the conversion
200         // there if necessary
201         string const temp_file =
202                 support::MakeAbsPath(params.filename.mangledFilename(),
203                                      m_buffer->temppath());
204         if (!abs_from_file.empty()) {
205                 unsigned long const from_checksum = support::sum(abs_from_file);
206                 unsigned long const temp_checksum = support::sum(temp_file);
207
208                 if (from_checksum != temp_checksum) {
209                         Mover const & mover = movers(from_format);
210                         if (!mover.copy(abs_from_file, temp_file)) {
211                                 lyxerr[Debug::EXTERNAL]
212                                         << "external::updateExternal. "
213                                         << "Unable to copy "
214                                         << abs_from_file << " to " << temp_file << endl;
215                                 return; // FAILURE
216                         }
217                 }
218         }
219
220         // the generated file (always in the temp dir)
221         string const to_file = doSubstitution(params, buffer,
222                                               outputFormat.updateResult,
223                                               true);
224         string const abs_to_file =
225                 support::MakeAbsPath(to_file, m_buffer->temppath());
226
227         // Record the referenced files for the exporter.
228         // The exporter will copy them to the export dir.
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 source = support::MakeAbsPath(
237                                         doSubstitution(params, buffer, *fit,
238                                                        true),
239                                         m_buffer->temppath());
240                         string const file = doSubstitution(params, buffer,
241                                                            *fit,
242                                                            external_in_tmpdir);
243                         // if file is a relative name, it is interpreted
244                         // relative to the master document.
245                         exportdata.addExternalFile(rit->first, source, file);
246                 }
247         }
248
249         // Do we need to perform the conversion?
250         // Yes if to_file does not exist or if from_file is newer than to_file
251         if (support::compare_timestamps(temp_file, abs_to_file) < 0)
252                 return; // SUCCESS
253         string const to_file_base =
254                 support::ChangeExtension(to_file, string());
255         /* bool const success = */
256                 converters.convert(&buffer, temp_file, to_file_base,
257                                    from_format, to_format);
258         // return success
259 }
260
261
262 string const substituteCommands(InsetExternalParams const & params,
263                                 string const & input, string const & format);
264
265 string const substituteOptions(InsetExternalParams const & params,
266                                string const & input, string const & format);
267
268 } // namespace anon
269
270
271 int writeExternal(InsetExternalParams const & params,
272                   string const & format,
273                   Buffer const & buffer, ostream & os,
274                   ExportData & exportdata,
275                   bool external_in_tmpdir)
276 {
277         Template const * const et_ptr = getTemplatePtr(params);
278         if (!et_ptr)
279                 return 0;
280         Template const & et = *et_ptr;
281
282         Template::Formats::const_iterator cit = et.formats.find(format);
283         if (cit == et.formats.end()) {
284                 lyxerr[Debug::EXTERNAL]
285                         << "External template format '" << format
286                         << "' not specified in template "
287                         << params.templatename() << endl;
288                 return 0;
289         }
290
291         updateExternal(params, format, buffer, exportdata, external_in_tmpdir);
292
293         string str = doSubstitution(params, buffer, cit->second.product,
294                                     external_in_tmpdir);
295         str = substituteCommands(params, str, format);
296         str = substituteOptions(params, str, format);
297         os << str;
298         return int(lyx::count(str.begin(), str.end(),'\n') + 1);
299 }
300
301 namespace {
302
303 // Empty template, specialised below.
304 template <typename TransformType>
305 string const substituteIt(string const &,
306                           TransformID,
307                           string const &,
308                           Template::Format const &,
309                           InsetExternalParams const &);
310
311
312 template <>
313 string const substituteIt<TransformCommand>(string const & input,
314                                             TransformID id,
315                                             string const & /* formatname */,
316                                             Template::Format const & format,
317                                             InsetExternalParams const & params)
318 {
319         typedef std::map<TransformID, TransformStore> Transformers;
320         Transformers::const_iterator it = format.command_transformers.find(id);
321         if (it == format.command_transformers.end())
322                 return input;
323
324         TransformStore const & store = it->second;
325
326         TransformCommand::ptr_type ptr;
327         if (id == Rotate)
328                 ptr = store.getCommandTransformer(params.rotationdata);
329         else if (id == Resize)
330                 ptr = store.getCommandTransformer(params.resizedata);
331
332         if (!ptr.get())
333                 return input;
334
335         string result =
336                 support::subst(input, ptr->front_placeholder(), ptr->front());
337         return support::subst(result, ptr->back_placeholder(),  ptr->back());
338 }
339
340
341 template <>
342 string const substituteIt<TransformOption>(string const & input,
343                                            TransformID id,
344                                            string const & fname,
345                                            Template::Format const & format,
346                                            InsetExternalParams const & params)
347 {
348         typedef std::map<TransformID, TransformStore> Transformers;
349         Transformers::const_iterator it = format.option_transformers.find(id);
350         if (it == format.option_transformers.end())
351                 return input;
352
353         TransformStore const & store = it->second;
354
355         TransformOption::ptr_type ptr;
356         switch (id) {
357         case Clip:
358                 ptr = store.getOptionTransformer(params.clipdata);
359                 break;
360         case Extra:
361                 ptr = store.getOptionTransformer(params.extradata.get(fname));
362                 break;
363         case Rotate:
364                 ptr = store.getOptionTransformer(params.rotationdata);
365                 break;
366         case Resize:
367                 ptr = store.getOptionTransformer(params.resizedata);
368                 break;
369         }
370
371         if (!ptr.get())
372                 return input;
373
374         return support::subst(input, ptr->placeholder(), ptr->option());
375 }
376
377
378 template <typename TransformerType>
379 string const transformIt(InsetExternalParams const & params,
380                          string const & s, string const & formatname)
381 {
382         Template const * const et = getTemplatePtr(params);
383         if (!et || et->transformIds.empty())
384                 return s;
385
386         Template::Formats::const_iterator fit = et->formats.find(formatname);
387         if (fit == et->formats.end())
388                 return s;
389
390         string result = s;
391         Template::Format const & format =  fit->second;
392
393         typedef vector<TransformID> TransformsIDs;
394         TransformsIDs::const_iterator it  = et->transformIds.begin();
395         TransformsIDs::const_iterator end = et->transformIds.end();
396         for (; it != end; ++it) {
397                 result = substituteIt<TransformerType>(result, *it, formatname,
398                                                        format, params);
399         }
400         return result;
401 }
402
403
404 string const substituteCommands(InsetExternalParams const & params,
405                                 string const & input, string const & format)
406 {
407         return transformIt<TransformCommand>(params, input, format);
408 }
409
410
411 string const substituteOption(InsetExternalParams const & params,
412                               string const & input, string const & format)
413 {
414         string opt = transformIt<TransformOption>(params, input, format);
415
416         if (format == "LaTeX" || format == "PDFLaTeX")
417                 return sanitizeLatexOption(opt);
418         if (format == "DocBook")
419                 return sanitizeDocBookOption(opt);
420         if (format == "LinuxDoc")
421                 return sanitizeLinuxDocOption(opt);
422         return opt;
423 }
424
425
426 string const substituteOptions(InsetExternalParams const & params,
427                                string const & input, string const & format)
428 {
429         string output = input;
430
431         Template const * const et = getTemplatePtr(params);
432         if (!et || et->transformIds.empty())
433                 return output;
434
435         Template::Formats::const_iterator fit = et->formats.find(format);
436         if (fit == et->formats.end() || fit->second.options.empty())
437                 return output;
438
439         typedef vector<Template::Option> Options;
440         Options const & options = fit->second.options;
441         Options::const_iterator it  = options.begin();
442         Options::const_iterator end = options.end();
443         for (; it != end; ++it) {
444                 string const opt = substituteOption(params, it->option, format);
445                 string const placeholder = "$$" + it->name;
446                 output = support::subst(output, placeholder, opt);
447         }
448
449         return output;
450  }
451
452 } // namespace anon
453
454 } // namespace external
455 } // namespace lyx