|
1
|
1 #include "foobar2000-sdk-pch.h"
|
|
|
2
|
|
|
3
|
|
|
4 t_filestats2 fsItemBase::getStatsOpportunist() {
|
|
|
5 return filestats2_invalid;
|
|
|
6 }
|
|
|
7
|
|
|
8 void fsItemBase::remove(abort_callback& aborter) {
|
|
|
9 getFS()->remove(this->canonicalPath()->c_str(), aborter);
|
|
|
10 }
|
|
|
11
|
|
|
12 fb2k::stringRef fsItemBase::shortName() {
|
|
|
13 return nameWithExt();
|
|
|
14 }
|
|
|
15
|
|
|
16 file::ptr fsItemFile::openRead(abort_callback& aborter) {
|
|
|
17 return open(filesystem::open_mode_read, aborter);
|
|
|
18 }
|
|
|
19 file::ptr fsItemFile::openWriteExisting(abort_callback& aborter) {
|
|
|
20 return open(filesystem::open_mode_write_existing, aborter);
|
|
|
21 }
|
|
|
22 file::ptr fsItemFile::openWriteNew(abort_callback& aborter) {
|
|
|
23 return open(filesystem::open_mode_write_new, aborter);
|
|
|
24 }
|
|
|
25
|
|
|
26 void fsItemFolder::removeRecur(abort_callback& aborter) {
|
|
|
27 auto list = this->listContents(listMode::filesAndFolders | listMode::hidden | listMode::suppressStats, aborter);
|
|
|
28 for (auto i : list->typed< fsItemBase >()) {
|
|
|
29 fsItemFolderPtr f;
|
|
|
30 if (f &= i) f->removeRecur(aborter);
|
|
|
31 else i->remove(aborter);
|
|
|
32 }
|
|
|
33
|
|
|
34 this->remove(aborter);
|
|
|
35 }
|
|
|
36
|
|
|
37 void fsItemFile::copyToOther(fsItemFilePtr other, abort_callback& aborter) {
|
|
|
38 aborter.check();
|
|
|
39
|
|
|
40 auto fSource = this->openRead(aborter);
|
|
|
41 auto fTarget = other->openWriteNew(aborter);
|
|
|
42 file::g_transfer_file(fSource, fTarget, aborter);
|
|
|
43
|
|
|
44 // fTarget->commit(aborter);
|
|
|
45
|
|
|
46 // we cannot transfer other properties, this should be overridden for such
|
|
|
47 }
|
|
|
48
|
|
|
49 fsItemPtr fsItemBase::copyTo(fsItemFolderPtr folder, const char* desiredName, unsigned createMode, abort_callback& aborter) {
|
|
|
50 aborter.check();
|
|
|
51 {
|
|
|
52 fsItemFilePtr f;
|
|
|
53 if (f &= this) {
|
|
|
54 auto target = folder->createFile(desiredName, createMode, aborter);
|
|
|
55 f->copyToOther(target, aborter);
|
|
|
56 return std::move(target);
|
|
|
57 }
|
|
|
58 }
|
|
|
59 {
|
|
|
60 fsItemFolderPtr f;
|
|
|
61 if (f &= this) {
|
|
|
62 auto target = folder->createFolder(desiredName, createMode, aborter);
|
|
|
63 auto contents = f->listContents(listMode::filesAndFolders | listMode::hidden | listMode::suppressStats, aborter);
|
|
|
64 for (auto item : contents->typed<fsItemBase>()) {
|
|
|
65 item->copyTo(target, createMode, aborter);
|
|
|
66 }
|
|
|
67 return std::move(target);
|
|
|
68 }
|
|
|
69 }
|
|
|
70 PFC_ASSERT(!"Should not get here, bad fsItemBase object");
|
|
|
71 return nullptr;
|
|
|
72 }
|
|
|
73
|
|
|
74 fsItemPtr fsItemBase::copyTo(fsItemFolderPtr folder, unsigned createMode, abort_callback& aborter) {
|
|
|
75 auto temp = this->nameWithExt();
|
|
|
76 return this->copyTo(folder, temp->c_str(), createMode, aborter);
|
|
|
77 }
|
|
|
78
|
|
|
79 fsItemPtr fsItemBase::moveTo(fsItemFolderPtr folder, const char* desiredName, unsigned createMode, abort_callback& aborter) {
|
|
|
80 auto ret = this->copyTo(folder, desiredName, createMode, aborter);
|
|
|
81 fsItemFolder::ptr asFolder;
|
|
|
82 if (asFolder &= this) {
|
|
|
83 asFolder->removeRecur(aborter);
|
|
|
84 } else {
|
|
|
85 this->remove(aborter);
|
|
|
86 }
|
|
|
87 return ret;
|
|
|
88 }
|
|
|
89
|
|
|
90 fsItemPtr fsItemBase::moveTo(fsItemFolderPtr folder, unsigned createMode, abort_callback& aborter) {
|
|
|
91 auto fn = this->nameWithExt();
|
|
|
92 return this->moveTo(folder, fn->c_str(), createMode, aborter);
|
|
|
93 }
|
|
|
94 fb2k::memBlockRef fsItemFile::readWhole(size_t sizeSanity, abort_callback& aborter) {
|
|
|
95 auto file = this->openRead(aborter);
|
|
|
96 auto size64 = file->get_size_ex(aborter);
|
|
|
97 if (size64 > sizeSanity) throw exception_io_data();
|
|
|
98 auto size = (size_t)size64;
|
|
|
99 pfc::mem_block block;
|
|
|
100 block.resize(size);
|
|
|
101 file->read_object(block.ptr(), size, aborter);
|
|
|
102 return fb2k::memBlock::blockWithData(std::move(block));
|
|
|
103 }
|
|
|
104
|
|
|
105 namespace {
|
|
|
106 static void uniqueFn(pfc::string8& fn, unsigned add) {
|
|
|
107 if (add > 0) {
|
|
|
108 pfc::string8 fnOnly = pfc::remove_ext_v2(fn);
|
|
|
109 pfc::string8 ext = pfc::extract_ext_v2(fn);
|
|
|
110 fn = std::move(fnOnly);
|
|
|
111 fn << " (" << add << ")";
|
|
|
112 if (ext.length() > 0) fn << "." << ext;
|
|
|
113 }
|
|
|
114 }
|
|
|
115
|
|
|
116 using namespace fb2k;
|
|
|
117 class fsItemFileStd : public fsItemFile {
|
|
|
118 public:
|
|
|
119 fsItemFileStd(filesystem::ptr fs, stringRef canonicalPath, t_filestats2 const & opportunistStats) : m_fs(fs), m_path(canonicalPath), m_opportunistStats(opportunistStats) {
|
|
|
120 m_opportunistStats.set_folder(false);
|
|
|
121 m_opportunistStats.set_remote(fs->is_remote(canonicalPath->c_str()));
|
|
|
122 }
|
|
|
123
|
|
|
124 filesystem::ptr getFS() override { return m_fs; }
|
|
|
125
|
|
|
126 bool isRemote() override { return m_fs->is_remote(m_path->c_str()); }
|
|
|
127
|
|
|
128 t_filestats2 getStats2(uint32_t s2flags, abort_callback& a) override {
|
|
|
129 auto s = m_fs->get_stats2_(m_path->c_str(), s2flags, a);
|
|
|
130 PFC_ASSERT((s2flags & stats2_fileOrFolder) == 0 || !s.is_folder());
|
|
|
131 return s;
|
|
|
132 }
|
|
|
133
|
|
|
134 fb2k::stringRef canonicalPath() override { return m_path; }
|
|
|
135
|
|
|
136 fb2k::stringRef nameWithExt() override {
|
|
|
137 pfc::string8 temp;
|
|
|
138 m_fs->extract_filename_ext(m_path->c_str(), temp);
|
|
|
139 return makeString(temp);
|
|
|
140 }
|
|
|
141 fb2k::stringRef shortName() override {
|
|
|
142 pfc::string8 temp;
|
|
|
143 if (m_fs->get_display_name_short_(m_path->c_str(), temp)) return makeString(temp);
|
|
|
144 return nameWithExt();
|
|
|
145 }
|
|
|
146 t_filestats2 getStatsOpportunist() override {
|
|
|
147 return m_opportunistStats;
|
|
|
148 }
|
|
|
149 file::ptr open(uint32_t openMode, abort_callback& aborter) override {
|
|
|
150 file::ptr ret;
|
|
|
151 m_fs->open(ret, m_path->c_str(), openMode, aborter);
|
|
|
152 return ret;
|
|
|
153 }
|
|
|
154 fb2k::memBlockRef readWhole(size_t sizeSanity, abort_callback& aborter) override {
|
|
|
155 // Prefer fs->readWholeFile over fsItemFile methods as the fs object might implement it
|
|
|
156 return m_fs->readWholeFile(m_path->c_str(), sizeSanity, aborter);
|
|
|
157 }
|
|
|
158 fsItemPtr moveTo(fsItemFolderPtr folder, const char* desiredName, unsigned createMode, abort_callback& aborter) override {
|
|
|
159 for (unsigned add = 0; ; ++add) {
|
|
|
160 pfc::string8 fn(desiredName); uniqueFn(fn, add);
|
|
|
161 pfc::string8 dst(folder->canonicalPath()->c_str());
|
|
|
162 dst.end_with(m_fs->pathSeparator());
|
|
|
163 dst += fn;
|
|
|
164 bool bDidExist = false;
|
|
|
165 try {
|
|
|
166 if (createMode == createMode::allowExisting) m_fs->move_overwrite(m_path->c_str(), dst, aborter);
|
|
|
167 else m_fs->move(m_path->c_str(), dst, aborter);
|
|
|
168 } catch (exception_io_already_exists const &) {
|
|
|
169 bDidExist = true;
|
|
|
170 }
|
|
|
171
|
|
|
172 switch (createMode) {
|
|
|
173 case createMode::allowExisting:
|
|
|
174 break; // OK
|
|
|
175 case createMode::failIfExists:
|
|
|
176 if (bDidExist) throw exception_io_already_exists();
|
|
|
177 break; // OK
|
|
|
178 case createMode::generateUniqueName:
|
|
|
179 if (bDidExist) {
|
|
|
180 continue; // the for loop
|
|
|
181 }
|
|
|
182 break; // OK
|
|
|
183 default:
|
|
|
184 PFC_ASSERT(!"Should not get here");
|
|
|
185 break;
|
|
|
186 }
|
|
|
187 auto stats = m_opportunistStats;
|
|
|
188 stats.set_file();
|
|
|
189 return m_fs->makeItemFileStd(dst, stats);
|
|
|
190 }
|
|
|
191 }
|
|
|
192 private:
|
|
|
193 const filesystem::ptr m_fs;
|
|
|
194 const stringRef m_path;
|
|
|
195 t_filestats2 m_opportunistStats;
|
|
|
196 };
|
|
|
197 class fsItemFolderStd : public fsItemFolder {
|
|
|
198 public:
|
|
|
199 fsItemFolderStd(filesystem::ptr fs, stringRef canonicalPath, t_filestats2 const & opportunistStats) : m_fs(fs), m_path(canonicalPath), m_opportunistStats(opportunistStats) {
|
|
|
200 m_opportunistStats.set_folder(true);
|
|
|
201 m_opportunistStats.set_remote(fs->is_remote(canonicalPath->c_str()));
|
|
|
202 }
|
|
|
203
|
|
|
204 filesystem::ptr getFS() override { return m_fs; }
|
|
|
205
|
|
|
206 t_filestats2 getStatsOpportunist() override {
|
|
|
207 return m_opportunistStats;
|
|
|
208 }
|
|
|
209
|
|
|
210 bool isRemote() override { return m_fs->is_remote(m_path->c_str()); }
|
|
|
211
|
|
|
212 t_filestats2 getStats2(uint32_t s2flags, abort_callback& a) override {
|
|
|
213 auto s = m_fs->get_stats2_(m_path->c_str(), s2flags, a);
|
|
|
214 PFC_ASSERT((s2flags & stats2_fileOrFolder) == 0 || s.is_folder() );
|
|
|
215 return s;
|
|
|
216 }
|
|
|
217
|
|
|
218 fb2k::stringRef canonicalPath() override { return m_path; }
|
|
|
219
|
|
|
220 fb2k::stringRef nameWithExt() override {
|
|
|
221 pfc::string8 temp;
|
|
|
222 m_fs->extract_filename_ext(m_path->c_str(), temp);
|
|
|
223 return makeString(temp);
|
|
|
224 }
|
|
|
225 fb2k::stringRef shortName() override {
|
|
|
226 pfc::string8 temp;
|
|
|
227 if (m_fs->get_display_name_short_(m_path->c_str(), temp)) return makeString(temp);
|
|
|
228 return nameWithExt();
|
|
|
229 }
|
|
|
230 fb2k::arrayRef listContents(unsigned listMode, abort_callback& aborter) override {
|
|
|
231 auto out = arrayMutable::empty();
|
|
|
232 filesystem::list_callback_t cb = [&] ( const char * p, t_filestats2 const & stats ) {
|
|
|
233 if (stats.is_folder()) {
|
|
|
234 if (listMode & listMode::folders) out->add(new service_impl_t< fsItemFolderStd >(m_fs, makeString(p), stats));
|
|
|
235 } else {
|
|
|
236 if (listMode & listMode::files) out->add(new service_impl_t< fsItemFileStd >(m_fs, makeString(p), stats));
|
|
|
237 }
|
|
|
238 };
|
|
|
239 m_fs->list_directory_(m_path->c_str(), cb, listMode, aborter);
|
|
|
240 return out->copyConst();
|
|
|
241 }
|
|
|
242
|
|
|
243 fsItemFile::ptr findChildFile(const char* fileName, abort_callback& aborter) override {
|
|
|
244 auto sub = subPath(fileName);
|
|
|
245 auto stats = m_fs->get_stats2_(sub->c_str(), stats2_fileOrFolder | foobar2000_io::stats2_remote, aborter);
|
|
|
246 if (!stats.is_folder()) {
|
|
|
247 return m_fs->makeItemFileStd(sub->c_str(), stats);
|
|
|
248 }
|
|
|
249 throw exception_io_not_found();
|
|
|
250 }
|
|
|
251 fsItemFolder::ptr findChildFolder(const char* fileName, abort_callback& aborter) override {
|
|
|
252 auto sub = subPath(fileName);
|
|
|
253 auto stats = m_fs->get_stats2_(sub->c_str(), stats2_fileOrFolder | foobar2000_io::stats2_remote, aborter);
|
|
|
254 if (stats.is_folder()) {
|
|
|
255 return m_fs->makeItemFolderStd(sub->c_str(), stats);
|
|
|
256 }
|
|
|
257 throw exception_io_not_found();
|
|
|
258 }
|
|
|
259 fsItemBase::ptr findChild(const char* fileName, abort_callback& aborter) override {
|
|
|
260 auto sub = subPath(fileName);
|
|
|
261 auto stats = m_fs->get_stats2_(sub->c_str(), stats2_fileOrFolder | foobar2000_io::stats2_remote, aborter);
|
|
|
262 if ( stats.is_folder() ) {
|
|
|
263 return m_fs->makeItemFileStd(sub->c_str(), stats );
|
|
|
264 } else {
|
|
|
265 return m_fs->makeItemFolderStd(sub->c_str(), stats );
|
|
|
266 }
|
|
|
267 }
|
|
|
268 fsItemFile::ptr createFile(const char* fileName, unsigned createMode, abort_callback& aborter) override {
|
|
|
269 for (unsigned add = 0; ; ++add) {
|
|
|
270 pfc::string8 fn(fileName); uniqueFn(fn, add);
|
|
|
271 auto sub = subPath(fn);
|
|
|
272
|
|
|
273 t_filestats2 stats;
|
|
|
274
|
|
|
275 bool bDidExist = false;
|
|
|
276 try {
|
|
|
277 stats = m_fs->get_stats2_( sub->c_str(), stats2_all, aborter );
|
|
|
278 bDidExist = stats.is_file();
|
|
|
279 } catch(exception_io_not_found const &) {}
|
|
|
280 switch (createMode) {
|
|
|
281 case createMode::allowExisting:
|
|
|
282 break; // OK
|
|
|
283 case createMode::failIfExists:
|
|
|
284 if (bDidExist) throw exception_io_already_exists();
|
|
|
285 break; // OK
|
|
|
286 case createMode::generateUniqueName:
|
|
|
287 if (bDidExist) {
|
|
|
288 continue; // the for loop
|
|
|
289 }
|
|
|
290 break;
|
|
|
291 default:
|
|
|
292 PFC_ASSERT(!"Should not get here");
|
|
|
293 break;
|
|
|
294 }
|
|
|
295 if (!bDidExist) {
|
|
|
296 // actually create an empty file if it did not yet exist
|
|
|
297 // FIX ME this should be atomic with exists() check
|
|
|
298 file::ptr creator;
|
|
|
299 m_fs->open(creator, sub->c_str(), filesystem::open_mode_write_new, aborter);
|
|
|
300 stats = creator->get_stats2_( stats2_all, aborter );
|
|
|
301 }
|
|
|
302 return m_fs->makeItemFileStd(sub->c_str(), stats);
|
|
|
303 }
|
|
|
304 }
|
|
|
305 fsItemFolder::ptr createFolder(const char* fileName, unsigned createMode, abort_callback& aborter) override {
|
|
|
306 for (unsigned add = 0; ; ++add) {
|
|
|
307 pfc::string8 fn(fileName); uniqueFn(fn, add);
|
|
|
308 auto sub = subPath(fn);
|
|
|
309 bool bDidExist = false;
|
|
|
310 try {
|
|
|
311 m_fs->create_directory(sub->c_str(), aborter);
|
|
|
312 } catch (exception_io_already_exists const &) {
|
|
|
313 bDidExist = true;
|
|
|
314 }
|
|
|
315 switch (createMode) {
|
|
|
316 case createMode::allowExisting:
|
|
|
317 break; // OK
|
|
|
318 case createMode::failIfExists:
|
|
|
319 if (bDidExist) throw exception_io_already_exists();
|
|
|
320 break;
|
|
|
321 case createMode::generateUniqueName:
|
|
|
322 if (bDidExist) {
|
|
|
323 continue; // the for loop
|
|
|
324 }
|
|
|
325 break;
|
|
|
326 default:
|
|
|
327 PFC_ASSERT(!"Should not get here");
|
|
|
328 break;
|
|
|
329 }
|
|
|
330 // Inherit opportunist stats
|
|
|
331 return m_fs->makeItemFolderStd(sub->c_str(), this->m_opportunistStats);
|
|
|
332 }
|
|
|
333 }
|
|
|
334 fsItemPtr moveTo(fsItemFolderPtr folder, const char* desiredName, unsigned createMode, abort_callback& aborter) override {
|
|
|
335 for (unsigned add = 0; ; ++add) {
|
|
|
336 pfc::string8 fn(desiredName); uniqueFn(fn, add);
|
|
|
337 pfc::string8 dst(folder->canonicalPath()->c_str());
|
|
|
338 dst.end_with(m_fs->pathSeparator());
|
|
|
339 dst += fn;
|
|
|
340 bool bDidExist = false;
|
|
|
341 try {
|
|
|
342 if (createMode == createMode::allowExisting) m_fs->move_overwrite(m_path->c_str(), dst, aborter);
|
|
|
343 else m_fs->move(m_path->c_str(), dst, aborter);
|
|
|
344 } catch (exception_io_already_exists const &) {
|
|
|
345 bDidExist = true;
|
|
|
346 }
|
|
|
347
|
|
|
348 switch (createMode) {
|
|
|
349 case createMode::allowExisting:
|
|
|
350 break; // OK
|
|
|
351 case createMode::failIfExists:
|
|
|
352 if (bDidExist) throw exception_io_already_exists();
|
|
|
353 break; // OK
|
|
|
354 case createMode::generateUniqueName:
|
|
|
355 if (bDidExist) {
|
|
|
356 continue; // the for loop
|
|
|
357 }
|
|
|
358 break; // OK
|
|
|
359 default:
|
|
|
360 PFC_ASSERT(!"Should not get here");
|
|
|
361 break;
|
|
|
362 }
|
|
|
363
|
|
|
364 return m_fs->makeItemFolderStd(dst, m_opportunistStats);
|
|
|
365
|
|
|
366 }
|
|
|
367 }
|
|
|
368 private:
|
|
|
369 stringRef subPath(const char* name) {
|
|
|
370 pfc::string8 temp(m_path->c_str());
|
|
|
371 temp.add_filename(name);
|
|
|
372 return makeString(temp);
|
|
|
373 }
|
|
|
374 const filesystem::ptr m_fs;
|
|
|
375 const stringRef m_path;
|
|
|
376 t_filestats2 m_opportunistStats;
|
|
|
377 };
|
|
|
378 }
|
|
|
379
|
|
|
380 fsItemFolder::ptr filesystem::makeItemFolderStd(const char* pathCanonical, t_filestats2 const& opportunistStats) {
|
|
|
381 return new service_impl_t<fsItemFolderStd>(this, makeString(pathCanonical), opportunistStats);
|
|
|
382 }
|
|
|
383
|
|
|
384 fsItemFile::ptr filesystem::makeItemFileStd(const char* pathCanonical, t_filestats2 const & opportunistStats) {
|
|
|
385 return new service_impl_t<fsItemFileStd>(this, makeString(pathCanonical), opportunistStats);
|
|
|
386 }
|
|
|
387 fsItemBase::ptr filesystem::findItem_(const char* path, abort_callback& p_abort) {
|
|
|
388 filesystem_v3::ptr v3;
|
|
|
389 if (v3 &= this) {
|
|
|
390 return v3->findItem(path, p_abort);
|
|
|
391 }
|
|
|
392 auto stats = this->get_stats2_(path, stats2_fileOrFolder, p_abort);
|
|
|
393 if (stats.is_folder()) return this->makeItemFolderStd(path, stats);
|
|
|
394 else return this->makeItemFileStd(path, stats);
|
|
|
395
|
|
|
396 }
|
|
|
397 fsItemFile::ptr filesystem::findItemFile_(const char* path, abort_callback& p_abort) {
|
|
|
398 filesystem_v3::ptr v3;
|
|
|
399 if (v3 &= this) return v3->findItemFile(path, p_abort);
|
|
|
400
|
|
|
401 auto stats = this->get_stats2_(path, stats2_fileOrFolder, p_abort);
|
|
|
402 if (!stats.is_folder()) return this->makeItemFileStd(path, stats);
|
|
|
403 throw exception_io_not_found();
|
|
|
404 }
|
|
|
405 fsItemFolder::ptr filesystem::findItemFolder_(const char* path, abort_callback& p_abort) {
|
|
|
406 filesystem_v3::ptr v3;
|
|
|
407 if (v3 &= this) return v3->findItemFolder(path, p_abort);
|
|
|
408
|
|
|
409 auto stats = this->get_stats2_(path, stats2_fileOrFolder, p_abort);
|
|
|
410 if (stats.is_folder()) return this->makeItemFolderStd(path, stats);
|
|
|
411 throw exception_io_not_found();
|
|
|
412 }
|
|
|
413
|
|
|
414
|
|
|
415 fsItemBase::ptr fsItemBase::fromPath(const char* path, abort_callback& aborter) {
|
|
|
416 return filesystem::get(path)->findItem_(path, aborter);
|
|
|
417 }
|
|
|
418
|
|
|
419 fsItemFile::ptr fsItemFile::fromPath(const char* path, abort_callback& aborter) {
|
|
|
420 return filesystem::get(path)->findItemFile_(path, aborter);
|
|
|
421 }
|
|
|
422
|
|
|
423 fsItemFolder::ptr fsItemFolder::fromPath(const char* path, abort_callback& aborter) {
|
|
|
424 return filesystem::get(path)->findItemFolder_(path, aborter);
|
|
|
425 }
|
|
|
426
|
|
|
427 t_filestats fsItemBase::getStats(abort_callback& a) {
|
|
|
428 return getStats2(stats2_all, a).to_legacy();
|
|
|
429 }
|