comparison foosdk/sdk/foobar2000/SDK/fsItem.cpp @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
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 }