2 * \file ExternalSupport.C
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup Nielsen
7 * \author Angus Leeming
9 * Full author contact details are available in file CREDITS.
14 #include "ExternalSupport.h"
15 #include "ExternalTemplate.h"
16 #include "ExternalTransforms.h"
17 #include "insetexternal.h"
20 #include "converter.h"
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 #include "support/path.h"
35 namespace support = lyx::support;
46 Template const * getTemplatePtr(InsetExternalParams const & params)
48 TemplateManager const & etm = TemplateManager::get();
49 return etm.getTemplateByName(params.templatename());
53 void editExternal(InsetExternalParams const & params, Buffer const & buffer)
55 string const file_with_path = params.filename.absFilename();
56 formats.edit(buffer, file_with_path,
57 formats.getFormatFromFile(file_with_path));
63 string const subst_path(string const & input,
64 string const & placeholder,
67 support::latex_path_extension ext = support::PROTECT_EXTENSION,
68 support::latex_path_dots dots = support::LEAVE_DOTS)
70 if (input.find(placeholder) == string::npos)
72 string const path2 = use_latex_path ?
73 support::latex_path(path, ext, dots) :
74 support::os::external_path(path);
75 return support::subst(input, placeholder, path2);
81 string const doSubstitution(InsetExternalParams const & params,
82 Buffer const & buffer, string const & s,
84 bool external_in_tmpdir,
87 Buffer const * m_buffer = buffer.getMasterBuffer();
88 string const parentpath = external_in_tmpdir ?
89 m_buffer->temppath() :
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);
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();
114 result = subst_path(result, "$$FPath", filepath,
116 support::PROTECT_EXTENSION,
117 support::ESCAPE_DOTS);
118 result = subst_path(result, "$$AbsPath", abspath,
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);
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);
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(), use_latex_path);
161 result = subst_path(result, "$$Sysdir",
162 support::package().system_support(), use_latex_path);
164 // Handle the $$Contents(filename) syntax
165 if (support::contains(result, "$$Contents(\"")) {
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));
172 string const filepath = support::isFileReadable(file) ?
173 buffer.filePath() : m_buffer->temppath();
174 support::Path p(filepath);
176 if (support::isFileReadable(file))
177 contents = support::getFileContents(file);
179 result = support::subst(result,
180 ("$$Contents(\"" + file + "\")").c_str(),
190 /** update the file represented by the template.
191 If \p external_in_tmpdir == true, then the generated file is
192 placed in the buffer's temporary directory.
194 void updateExternal(InsetExternalParams const & params,
195 string const & format,
196 Buffer const & buffer,
197 ExportData & exportdata,
198 bool external_in_tmpdir)
200 Template const * const et_ptr = getTemplatePtr(params);
203 Template const & et = *et_ptr;
205 if (!et.automaticProduction)
206 return; // NOT_NEEDED
208 Template::Formats::const_iterator cit = et.formats.find(format);
209 if (cit == et.formats.end())
212 Template::Format const & outputFormat = cit->second;
213 if (outputFormat.updateResult.empty())
214 return; // NOT_NEEDED
216 string from_format = et.inputFormat;
217 if (from_format.empty())
218 return; // NOT_NEEDED
220 string abs_from_file = params.filename.absFilename();
222 if (from_format == "*") {
223 if (abs_from_file.empty())
224 return; // NOT_NEEDED
226 // Try and ascertain the file format from its contents.
227 from_format = formats.getFormatFromFile(abs_from_file);
228 if (from_format.empty())
233 string const to_format = outputFormat.updateFormat;
234 if (to_format.empty())
235 return; // NOT_NEEDED
237 if (!converters.isReachable(from_format, to_format)) {
238 lyxerr[Debug::EXTERNAL]
239 << "external::updateExternal. "
240 << "Unable to convert from "
241 << from_format << " to " << to_format << endl;
245 // The master buffer. This is useful when there are multiple levels
247 Buffer const * m_buffer = buffer.getMasterBuffer();
249 // We copy the source file to the temp dir and do the conversion
250 // there if necessary
251 string const temp_file =
252 support::makeAbsPath(params.filename.mangledFilename(),
253 m_buffer->temppath());
254 if (!abs_from_file.empty()) {
255 unsigned long const from_checksum = support::sum(abs_from_file);
256 unsigned long const temp_checksum = support::sum(temp_file);
258 if (from_checksum != temp_checksum) {
259 Mover const & mover = movers(from_format);
260 if (!mover.copy(abs_from_file, temp_file)) {
261 lyxerr[Debug::EXTERNAL]
262 << "external::updateExternal. "
264 << abs_from_file << " to " << temp_file << endl;
270 // the generated file (always in the temp dir)
271 string const to_file = doSubstitution(params, buffer,
272 outputFormat.updateResult,
274 string const abs_to_file =
275 support::makeAbsPath(to_file, m_buffer->temppath());
277 // Record the referenced files for the exporter.
278 // The exporter will copy them to the export dir.
279 typedef Template::Format::FileMap FileMap;
280 FileMap::const_iterator rit = outputFormat.referencedFiles.begin();
281 FileMap::const_iterator rend = outputFormat.referencedFiles.end();
282 for (; rit != rend; ++rit) {
283 vector<string>::const_iterator fit = rit->second.begin();
284 vector<string>::const_iterator fend = rit->second.end();
285 for (; fit != fend; ++fit) {
286 string const source = support::makeAbsPath(
287 doSubstitution(params, buffer, *fit,
289 m_buffer->temppath());
290 // The path of the referenced file is never the
291 // temp path, but the filename may be the mangled
292 // or the real name. Therefore we substitute the
293 // paths and names separately.
294 string file = support::subst(*fit, "$$FName",
295 "$$FPath$$Basename$$Extension");
296 file = doSubstitution(params, buffer, file, false, false,
298 file = doSubstitution(params, buffer, file,
299 false, external_in_tmpdir,
301 // if file is a relative name, it is interpreted
302 // relative to the master document.
303 exportdata.addExternalFile(rit->first, source, file);
307 // Do we need to perform the conversion?
308 // Yes if to_file does not exist or if from_file is newer than to_file
309 if (support::compare_timestamps(temp_file, abs_to_file) < 0)
311 string const to_file_base =
312 support::changeExtension(to_file, string());
314 // FIXME (Abdel 12/08/06): Is there a need to show these errors?
316 /* bool const success = */
317 converters.convert(&buffer, temp_file, to_file_base,
318 from_format, to_format, el, true);
323 string const substituteCommands(InsetExternalParams const & params,
324 string const & input, string const & format);
326 string const substituteOptions(InsetExternalParams const & params,
327 string const & input, string const & format);
332 int writeExternal(InsetExternalParams const & params,
333 string const & format,
334 Buffer const & buffer, lyx::odocstream & os,
335 ExportData & exportdata,
336 bool external_in_tmpdir,
337 bool external_in_comment)
339 Template const * const et_ptr = getTemplatePtr(params);
342 Template const & et = *et_ptr;
344 Template::Formats::const_iterator cit = et.formats.find(format);
345 if (cit == et.formats.end()) {
346 lyxerr[Debug::EXTERNAL]
347 << "External template format '" << format
348 << "' not specified in template "
349 << params.templatename() << endl;
353 if (!external_in_comment)
354 updateExternal(params, format, buffer, exportdata,
357 bool const use_latex_path = format == "LaTeX";
358 string str = doSubstitution(params, buffer, cit->second.product,
359 use_latex_path, external_in_tmpdir);
360 str = substituteCommands(params, str, format);
361 str = substituteOptions(params, str, format);
363 os << lyx::from_utf8(str);
364 return int(lyx::count(str.begin(), str.end(),'\n'));
369 // Empty template, specialised below.
370 template <typename TransformType>
371 string const substituteIt(string const &,
374 Template::Format const &,
375 InsetExternalParams const &);
379 string const substituteIt<TransformCommand>(string const & input,
381 string const & /* formatname */,
382 Template::Format const & format,
383 InsetExternalParams const & params)
385 typedef std::map<TransformID, TransformStore> Transformers;
386 Transformers::const_iterator it = format.command_transformers.find(id);
387 if (it == format.command_transformers.end())
390 TransformStore const & store = it->second;
392 TransformCommand::ptr_type ptr;
394 ptr = store.getCommandTransformer(params.rotationdata);
395 else if (id == Resize)
396 ptr = store.getCommandTransformer(params.resizedata);
402 support::subst(input, ptr->front_placeholder(), ptr->front());
403 return support::subst(result, ptr->back_placeholder(), ptr->back());
408 string const substituteIt<TransformOption>(string const & input,
410 string const & fname,
411 Template::Format const & format,
412 InsetExternalParams const & params)
414 typedef std::map<TransformID, TransformStore> Transformers;
415 Transformers::const_iterator it = format.option_transformers.find(id);
416 if (it == format.option_transformers.end())
419 TransformStore const & store = it->second;
421 TransformOption::ptr_type ptr;
424 ptr = store.getOptionTransformer(params.clipdata);
427 ptr = store.getOptionTransformer(params.extradata.get(fname));
430 ptr = store.getOptionTransformer(params.rotationdata);
433 ptr = store.getOptionTransformer(params.resizedata);
440 return support::subst(input, ptr->placeholder(), ptr->option());
444 template <typename TransformerType>
445 string const transformIt(InsetExternalParams const & params,
446 string const & s, string const & formatname)
448 Template const * const et = getTemplatePtr(params);
449 if (!et || et->transformIds.empty())
452 Template::Formats::const_iterator fit = et->formats.find(formatname);
453 if (fit == et->formats.end())
457 Template::Format const & format = fit->second;
459 typedef vector<TransformID> TransformsIDs;
460 TransformsIDs::const_iterator it = et->transformIds.begin();
461 TransformsIDs::const_iterator end = et->transformIds.end();
462 for (; it != end; ++it) {
463 result = substituteIt<TransformerType>(result, *it, formatname,
470 string const substituteCommands(InsetExternalParams const & params,
471 string const & input, string const & format)
473 return transformIt<TransformCommand>(params, input, format);
477 string const substituteOption(InsetExternalParams const & params,
478 string const & input, string const & format)
480 string opt = transformIt<TransformOption>(params, input, format);
482 if (format == "LaTeX" || format == "PDFLaTeX")
483 return sanitizeLatexOption(opt);
484 if (format == "DocBook")
485 return sanitizeDocBookOption(opt);
490 string const substituteOptions(InsetExternalParams const & params,
491 string const & input, string const & format)
493 string output = input;
495 Template const * const et = getTemplatePtr(params);
496 if (!et || et->transformIds.empty())
499 Template::Formats::const_iterator fit = et->formats.find(format);
500 if (fit == et->formats.end() || fit->second.options.empty())
503 typedef vector<Template::Option> Options;
504 Options const & options = fit->second.options;
505 Options::const_iterator it = options.begin();
506 Options::const_iterator end = options.end();
507 for (; it != end; ++it) {
508 string const opt = substituteOption(params, it->option, format);
509 string const placeholder = "$$" + it->name;
510 output = support::subst(output, placeholder, opt);
518 } // namespace external