]> git.lyx.org Git - lyx.git/blob - src/EmbeddedFiles.cpp
Revert part of 21965 which was debugging code.
[lyx.git] / src / EmbeddedFiles.cpp
1 // -*- C++ -*-
2 /**
3  * \file EmbeddedFiles.cpp
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Bo Peng
8  *
9  * Full author contact details are available in file CREDITS.
10  *
11  */
12
13 #include <config.h>
14
15 #include "EmbeddedFiles.h"
16
17 #include "Buffer.h"
18 #include "BufferParams.h"
19 #include "ErrorList.h"
20 #include "Format.h"
21 #include "InsetIterator.h"
22 #include "Lexer.h"
23 #include "LyX.h"
24 #include "Paragraph.h"
25 #include "Session.h"
26
27 #include "frontends/alert.h"
28
29 #include "support/debug.h"
30 #include "support/filetools.h"
31 #include "support/gettext.h"
32 #include "support/convert.h"
33 #include "support/lyxlib.h"
34 #include "support/lstrings.h"
35 #include "support/FileZipListDir.h"
36
37 #include <sstream>
38 #include <fstream>
39 #include <utility>
40
41 using std::ofstream;
42 using std::endl;
43 using std::vector;
44 using std::string;
45 using std::pair;
46 using std::make_pair;
47 using std::istream;
48 using std::ostream;
49 using std::getline;
50 using std::istringstream;
51
52 namespace lyx {
53
54 namespace Alert = frontend::Alert;
55
56 using support::FileName;
57 using support::DocFileName;
58 using support::makeAbsPath;
59 using support::addName;
60 using support::absolutePath;
61 using support::onlyFilename;
62 using support::makeRelPath;
63 using support::changeExtension;
64 using support::bformat;
65 using support::prefixIs;
66 using support::makedir;
67
68
69 EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
70         bool embed, Inset const * inset)
71         : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
72                 inset_list_()
73 {
74         if (inset != NULL)
75                 inset_list_.push_back(inset);
76 }
77
78
79 string EmbeddedFile::embeddedFile(Buffer const * buf) const
80 {
81         return addName(buf->temppath(), inzip_name_);
82 }
83
84
85 void EmbeddedFile::addInset(Inset const * inset)
86 {
87         inset_list_.push_back(inset);
88 }
89
90
91 Inset const * EmbeddedFile::inset(int idx) const
92 {
93         BOOST_ASSERT(idx < refCount());
94         // some embedded file do not have a valid par iterator
95         return inset_list_[idx];
96 }
97
98
99 void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const
100 {
101         Inset const * ptr = inset(idx);
102         // This might not be the most efficient method ... 
103         for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
104                 if (&(*it) == ptr) {
105                         // this is basically BufferView::saveBookmark(0)
106                         LyX::ref().session().bookmarks().save(
107                                 FileName(buf->absFileName()),
108                                 it.bottom().pit(),
109                                 it.bottom().pos(),
110                                 it.paragraph().id(),
111                                 it.pos(),
112                                 0
113                         );
114                 }
115         // this inset can not be located. There is something wrong that needs
116         // to be fixed.
117         BOOST_ASSERT(true);
118 }
119
120
121 string EmbeddedFile::availableFile(Buffer const * buf) const
122 {
123         return embedded() ? embeddedFile(buf) : absFilename();
124 }
125
126
127 bool EmbeddedFile::extract(Buffer const * buf) const
128 {
129         string ext_file = absFilename();
130         string emb_file = embeddedFile(buf);
131
132         FileName emb(emb_file);
133         FileName ext(ext_file);
134
135         if (!emb.exists())
136                 return false;
137
138         // if external file already exists ...
139         if (ext.exists()) {
140                 // no need to copy if the files are the same
141                 if (checksum() == FileName(emb_file).checksum())
142                         return true;
143                 // otherwise, ask if overwrite
144                 int ret = Alert::prompt(
145                         _("Overwrite external file?"),
146                         bformat(_("External file %1$s already exists, do you want to overwrite it"),
147                                 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
148                 if (ret != 0)
149                         // if the user does not want to overwrite, we still consider it
150                         // a successful operation.
151                         return true;
152         }
153         // copy file
154
155         // need to make directory?
156         FileName path = ext.onlyPath();
157         if (!path.isDirectory())
158                 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
159         if (emb.copyTo(ext))
160                 return true;
161         Alert::error(_("Copy file failure"),
162                  bformat(_("Cannot copy file %1$s to %2$s.\n"
163                                  "Please check whether the directory exists and is writeable."),
164                                 from_utf8(emb_file), from_utf8(ext_file)));
165         //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
166         return false;
167 }
168
169
170 bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
171 {
172         string ext_file = absFilename();
173         string emb_file = embeddedFile(buf);
174
175         FileName emb(emb_file);
176         FileName ext(ext_file);
177
178         if (!ext.exists())
179                 return false;
180         
181         // if embedded file already exists ...
182         if (emb.exists()) {
183                 // no need to copy if the files are the same
184                 if (checksum() == FileName(emb_file).checksum())
185                         return true;
186                 // other wise, ask if overwrite
187                 int const ret = Alert::prompt(
188                         _("Update embedded file?"),
189                         bformat(_("Embedded file %1$s already exists, do you want to overwrite it"),
190                                 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
191                 if (ret != 0)
192                         // if the user does not want to overwrite, we still consider it
193                         // a successful operation.
194                         return true;
195         }
196         // copy file
197         // need to make directory?
198         FileName path = emb.onlyPath();
199         if (!path.isDirectory())
200                 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
201         if (ext.copyTo(emb))
202                 return true;
203         Alert::error(_("Copy file failure"),
204                  bformat(_("Cannot copy file %1$s to %2$s.\n"
205                            "Please check whether the directory exists and is writeable."),
206                                 from_utf8(ext_file), from_utf8(emb_file)));
207         //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
208         return false;
209 }
210
211
212 void EmbeddedFile::updateInsets(Buffer const * buf) const
213 {
214         vector<Inset const *>::const_iterator it = inset_list_.begin();
215         vector<Inset const *>::const_iterator it_end = inset_list_.end();
216         for (; it != it_end; ++it)
217                 const_cast<Inset *>(*it)->updateEmbeddedFile(*buf, *this);
218 }
219
220
221 bool EmbeddedFiles::enabled() const
222 {
223         return buffer_->params().embedded;
224 }
225
226
227 void EmbeddedFiles::enable(bool flag)
228 {
229         if (enabled() != flag) {
230                 // if enable, copy all files to temppath()
231                 // if disable, extract all files
232                 if ((flag && !updateFromExternalFile()) || (!flag && !extract()))
233                         return;
234                 // if operation is successful
235                 buffer_->markDirty();
236                 buffer_->params().embedded = flag;
237                 if (flag)
238                         updateInsets();
239         }
240 }
241
242
243 EmbeddedFile & EmbeddedFiles::registerFile(string const & filename,
244         bool embed, Inset const * inset, string const & inzipName)
245 {
246         // filename can be relative or absolute, translate to absolute filename
247         string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
248         // try to find this file from the list
249         EmbeddedFileList::iterator it = file_list_.begin();
250         EmbeddedFileList::iterator it_end = file_list_.end();
251         for (; it != it_end; ++it)
252                 if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
253                         break;
254         // find this filename, keep the original embedding status
255         if (it != file_list_.end()) {
256                 it->addInset(inset);
257                 return *it;
258         }
259         //
260         file_list_.push_back(EmbeddedFile(abs_filename, 
261                 getInzipName(abs_filename, inzipName), embed, inset));
262         return file_list_.back();
263 }
264
265
266 void EmbeddedFiles::update()
267 {
268         file_list_.clear();
269
270         for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it)
271                 it->registerEmbeddedFiles(*buffer_, *this);
272 }
273
274
275 bool EmbeddedFiles::writeFile(DocFileName const & filename)
276 {
277         // file in the temporary path has the content
278         string const content = FileName(addName(buffer_->temppath(),
279                 "content.lyx")).toFilesystemEncoding();
280
281         vector<pair<string, string> > filenames;
282         // add content.lyx to filenames
283         filenames.push_back(make_pair(content, "content.lyx"));
284         // prepare list of embedded file
285         EmbeddedFileList::iterator it = file_list_.begin();
286         EmbeddedFileList::iterator it_end = file_list_.end();
287         for (; it != it_end; ++it) {
288                 if (it->embedded()) {
289                         string file = it->availableFile(buffer_);
290                         if (file.empty())
291                                 lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl;
292                         else
293                                 filenames.push_back(make_pair(file, it->inzipName()));
294                 }
295         }
296         // write a zip file with all these files. Write to a temp file first, to
297         // avoid messing up the original file in case something goes terribly wrong.
298         DocFileName zipfile(addName(buffer_->temppath(),
299                 onlyFilename(changeExtension(
300                         filename.toFilesystemEncoding(), ".zip"))));
301
302         ::zipFiles(zipfile.toFilesystemEncoding(), filenames);
303         // copy file back
304         if (!zipfile.copyTo(filename)) {
305                 Alert::error(_("Save failure"),
306                                  bformat(_("Cannot create file %1$s.\n"
307                                            "Please check whether the directory exists and is writeable."),
308                                          from_utf8(filename.absFilename())));
309                 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
310         }
311         return true;
312 }
313
314
315 EmbeddedFiles::EmbeddedFileList::const_iterator
316 EmbeddedFiles::find(std::string filename) const
317 {
318         EmbeddedFileList::const_iterator it = file_list_.begin();
319         EmbeddedFileList::const_iterator it_end = file_list_.end();
320         for (; it != it_end; ++it)
321                 if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)     
322                         return it;
323         return file_list_.end();
324 }
325
326
327 bool EmbeddedFiles::extract() const
328 {
329         EmbeddedFileList::const_iterator it = file_list_.begin();
330         EmbeddedFileList::const_iterator it_end = file_list_.end();
331         for (; it != it_end; ++it)
332                 if (it->embedded())
333                         if(!it->extract(buffer_))
334                                 return false;
335         return true;
336 }
337
338
339 bool EmbeddedFiles::updateFromExternalFile() const
340 {
341         EmbeddedFileList::const_iterator it = file_list_.begin();
342         EmbeddedFileList::const_iterator it_end = file_list_.end();
343         for (; it != it_end; ++it)
344                 if (it->embedded())
345                         if (!it->updateFromExternalFile(buffer_))
346                                 return false;
347         return true;
348 }
349
350
351 string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name)
352 {
353         // register a new one, using relative file path as inzip_name
354         string inzip_name = name;
355         if (name.empty())
356                 inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename),
357                         from_utf8(buffer_->filePath())));
358         // if inzip_name is an absolute path, use filename only to avoid
359         // leaking of filesystem information in inzip_name
360         // The second case covers cases '../path/file' and '.'
361         if (absolutePath(inzip_name) || prefixIs(inzip_name, "."))
362                 inzip_name = onlyFilename(abs_filename);
363         // if this name has been used...
364         // use _1_name, _2_name etc
365         string tmp = inzip_name;
366         EmbeddedFileList::iterator it;
367         EmbeddedFileList::iterator it_end = file_list_.end();
368         bool unique_name = false;
369         size_t i = 0;
370         while (!unique_name) {
371                 unique_name = true;
372                 if (i > 0)
373                         inzip_name = convert<string>(i) + "_" + tmp;
374                 it = file_list_.begin();
375                 for (; it != it_end; ++it)
376                         if (it->inzipName() == inzip_name) {
377                                 unique_name = false;
378                                 ++i;
379                                 break;
380                         }
381         }
382         return inzip_name;
383 }
384
385
386 void EmbeddedFiles::updateInsets() const
387 {
388         EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
389         EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end();
390         for (; it != it_end; ++it)
391                 if (it->refCount() > 0)
392                         it->updateInsets(buffer_);
393 }
394
395
396 }