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