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