Mercurial > minori
comparison src/core/filesystem.cc @ 413:192da585a0a8 default tip
*: fix formatting
the clang-format file was broken andjust using LLVM defaults.
now it's fixed and is more in line with what I actually prefer my
code to look like -- esp. with regards to tabulators
| author | Paper <paper@tflc.us> |
|---|---|
| date | Sat, 25 Jul 2026 14:54:32 -0400 |
| parents | 8d06825d96d1 |
| children |
comparison
equal
deleted
inserted
replaced
| 412:05aed03e0111 | 413:192da585a0a8 |
|---|---|
| 10 #ifdef WIN32 | 10 #ifdef WIN32 |
| 11 # include <windows.h> | 11 # include <windows.h> |
| 12 #elif defined(HAVE_INOTIFY) | 12 #elif defined(HAVE_INOTIFY) |
| 13 /* ehhhh */ | 13 /* ehhhh */ |
| 14 # include <fcntl.h> | 14 # include <fcntl.h> |
| 15 # include <sys/inotify.h> | |
| 15 # include <unistd.h> | 16 # include <unistd.h> |
| 16 # include <sys/inotify.h> | |
| 17 #elif defined(__MACH__) && defined(__APPLE__) | 17 #elif defined(__MACH__) && defined(__APPLE__) |
| 18 # include <CoreFoundation/CoreFoundation.h> | 18 # include <CoreFoundation/CoreFoundation.h> |
| 19 # include <CoreServices/CoreServices.h> | 19 # include <CoreServices/CoreServices.h> |
| 20 # include <mutex> | 20 # include <mutex> |
| 21 # include <thread> | 21 # include <thread> |
| 78 /* This is the "base class" for basically every watcher. */ | 78 /* This is the "base class" for basically every watcher. */ |
| 79 | 79 |
| 80 class Watcher : public IWatcher { | 80 class Watcher : public IWatcher { |
| 81 public: | 81 public: |
| 82 Watcher(void *opaque, const std::filesystem::path &path, EventHandler handler) | 82 Watcher(void *opaque, const std::filesystem::path &path, EventHandler handler) |
| 83 : path_(path), handler_(handler), opaque_(opaque) | 83 : path_(path), handler_(handler), opaque_(opaque) |
| 84 { | 84 { |
| 85 } | 85 } |
| 86 | 86 |
| 87 virtual ~Watcher() override {} | 87 virtual ~Watcher() override {} |
| 88 | 88 |
| 105 completely stops. I am dumbfounded at this behavior, but | 105 completely stops. I am dumbfounded at this behavior, but |
| 106 nevertheless it means we simply can't use it, and must | 106 nevertheless it means we simply can't use it, and must |
| 107 resort to old-fashioned recursion. --paper | 107 resort to old-fashioned recursion. --paper |
| 108 */ | 108 */ |
| 109 static void IterateDirectory(const std::filesystem::path &path, bool recursive, | 109 static void IterateDirectory(const std::filesystem::path &path, bool recursive, |
| 110 const std::function<void(const std::filesystem::path &path)> &func) | 110 const std::function<void(const std::filesystem::path &path)> &func) |
| 111 { | 111 { |
| 112 std::error_code ec; | 112 std::error_code ec; |
| 113 static const std::filesystem::directory_iterator end; | 113 static const std::filesystem::directory_iterator end; |
| 114 | 114 |
| 115 for (std::filesystem::directory_iterator item(path, ec); item != end; item.increment(ec)) { | 115 for (std::filesystem::directory_iterator item(path, ec); item != end; item.increment(ec)) { |
| 128 } | 128 } |
| 129 | 129 |
| 130 class StdFilesystemWatcher : public Watcher { | 130 class StdFilesystemWatcher : public Watcher { |
| 131 public: | 131 public: |
| 132 StdFilesystemWatcher(void *opaque, const std::filesystem::path &path, EventHandler handler, bool recursive) | 132 StdFilesystemWatcher(void *opaque, const std::filesystem::path &path, EventHandler handler, bool recursive) |
| 133 : Watcher(opaque, path, handler), recursive_(recursive) | 133 : Watcher(opaque, path, handler), recursive_(recursive) |
| 134 { | 134 { |
| 135 } | 135 } |
| 136 | 136 |
| 137 virtual ~StdFilesystemWatcher() override {} | 137 virtual ~StdFilesystemWatcher() override {} |
| 138 | 138 |
| 203 /* On Windows, we can ask the OS whether the directory tree has changed or not. | 203 /* On Windows, we can ask the OS whether the directory tree has changed or not. |
| 204 * This is great for us! */ | 204 * This is great for us! */ |
| 205 class Win32Watcher : public StdFilesystemWatcher { | 205 class Win32Watcher : public StdFilesystemWatcher { |
| 206 public: | 206 public: |
| 207 Win32Watcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) | 207 Win32Watcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) |
| 208 : StdFilesystemWatcher(opaque, path, handler, recursive), dirwatcher_(INVALID_HANDLE_VALUE), first_(true) | 208 : StdFilesystemWatcher(opaque, path, handler, recursive), dirwatcher_(INVALID_HANDLE_VALUE), first_(true) |
| 209 { | 209 { |
| 210 } | 210 } |
| 211 | 211 |
| 212 virtual ~Win32Watcher() override | 212 virtual ~Win32Watcher() override |
| 213 { | 213 { |
| 237 } | 237 } |
| 238 | 238 |
| 239 protected: | 239 protected: |
| 240 bool TryCreateDirWatcher() | 240 bool TryCreateDirWatcher() |
| 241 { | 241 { |
| 242 dirwatcher_ = FindFirstChangeNotificationW(Watcher::path_.wstring().c_str(), recursive_, | 242 dirwatcher_ = FindFirstChangeNotificationW( |
| 243 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME); | 243 Watcher::path_.wstring().c_str(), recursive_, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME); |
| 244 | 244 |
| 245 return (dirwatcher_ != INVALID_HANDLE_VALUE); | 245 return (dirwatcher_ != INVALID_HANDLE_VALUE); |
| 246 } | 246 } |
| 247 | 247 |
| 248 /* variables */ | 248 /* variables */ |
| 256 * Anyway, this is a version that doesn't need to keep the whole directory | 256 * Anyway, this is a version that doesn't need to keep the whole directory |
| 257 * tree in-memory if it is available. */ | 257 * tree in-memory if it is available. */ |
| 258 class Win32WatcherVista : public Win32Watcher { | 258 class Win32WatcherVista : public Win32Watcher { |
| 259 public: | 259 public: |
| 260 Win32WatcherVista(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) | 260 Win32WatcherVista(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) |
| 261 : Win32Watcher(opaque, path, handler, recursive), dirhandle_(INVALID_HANDLE_VALUE) | 261 : Win32Watcher(opaque, path, handler, recursive), dirhandle_(INVALID_HANDLE_VALUE) |
| 262 { | 262 { |
| 263 ZeroMemory(&overlapped_, sizeof(overlapped_)); | 263 ZeroMemory(&overlapped_, sizeof(overlapped_)); |
| 264 overlapped_.hEvent = nullptr; | 264 overlapped_.hEvent = nullptr; |
| 265 } | 265 } |
| 266 | 266 |
| 283 | 283 |
| 284 /* Avoid running Win32Watcher::Process, as it will read the whole | 284 /* Avoid running Win32Watcher::Process, as it will read the whole |
| 285 * directory tree into memory. Instead, iterate through the directory | 285 * directory tree into memory. Instead, iterate through the directory |
| 286 * ourselves. */ | 286 * ourselves. */ |
| 287 IterateDirectory(path_, recursive_, | 287 IterateDirectory(path_, recursive_, |
| 288 [this](const std::filesystem::path &p) { handler_(opaque_, p, Event::Created); }); | 288 [this](const std::filesystem::path &p) { handler_(opaque_, p, Event::Created); }); |
| 289 } else { | 289 } else { |
| 290 /* Uh oh; we might have to fall back to Win32Watcher. Call into it to | 290 /* Uh oh; we might have to fall back to Win32Watcher. Call into it to |
| 291 * load the tree into memory. */ | 291 * load the tree into memory. */ |
| 292 Win32Watcher::Process(); | 292 Win32Watcher::Process(); |
| 293 } | 293 } |
| 351 return false; | 351 return false; |
| 352 } | 352 } |
| 353 | 353 |
| 354 if (dirhandle_ == INVALID_HANDLE_VALUE) { | 354 if (dirhandle_ == INVALID_HANDLE_VALUE) { |
| 355 dirhandle_ = | 355 dirhandle_ = |
| 356 CreateFileW(Watcher::path_.wstring().c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, | 356 CreateFileW(Watcher::path_.wstring().c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 357 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); | 357 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); |
| 358 if (dirhandle_ == INVALID_HANDLE_VALUE) | 358 if (dirhandle_ == INVALID_HANDLE_VALUE) |
| 359 return false; | 359 return false; |
| 360 } | 360 } |
| 361 | 361 |
| 362 /* We're done here */ | 362 /* We're done here */ |
| 364 } | 364 } |
| 365 | 365 |
| 366 bool QueueDirectoryRead() | 366 bool QueueDirectoryRead() |
| 367 { | 367 { |
| 368 return ReadDirectoryChangesW(dirhandle_, change_buf_, sizeof(change_buf_), TRUE, | 368 return ReadDirectoryChangesW(dirhandle_, change_buf_, sizeof(change_buf_), TRUE, |
| 369 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, nullptr, &overlapped_, | 369 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, nullptr, &overlapped_, nullptr); |
| 370 nullptr); | |
| 371 } | 370 } |
| 372 | 371 |
| 373 HANDLE dirhandle_; | 372 HANDLE dirhandle_; |
| 374 OVERLAPPED overlapped_; | 373 OVERLAPPED overlapped_; |
| 375 alignas(FILE_NOTIFY_INFORMATION) char change_buf_[4096]; | 374 alignas(FILE_NOTIFY_INFORMATION) char change_buf_[4096]; |
| 379 #elif defined(HAVE_INOTIFY) | 378 #elif defined(HAVE_INOTIFY) |
| 380 /* Inotify watcher */ | 379 /* Inotify watcher */ |
| 381 class InotifyWatcher : public StdFilesystemWatcher { | 380 class InotifyWatcher : public StdFilesystemWatcher { |
| 382 public: | 381 public: |
| 383 InotifyWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) | 382 InotifyWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) |
| 384 : StdFilesystemWatcher(opaque, path, handler, recursive) | 383 : StdFilesystemWatcher(opaque, path, handler, recursive), first_(true), giveup_(false), ev_(nullptr), |
| 385 , first_(true) | 384 ev_size_(0) |
| 386 , giveup_(false) | |
| 387 , ev_(nullptr) | |
| 388 , ev_size_(0) | |
| 389 { | 385 { |
| 390 } | 386 } |
| 391 | 387 |
| 392 virtual ~InotifyWatcher() override | 388 virtual ~InotifyWatcher() override |
| 393 { | 389 { |
| 465 /* Watched directory has been deleted */ | 461 /* Watched directory has been deleted */ |
| 466 RemoveWatchDescriptor(ev->wd); | 462 RemoveWatchDescriptor(ev->wd); |
| 467 continue; | 463 continue; |
| 468 } | 464 } |
| 469 | 465 |
| 470 if (ev->mask & (IN_MOVE|IN_CREATE|IN_DELETE)) { | 466 if (ev->mask & (IN_MOVE | IN_CREATE | IN_DELETE)) { |
| 471 std::filesystem::path p = wds_[ev->wd] / ev->name; | 467 std::filesystem::path p = wds_[ev->wd] / ev->name; |
| 472 | 468 |
| 473 if (ev->mask & (IN_MOVED_TO|IN_CREATE)) { | 469 if (ev->mask & (IN_MOVED_TO | IN_CREATE)) { |
| 474 if (std::filesystem::is_directory(p)) | 470 if (std::filesystem::is_directory(p)) |
| 475 AddWatchDescriptor(p); | 471 AddWatchDescriptor(p); |
| 476 handler_(opaque_, p, Event::Created); | 472 handler_(opaque_, p, Event::Created); |
| 477 } else if (ev->mask & (IN_MOVED_FROM|IN_DELETE)) { | 473 } else if (ev->mask & (IN_MOVED_FROM | IN_DELETE)) { |
| 478 | 474 |
| 479 handler_(opaque_, wds_[ev->wd] / ev->name, Event::Deleted); | 475 handler_(opaque_, wds_[ev->wd] / ev->name, Event::Deleted); |
| 480 } | 476 } |
| 481 } | 477 } |
| 482 | 478 |
| 488 protected: | 484 protected: |
| 489 /* File descriptor helper. Mostly follows std::unique_ptr, | 485 /* File descriptor helper. Mostly follows std::unique_ptr, |
| 490 * but has a function for toggling non-blocking */ | 486 * but has a function for toggling non-blocking */ |
| 491 struct FileDescriptor { | 487 struct FileDescriptor { |
| 492 public: | 488 public: |
| 493 FileDescriptor() : fd_(-1) { } | 489 FileDescriptor() : fd_(-1) {} |
| 494 ~FileDescriptor() { reset(); } | 490 ~FileDescriptor() { reset(); } |
| 495 | 491 |
| 496 int get() { return fd_; } | 492 int get() { return fd_; } |
| 497 operator bool() { return (fd_ != -1); } | 493 operator bool() { return (fd_ != -1); } |
| 498 void reset(int fd = -1) | 494 void reset(int fd = -1) |
| 534 { | 530 { |
| 535 if (giveup_) | 531 if (giveup_) |
| 536 return false; | 532 return false; |
| 537 | 533 |
| 538 if (!fd_) { | 534 if (!fd_) { |
| 539 #ifdef HAVE_INOTIFY_INIT1 | 535 # ifdef HAVE_INOTIFY_INIT1 |
| 540 fd_.reset(inotify_init1(IN_NONBLOCK)); | 536 fd_.reset(inotify_init1(IN_NONBLOCK)); |
| 541 #else | 537 # else |
| 542 fd_.reset(inotify_init()); | 538 fd_.reset(inotify_init()); |
| 543 #endif | 539 # endif |
| 544 if (!fd_) | 540 if (!fd_) |
| 545 return false; | 541 return false; |
| 546 | 542 |
| 547 #ifndef HAVE_INOTIFY_INIT1 | 543 # ifndef HAVE_INOTIFY_INIT1 |
| 548 /* Very old linux */ | 544 /* Very old linux */ |
| 549 if (!fd_.SetNonBlocking(true)) | 545 if (!fd_.SetNonBlocking(true)) |
| 550 return false; | 546 return false; |
| 551 #endif | 547 # endif |
| 552 } | 548 } |
| 553 | 549 |
| 554 return !!fd_; | 550 return !!fd_; |
| 555 } | 551 } |
| 556 | 552 |
| 557 bool AddWatchDescriptor(const std::filesystem::path &p) | 553 bool AddWatchDescriptor(const std::filesystem::path &p) |
| 558 { | 554 { |
| 559 if (!fd_ || giveup_) | 555 if (!fd_ || giveup_) |
| 560 return false; | 556 return false; |
| 561 | 557 |
| 562 int wd = inotify_add_watch(fd_.get(), p.string().c_str(), IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE_SELF); | 558 int wd = inotify_add_watch( |
| 559 fd_.get(), p.string().c_str(), IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO | IN_DELETE_SELF); | |
| 563 if (wd < 0) { | 560 if (wd < 0) { |
| 564 /* Don't even try to watch any more */ | 561 /* Don't even try to watch any more */ |
| 565 giveup_ = true; | 562 giveup_ = true; |
| 566 return false; | 563 return false; |
| 567 } | 564 } |
| 594 using DefaultWatcher = InotifyWatcher; | 591 using DefaultWatcher = InotifyWatcher; |
| 595 #elif defined(__APPLE__) && defined(__MACH__) | 592 #elif defined(__APPLE__) && defined(__MACH__) |
| 596 class FSEventsWatcher : public StdFilesystemWatcher { | 593 class FSEventsWatcher : public StdFilesystemWatcher { |
| 597 public: | 594 public: |
| 598 FSEventsWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) | 595 FSEventsWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) |
| 599 : StdFilesystemWatcher(opaque, path, handler, recursive) | 596 : StdFilesystemWatcher(opaque, path, handler, recursive), first_(true) |
| 600 , first_(true) | |
| 601 { | 597 { |
| 602 FSEventStreamContext ctx; | 598 FSEventStreamContext ctx; |
| 603 | 599 |
| 604 ctx.copyDescription = NULL; | 600 ctx.copyDescription = NULL; |
| 605 ctx.info = this; | 601 ctx.info = this; |
| 606 ctx.release = NULL; | 602 ctx.release = NULL; |
| 607 ctx.retain = NULL; | 603 ctx.retain = NULL; |
| 608 ctx.version = 0; | 604 ctx.version = 0; |
| 609 | 605 |
| 610 CFStringRef str = Strings::ToCFString(path.u8string()); | 606 CFStringRef str = Strings::ToCFString(path.u8string()); |
| 611 CFArrayRef arr = CFArrayCreate(kCFAllocatorDefault, reinterpret_cast<const void **>(&str), 1, &kCFTypeArrayCallBacks); | 607 CFArrayRef arr = |
| 612 | 608 CFArrayCreate(kCFAllocatorDefault, reinterpret_cast<const void **>(&str), 1, &kCFTypeArrayCallBacks); |
| 613 stream_ = FSEventStreamCreate(kCFAllocatorDefault, callback_static, &ctx, arr, kFSEventStreamEventIdSinceNow, 0.5, 0); | 609 |
| 610 stream_ = | |
| 611 FSEventStreamCreate(kCFAllocatorDefault, callback_static, &ctx, arr, kFSEventStreamEventIdSinceNow, 0.5, 0); | |
| 614 | 612 |
| 615 // kill these off now | 613 // kill these off now |
| 616 CFRelease(str); | 614 CFRelease(str); |
| 617 CFRelease(arr); | 615 CFRelease(arr); |
| 618 | 616 |
| 650 Watcher::handler_(Watcher::opaque_, ev.path, ev.type); | 648 Watcher::handler_(Watcher::opaque_, ev.path, ev.type); |
| 651 events_.clear(); | 649 events_.clear(); |
| 652 } | 650 } |
| 653 | 651 |
| 654 private: | 652 private: |
| 655 void callback(ConstFSEventStreamRef streamRef, std::size_t numEvents, void *eventPaths, const FSEventStreamEventFlags *eventFlags, const FSEventStreamEventId *eventIds) | 653 void callback(ConstFSEventStreamRef streamRef, std::size_t numEvents, void *eventPaths, |
| 654 const FSEventStreamEventFlags *eventFlags, const FSEventStreamEventId *eventIds) | |
| 656 { | 655 { |
| 657 // assert(streamRef == stream_); | 656 // assert(streamRef == stream_); |
| 658 | 657 |
| 659 #if 0 | 658 # if 0 |
| 660 for (std::size_t i = 0; i < numEvents; i++) { | 659 for (std::size_t i = 0; i < numEvents; i++) { |
| 661 if ((eventFlags[i] == 0) || (eventFlags[i] & (kFSEventStreamEventFlagItemCreated|kFSEventStreamEventFlagItemRemoved|kFSEventStreamEventFlagMustScanSubDirs))) { | 660 if ((eventFlags[i] == 0) || (eventFlags[i] & (kFSEventStreamEventFlagItemCreated|kFSEventStreamEventFlagItemRemoved|kFSEventStreamEventFlagMustScanSubDirs))) { |
| 662 EventInfo ev; | 661 EventInfo ev; |
| 663 | 662 |
| 664 ev.path = std::filesystem::path(reinterpret_cast<const char **>(eventPaths)[i]); | 663 ev.path = std::filesystem::path(reinterpret_cast<const char **>(eventPaths)[i]); |
| 677 ev.type = IWatcher::Deleted; | 676 ev.type = IWatcher::Deleted; |
| 678 events_.push_back(ev); | 677 events_.push_back(ev); |
| 679 } | 678 } |
| 680 } | 679 } |
| 681 } | 680 } |
| 682 #else | 681 # else |
| 683 /* I only evr get eventFlags[i] == 0 so I think this is | 682 /* I only evr get eventFlags[i] == 0 so I think this is |
| 684 * probably the best way ??? */ | 683 * probably the best way ??? */ |
| 685 rescan_.push_back(Watcher::path_); | 684 rescan_.push_back(Watcher::path_); |
| 686 #endif | 685 # endif |
| 687 } | 686 } |
| 688 | 687 |
| 689 static void callback_static(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, std::size_t numEvents, void *eventPaths, const FSEventStreamEventFlags *eventFlags, const FSEventStreamEventId *eventIds) | 688 static void callback_static(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, std::size_t numEvents, |
| 690 { | 689 void *eventPaths, const FSEventStreamEventFlags *eventFlags, const FSEventStreamEventId *eventIds) |
| 691 reinterpret_cast<FSEventsWatcher *>(clientCallBackInfo)->callback(streamRef, numEvents, eventPaths, eventFlags, eventIds); | 690 { |
| 691 reinterpret_cast<FSEventsWatcher *>(clientCallBackInfo) | |
| 692 ->callback(streamRef, numEvents, eventPaths, eventFlags, eventIds); | |
| 692 } | 693 } |
| 693 | 694 |
| 694 void runloop_thread() | 695 void runloop_thread() |
| 695 { | 696 { |
| 696 { | 697 { |
| 705 // now.. loop was stopped from destructor. | 706 // now.. loop was stopped from destructor. |
| 706 // we don't need to do anything; destructor | 707 // we don't need to do anything; destructor |
| 707 // will handle everything from here | 708 // will handle everything from here |
| 708 } | 709 } |
| 709 | 710 |
| 710 static void runloop_thread_static(FSEventsWatcher *e) | 711 static void runloop_thread_static(FSEventsWatcher *e) { e->runloop_thread(); } |
| 711 { | |
| 712 e->runloop_thread(); | |
| 713 } | |
| 714 | 712 |
| 715 struct EventInfo { | 713 struct EventInfo { |
| 716 enum Event type; | 714 enum Event type; |
| 717 std::filesystem::path path; | 715 std::filesystem::path path; |
| 718 }; | 716 }; |
