]> git.lyx.org Git - lyx.git/blob - src/support/FileName.cpp
isome more FileName shuffling
[lyx.git] / src / support / FileName.cpp
1 /**
2  * \file FileName.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "support/FileName.h"
14 #include "support/filetools.h"
15 #include "support/lstrings.h"
16 #include "support/os.h"
17 #include "support/qstring_helpers.h"
18
19 #include "debug.h"
20 #include "lyxlib.h"
21
22 #include <QFile>
23 #include <QFileInfo>
24
25 #include <boost/filesystem/exception.hpp>
26 #include <boost/filesystem/operations.hpp>
27
28 #include <map>
29 #include <sstream>
30 #include <algorithm>
31
32
33 using std::map;
34 using std::string;
35
36 namespace lyx {
37 namespace support {
38
39
40 /////////////////////////////////////////////////////////////////////
41 //
42 // FileName
43 //
44 /////////////////////////////////////////////////////////////////////
45
46
47 FileName::FileName(string const & abs_filename)
48         : name_(abs_filename)
49 {
50         BOOST_ASSERT(empty() || absolutePath(name_));
51 #if defined(_WIN32)
52         BOOST_ASSERT(!contains(name_, '\\'));
53 #endif
54 }
55
56
57 void FileName::set(string const & name)
58 {
59         name_ = name;
60         BOOST_ASSERT(absolutePath(name_));
61 #if defined(_WIN32)
62         BOOST_ASSERT(!contains(name_, '\\'));
63 #endif
64 }
65
66
67 void FileName::erase()
68 {
69         name_.erase();
70 }
71
72
73 string FileName::toFilesystemEncoding() const
74 {
75         QByteArray const encoded = QFile::encodeName(toqstr(name_));
76         return string(encoded.begin(), encoded.end());
77 }
78
79
80 FileName FileName::fromFilesystemEncoding(string const & name)
81 {
82         QByteArray const encoded(name.c_str(), name.length());
83         return FileName(fromqstr(QFile::decodeName(encoded)));
84 }
85
86
87 bool FileName::exists() const
88 {
89         return QFileInfo(toqstr(name_)).exists();
90 }
91
92
93 bool FileName::isDirectory() const
94 {
95         return QFileInfo(toqstr(name_)).isDir();
96 }
97
98
99 bool FileName::isReadOnly() const
100 {
101         QFileInfo const fi(toqstr(name_));
102         return fi.isReadable() && !fi.isWritable();
103 }
104
105
106 bool FileName::isReadable() const
107 {
108         QFileInfo const fi(toqstr(name_));
109         return fi.isReadable();
110 }
111
112
113 bool FileName::isFileReadable() const
114 {
115         QFileInfo const fi(toqstr(name_));
116         return fi.isFile() && fi.isReadable();
117 }
118
119
120 bool FileName::isWritable() const
121 {
122         QFileInfo const fi(toqstr(name_));
123         return fi.isReadable();
124 }
125
126
127 bool FileName::isDirWritable() const
128 {
129         LYXERR(Debug::FILES) << "isDirWriteable: " << *this << std::endl;
130
131         FileName const tmpfl(tempName(*this, "lyxwritetest"));
132
133         if (tmpfl.empty())
134                 return false;
135
136         unlink(tmpfl);
137         return true;
138 }
139
140
141 FileName FileName::tempName(FileName const & dir, std::string const & mask)
142 {
143         return support::tempName(dir, mask);
144 }
145
146
147 std::time_t FileName::lastModified() const
148 {
149         return boost::filesystem::last_write_time(toFilesystemEncoding());
150 }
151
152
153 bool operator==(FileName const & lhs, FileName const & rhs)
154 {
155         return lhs.absFilename() == rhs.absFilename();
156 }
157
158
159 bool operator!=(FileName const & lhs, FileName const & rhs)
160 {
161         return lhs.absFilename() != rhs.absFilename();
162 }
163
164
165 bool operator<(FileName const & lhs, FileName const & rhs)
166 {
167         return lhs.absFilename() < rhs.absFilename();
168 }
169
170
171 bool operator>(FileName const & lhs, FileName const & rhs)
172 {
173         return lhs.absFilename() > rhs.absFilename();
174 }
175
176
177 std::ostream & operator<<(std::ostream & os, FileName const & filename)
178 {
179         return os << filename.absFilename();
180 }
181
182
183 /////////////////////////////////////////////////////////////////////
184 //
185 // DocFileName
186 //
187 /////////////////////////////////////////////////////////////////////
188
189
190 DocFileName::DocFileName()
191         : save_abs_path_(true)
192 {}
193
194
195 DocFileName::DocFileName(string const & abs_filename, bool save_abs)
196         : FileName(abs_filename), save_abs_path_(save_abs), zipped_valid_(false)
197 {}
198
199
200 DocFileName::DocFileName(FileName const & abs_filename, bool save_abs)
201         : FileName(abs_filename), save_abs_path_(save_abs), zipped_valid_(false)
202 {}
203
204
205 void DocFileName::set(string const & name, string const & buffer_path)
206 {
207         save_abs_path_ = absolutePath(name);
208         name_ = save_abs_path_ ? name : makeAbsPath(name, buffer_path).absFilename();
209         zipped_valid_ = false;
210 }
211
212
213 void DocFileName::erase()
214 {
215         name_.erase();
216         zipped_valid_ = false;
217 }
218
219
220 string const DocFileName::relFilename(string const & path) const
221 {
222         // FIXME UNICODE
223         return to_utf8(makeRelPath(from_utf8(name_), from_utf8(path)));
224 }
225
226
227 string const DocFileName::outputFilename(string const & path) const
228 {
229         // FIXME UNICODE
230         return save_abs_path_ ? name_ : to_utf8(makeRelPath(from_utf8(name_), from_utf8(path)));
231 }
232
233
234 string const DocFileName::mangledFilename(std::string const & dir) const
235 {
236         // We need to make sure that every DocFileName instance for a given
237         // filename returns the same mangled name.
238         typedef map<string, string> MangledMap;
239         static MangledMap mangledNames;
240         MangledMap::const_iterator const it = mangledNames.find(name_);
241         if (it != mangledNames.end())
242                 return (*it).second;
243
244         // Now the real work
245         string mname = os::internal_path(name_);
246         // Remove the extension.
247         mname = changeExtension(name_, string());
248         // The mangled name must be a valid LaTeX name.
249         // The list of characters to keep is probably over-restrictive,
250         // but it is not really a problem.
251         // Apart from non-ASCII characters, at least the following characters
252         // are forbidden: '/', '.', ' ', and ':'.
253         // On windows it is not possible to create files with '<', '>' or '?'
254         // in the name.
255         static string const keep = "abcdefghijklmnopqrstuvwxyz"
256                                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
257                                    "+,-0123456789;=";
258         string::size_type pos = 0;
259         while ((pos = mname.find_first_not_of(keep, pos)) != string::npos)
260                 mname[pos++] = '_';
261         // Add the extension back on
262         mname = changeExtension(mname, getExtension(name_));
263
264         // Prepend a counter to the filename. This is necessary to make
265         // the mangled name unique.
266         static int counter = 0;
267         std::ostringstream s;
268         s << counter++ << mname;
269         mname = s.str();
270
271         // MiKTeX's YAP (version 2.4.1803) crashes if the file name
272         // is longer than about 160 characters. MiKTeX's pdflatex
273         // is even pickier. A maximum length of 100 has been proven to work.
274         // If dir.size() > max length, all bets are off for YAP. We truncate
275         // the filename nevertheless, keeping a minimum of 10 chars.
276
277         string::size_type max_length = std::max(100 - ((int)dir.size() + 1), 10);
278
279         // If the mangled file name is too long, hack it to fit.
280         // We know we're guaranteed to have a unique file name because
281         // of the counter.
282         if (mname.size() > max_length) {
283                 int const half = (int(max_length) / 2) - 2;
284                 if (half > 0) {
285                         mname = mname.substr(0, half) + "___" +
286                                 mname.substr(mname.size() - half);
287                 }
288         }
289
290         mangledNames[name_] = mname;
291         return mname;
292 }
293
294
295 bool DocFileName::isZipped() const
296 {
297         if (!zipped_valid_) {
298                 zipped_ = zippedFile(*this);
299                 zipped_valid_ = true;
300         }
301         return zipped_;
302 }
303
304
305 string const DocFileName::unzippedFilename() const
306 {
307         return unzippedFileName(name_);
308 }
309
310
311 bool operator==(DocFileName const & lhs, DocFileName const & rhs)
312 {
313         return lhs.absFilename() == rhs.absFilename() &&
314                 lhs.saveAbsPath() == rhs.saveAbsPath();
315 }
316
317
318 bool operator!=(DocFileName const & lhs, DocFileName const & rhs)
319 {
320         return !(lhs == rhs);
321 }
322
323 } // namespace support
324 } // namespace lyx