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