Author: stippi Date: 2010-08-19 21:52:19 +0200 (Thu, 19 Aug 2010) New Revision: 38280 Changeset: http://dev.haiku-os.org/changeset/38280 Modified: haiku/trunk/headers/private/storage/AddOnMonitorHandler.h haiku/trunk/src/kits/storage/AddOnMonitorHandler.cpp Log: * Refactored a lot of repetitive code and made everything more readible. * Once I understood better how everthing is supposed to work, I've fixed some bugs. * The most important problem was that AddDirectory() placed the wrong entry (resolved symlinks) into the entry list it keeps for each directory. This resulted in the mechanisms not working at all when an add-on was a symlink. * There was a hidden TODO, which would mean that moving an add-on from one watched directory into another, like from home/config/add-ons/... into common/add-ons/... would drop the client application (media_server, input_server, ...) into the debugger. * The fFormerEntries list did not seem to serve any purpose. Basically it would not disable add-ons removed from a watched directory unless it changed it's name at the same time. I've removed it completely, since it didn't seem to be an optimization (entry cache) either. * Each actual add-on file is now node-monitored for stat changes. So if you have a link in the add-on folder, and the linked to add-on changes, it triggers a reload of the add-on now. This will make it much more pleasant to develop add-ons and have them affective immediately. I tested with a fresh image, but there are no immediate regressions I am aware of. I could imagine that messing with certain add-ons can have a bad effect now, like removing the keyboard input_server add-on may trigger the keyboard to stop working immediately without an input_server restart, but Tracker should warn before it happens. Modified: haiku/trunk/headers/private/storage/AddOnMonitorHandler.h =================================================================== --- haiku/trunk/headers/private/storage/AddOnMonitorHandler.h 2010-08-19 19:40:15 UTC (rev 38279) +++ haiku/trunk/headers/private/storage/AddOnMonitorHandler.h 2010-08-19 19:52:19 UTC (rev 38280) @@ -1,10 +1,11 @@ /* - * Copyright 2004-2008, Haiku, Inc. All rights reserved. + * Copyright 2004-2010, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _ADD_ON_MONITOR_HANDLER_H #define _ADD_ON_MONITOR_HANDLER_H + #include <list> #include "NodeMonitorHandler.h" @@ -13,55 +14,112 @@ namespace BPrivate { namespace Storage { + struct add_on_entry_info { char name[B_FILE_NAME_LENGTH]; node_ref nref; node_ref dir_nref; + node_ref addon_nref; }; -struct add_on_directory_info { - node_ref nref; - std::list<add_on_entry_info> entries; -}; class AddOnMonitorHandler : public NodeMonitorHandler { public: - AddOnMonitorHandler(const char* name = NULL); - virtual ~AddOnMonitorHandler(); + AddOnMonitorHandler(const char* name = NULL); + virtual ~AddOnMonitorHandler(); - virtual void MessageReceived(BMessage* msg); + virtual void MessageReceived(BMessage* message); - // supply the add-on directories here, in the order you want them checked - virtual status_t AddDirectory(const node_ref* nref); + // Supply the add-on directories here, in the order you want them checked. + // Add-ons in directories added earlier will shadow add-ons in directories + // added later, if they share the same file name. If an add-on is removed + // from or renamed in a directory and it has previously shadowed another + // add-on, the previously shadowed add-on shall become enabled + // (AddOnEnabled()). If an add-on appears in a directory, or is renamed, + // it can cause another add-on to become disabled, if it has the same name. + // Note that directories are not watched recursively, and all entries + // are reported as add-ons regardless of their node type (files, + // directories, symlinks). + virtual status_t AddDirectory(const node_ref* nref); protected: - // hooks for subclass - virtual void AddOnCreated(const add_on_entry_info* entryInfo); - virtual void AddOnEnabled(const add_on_entry_info* entryInfo); - virtual void AddOnDisabled(const add_on_entry_info* entryInfo); - virtual void AddOnRemoved(const add_on_entry_info* entryInfo); + // hooks for sub-class + virtual void AddOnCreated( + const add_on_entry_info* entryInfo); + virtual void AddOnEnabled( + const add_on_entry_info* entryInfo); + virtual void AddOnDisabled( + const add_on_entry_info* entryInfo); + // name field will be invalid! + virtual void AddOnRemoved( + const add_on_entry_info* entryInfo); + // name field will be invalid! protected: - virtual void EntryCreated(const char* name, ino_t directory, - dev_t device, ino_t node); - virtual void EntryRemoved(ino_t directory, dev_t device, ino_t node); - virtual void EntryMoved(const char* name, ino_t fromDirectory, - ino_t toDirectory, dev_t device, ino_t node); + virtual void EntryCreated(const char* name, ino_t directory, + dev_t device, ino_t node); + virtual void EntryRemoved(ino_t directory, dev_t device, + ino_t node); + virtual void EntryMoved(const char* name, + ino_t fromDirectory, ino_t toDirectory, + dev_t device, ino_t node); + virtual void StatChanged(ino_t node, dev_t device); private: - typedef NodeMonitorHandler inherited; + void _HandlePulse(); + void _EntryCreated(add_on_entry_info& info); - void _HandlePulse(); + typedef NodeMonitorHandler inherited; + typedef std::list<add_on_entry_info> EntryList; - std::list<add_on_directory_info> fDirectories; - std::list<add_on_entry_info> fPendingEntries; - std::list<add_on_entry_info> fFormerEntries; + struct add_on_directory_info { + node_ref nref; + EntryList entries; + }; + + typedef std::list<add_on_directory_info> DirectoryList; + + bool _FindEntry(const node_ref& entry, + const EntryList& list, + EntryList::iterator& it) const; + bool _FindEntry(const char* name, + const EntryList& list, + EntryList::iterator& it) const; + + bool _HasEntry(const node_ref& entry, + EntryList& list) const; + bool _HasEntry(const char* name, + EntryList& list) const; + + bool _FindDirectory(ino_t directory, dev_t device, + DirectoryList::iterator& it) const; + bool _FindDirectory( + const node_ref& directoryNodeRef, + DirectoryList::iterator& it) const; + bool _FindDirectory(ino_t directory, dev_t device, + DirectoryList::iterator& it, + const DirectoryList::const_iterator& end) + const; + bool _FindDirectory( + const node_ref& directoryNodeRef, + DirectoryList::iterator& it, + const DirectoryList::const_iterator& end) + const; + + void _AddNewEntry(EntryList& list, + add_on_entry_info& info); + +private: + DirectoryList fDirectories; + EntryList fPendingEntries; }; }; // namespace Storage }; // namespace BPrivate + using namespace BPrivate::Storage; + #endif // _ADD_ON_MONITOR_HANDLER_H Modified: haiku/trunk/src/kits/storage/AddOnMonitorHandler.cpp =================================================================== --- haiku/trunk/src/kits/storage/AddOnMonitorHandler.cpp 2010-08-19 19:40:15 UTC (rev 38279) +++ haiku/trunk/src/kits/storage/AddOnMonitorHandler.cpp 2010-08-19 19:52:19 UTC (rev 38280) @@ -4,6 +4,7 @@ * * Authors: * Andrew Bachmann + * Stephan Aßmus <superstippi@xxxxxx> */ @@ -29,6 +30,16 @@ AddOnMonitorHandler::~AddOnMonitorHandler() { + // TODO: Actually calling watch_node() here should be too late, since we + // are likely not attached to a looper anymore, and thus consitute no valid + // BMessenger at this time. + DirectoryList::iterator it = fDirectories.begin(); + for (; it != fDirectories.end(); it++) { + EntryList::iterator eiter = it->entries.begin(); + for (; eiter != it->entries.end(); eiter++) + watch_node(&eiter->addon_nref, B_STOP_WATCHING, this); + watch_node(&it->nref, B_STOP_WATCHING, this); + } } @@ -47,16 +58,17 @@ { // Keep the looper thread locked, since this method is likely to be called // in a thread other than the looper thread. Otherwise we may access the - // lists concurrently with the looper thread, when node monitor notifications - // arrive while we are still adding initial entries from the directory, or - // (much more likely) if the looper thread handles a pulse message and wants - // to process pending entries while we are still adding them. + // lists concurrently with the looper thread, when node monitor + // notifications arrive while we are still adding initial entries from the + // directory, or (much more likely) if the looper thread handles a pulse + // message and wants to process pending entries while we are still adding + // them. BAutolock _(Looper()); // ignore directories added twice - std::list<add_on_directory_info>::iterator iterator = fDirectories.begin(); - for (; iterator != fDirectories.end() ; iterator++) { - if (iterator->nref == *nref) + DirectoryList::iterator it = fDirectories.begin(); + for (; it != fDirectories.end(); it++) { + if (it->nref == *nref) return B_OK; } @@ -65,25 +77,24 @@ if (status != B_OK) return status; + status = watch_node(nref, B_WATCH_DIRECTORY, this); + if (status != B_OK) + return status; + add_on_directory_info dirInfo; dirInfo.nref = *nref; fDirectories.push_back(dirInfo); - status = watch_node(nref, B_WATCH_DIRECTORY, this); - if (status != B_OK) { - fDirectories.pop_back(); - return status; - } + add_on_entry_info entryInfo; + entryInfo.dir_nref = *nref; BEntry entry; - while (directory.GetNextEntry(&entry, true) == B_OK) { - add_on_entry_info entryInfo; + while (directory.GetNextEntry(&entry) == B_OK) { if (entry.GetName(entryInfo.name) != B_OK || entry.GetNodeRef(&entryInfo.nref) != B_OK) { continue; } - entryInfo.dir_nref = *nref; fPendingEntries.push_back(entryInfo); } @@ -143,70 +154,55 @@ // Search pending entries first, which can simply be discarded // We might have this entry in the pending list multiple times, // so we search entire list through, even after finding one. - std::list<add_on_entry_info>::iterator eiter = fPendingEntries.begin(); + EntryList::iterator eiter = fPendingEntries.begin(); while (eiter != fPendingEntries.end()) { - if (eiter->nref == entryNodeRef) { + if (eiter->nref == entryNodeRef) eiter = fPendingEntries.erase(eiter); - } else { + else eiter++; - } } - add_on_entry_info info; - node_ref dirNodeRef; - make_node_ref(device, directory, &dirNodeRef); - - // find the entry's info, and the entry's directory info - std::list<add_on_directory_info>::iterator diter = fDirectories.begin(); - for (; diter != fDirectories.end() ; diter++) { - if (diter->nref == dirNodeRef) { - std::list<add_on_entry_info>::iterator eiter - = diter->entries.begin(); - for (; eiter != diter->entries.end() ; eiter++) { - info = *eiter; - if (eiter->nref == entryNodeRef) { - info = *eiter; - diter->entries.erase(eiter); - break; - } - } - break; - } + // Find the directory of the entry. + DirectoryList::iterator diter = fDirectories.begin(); + if (!_FindDirectory(directory, device, diter)) { + // If it was not found, we're done + return; } - // if it was not found, we're done - if (diter == fDirectories.end()) + eiter = diter->entries.begin(); + if (!_FindEntry(entryNodeRef, diter->entries, eiter)) { + // This must be the directory, but we didn't find the entry. return; + } + add_on_entry_info info = *eiter; + watch_node(&entryNodeRef, B_STOP_WATCHING, this); + diter->entries.erase(eiter); + // Start at the top again, and search until the directory we found // the old add-on in. If we find an add-on with the same name then - // the old add-on was not enabled. So we deallocate the old - // add-on and return. - std::list<add_on_directory_info>::iterator diter2 = fDirectories.begin(); - for (; diter2 != diter ; diter2++) { - std::list<add_on_entry_info>::iterator eiter = diter2->entries.begin(); - for (; eiter != diter2->entries.end() ; eiter++) { - if (strcmp(eiter->name, info.name) == 0) { - AddOnRemoved(&info); - return; - } + // the old add-on was not enabled. So we deallocate the old add-on and + // return. + DirectoryList::iterator diter2 = fDirectories.begin(); + for (; diter2 != diter; diter2++) { + if (_HasEntry(info.name, diter2->entries)) { + AddOnRemoved(&info); + return; } } - // The active add-on was removed. We need to disable and - // then subsequently deallocate it. + // An active add-on was removed. We need to disable and then subsequently + // deallocate it. AddOnDisabled(&info); AddOnRemoved(&info); // Continue searching for an add-on below us. If we find an add-on // with the same name, we must enable it. - for (diter++ ; diter != fDirectories.end() ; diter++) { - std::list<add_on_entry_info>::iterator eiter = diter->entries.begin(); - for (; eiter != diter->entries.end() ; eiter++) { - if (strcmp(eiter->name, info.name) == 0) { - AddOnEnabled(&*eiter); - return; - } + for (diter++; diter != fDirectories.end(); diter++) { + eiter = diter->entries.begin(); + if (_FindEntry(info.name, diter->entries, eiter)) { + AddOnEnabled(&*eiter); + break; } } } @@ -216,69 +212,49 @@ AddOnMonitorHandler::EntryMoved(const char* name, ino_t fromDirectory, ino_t toDirectory, dev_t device, ino_t node) { - // Search the "from" directory in the known entries - node_ref fromNodeRef; - make_node_ref(device, fromDirectory, &fromNodeRef); - std::list<add_on_directory_info>::iterator from_iter = fDirectories.begin(); - for (; from_iter != fDirectories.end(); from_iter++) { - if (from_iter->nref == fromNodeRef) - break; - } - - // Search the "to" directory in the known entries node_ref toNodeRef; make_node_ref(device, toDirectory, &toNodeRef); - std::list<add_on_directory_info>::iterator to_iter = fDirectories.begin(); - for ( ; to_iter != fDirectories.end() ; to_iter++) { - if (to_iter->nref == toNodeRef) - break; - } - if (from_iter == fDirectories.end() && to_iter == fDirectories.end()) { + // Search the "from" and "to" directory in the known directories + DirectoryList::iterator from_iter = fDirectories.begin(); + bool watchingFromDirectory = _FindDirectory(fromDirectory, device, + from_iter); + + DirectoryList::iterator to_iter = fDirectories.begin(); + bool watchingToDirectory = _FindDirectory(toNodeRef, to_iter); + + if (!watchingFromDirectory && !watchingToDirectory) { // It seems the notification was for a directory we are not - // actually watching by intention. + // actually watching (at least not by intention). return; } add_on_entry_info info; + node_ref entryNodeRef; make_node_ref(device, node, &entryNodeRef); - if (to_iter == fDirectories.end()) { + if (!watchingToDirectory) { // moved out of our view - std::list<add_on_entry_info>::iterator eiter - = from_iter->entries.begin(); - for (; eiter != from_iter->entries.end() ; eiter++) { - if (entryNodeRef == eiter->nref) { - // save the info and remove the entry - info = *eiter; - from_iter->entries.erase(eiter); - break; - } - } - - if (eiter == from_iter->entries.end()) { + EntryList::iterator eiter = from_iter->entries.begin(); + if (!_FindEntry(entryNodeRef, from_iter->entries, eiter)) { // we don't know anything about this entry yet.. ignore it return; } - // if the name is the same, save the information about this - // add-on into fFormerEntries for later use - if (strcmp(info.name, name) == 0) - fFormerEntries.push_back(info); + // save the info and remove the entry + info = *eiter; + watch_node(&entryNodeRef, B_STOP_WATCHING, this); + from_iter->entries.erase(eiter); // Start at the top again, and search until the from directory. // If we find a add-on with the same name then the moved add-on // was not enabled. So we are done. - std::list<add_on_directory_info>::iterator diter = fDirectories.begin(); - for (; diter != from_iter ; diter++) { - std::list<add_on_entry_info>::iterator eiter2 - = diter->entries.begin(); - for (; eiter2 != diter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - return; - } - } + DirectoryList::iterator diter = fDirectories.begin(); + for (; diter != from_iter; diter++) { + eiter = diter->entries.begin(); + if (_FindEntry(info.name, diter->entries, eiter)) + return; } // finally disable the add-on @@ -286,141 +262,110 @@ // Continue searching for a add-on below us. If we find a add-on // with the same name, we must enable it. - for (from_iter++ ; from_iter != fDirectories.end() ; from_iter++) { - std::list<add_on_entry_info>::iterator eiter2 - = from_iter->entries.begin(); - for (; eiter2 != from_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - AddOnEnabled(&*eiter2); - return; - } + for (from_iter++; from_iter != fDirectories.end(); from_iter++) { + eiter = from_iter->entries.begin(); + if (_FindEntry(info.name, from_iter->entries, eiter)) { + AddOnEnabled(&*eiter); + return; } } - // finally, if the new name is different, destroy the addon - if (strcmp(info.name, name) != 0) - AddOnRemoved(&info); + // finally destroy the addon + AddOnRemoved(&info); // done return; } - if (from_iter == fDirectories.end()) { + if (!watchingFromDirectory) { // moved into our view - std::list<add_on_entry_info>::iterator eiter = fFormerEntries.begin(); - for (; eiter != fFormerEntries.end() ; eiter++) { - if (entryNodeRef == eiter->nref) { - // save the info and remove the entry - info = *eiter; - fFormerEntries.erase(eiter); - break; - } - } - if (eiter != fFormerEntries.end()) { - if (strcmp(info.name, name) != 0) { - // name changed on the way in, remove the old one - AddOnRemoved(&info); - } - } - // update the info strncpy(info.name, name, sizeof(info.name)); info.nref = entryNodeRef; info.dir_nref = toNodeRef; - if (eiter == fFormerEntries.end()) { - // this add-on was not seen before - AddOnCreated(&info); - } + AddOnCreated(&info); // Start at the top again, and search until the to directory. // If we find an add-on with the same name then the moved add-on // is not to be enabled. So we are done. - std::list<add_on_directory_info>::iterator diter = fDirectories.begin(); - for (; diter != to_iter ; diter++) { - std::list<add_on_entry_info>::iterator eiter2 = diter->entries.begin(); - for (; eiter2 != diter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) - return; + DirectoryList::iterator diter = fDirectories.begin(); + for (; diter != to_iter; diter++) { + if (_HasEntry(info.name, diter->entries)) { + // The new add-on is being shadowed. + return; } } // The new add-on should be enabled, but first we check to see // if there is an add-on below us. If we find one, we disable it. - bool shadowing = false; - for (diter++ ; diter != fDirectories.end() ; diter++) { - std::list<add_on_entry_info>::iterator eiter2 = diter->entries.begin(); - for (; eiter2 != diter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - AddOnDisabled(&*eiter2); - shadowing = true; - break; - } + for (diter++ ; diter != fDirectories.end(); diter++) { + EntryList::iterator eiter = diter->entries.begin(); + if (_FindEntry(info.name, diter->entries, eiter)) { + AddOnDisabled(&*eiter); + break; } - if (shadowing) - break; } // enable the new add-on AddOnEnabled(&info); // put the new entry into the target directory - to_iter->entries.push_back(info); + _AddNewEntry(to_iter->entries, info); // done return; } - std::list<add_on_entry_info>::iterator eiter = from_iter->entries.begin(); - for (; eiter != from_iter->entries.end() ; eiter++) { - if (entryNodeRef == eiter->nref) { - // save the old info and remove the entry - info = *eiter; - from_iter->entries.erase(eiter); - break; - } + // The add-on was renamed, or moved within our hierarchy. + + EntryList::iterator eiter = from_iter->entries.begin(); + if (_FindEntry(entryNodeRef, from_iter->entries, eiter)) { + // save the old info and remove the entry + info = *eiter; + } else { + // If an entry moved from one watched directory into another watched + // directory, there will be two notifications, and this may be the + // second. We have handled everything in the first. In that case the + // entry was already removed from the fromDirectory and added in the + // toDirectory list. + return; } if (strcmp(info.name, name) == 0) { - // same name moved in heirarchy - debugger("add-on moved inside the heirarchy"); + // It should be impossible for the name to stay the same, unless the + // node moved in the watched hierarchy. Handle this case by removing + // the entry and readding it. TODO: This can temporarily enable add-ons + // which should in fact stay hidden (moving add-on from home to common + // folder or vice versa, the system add-on should remain hidden). + EntryRemoved(fromDirectory, device, node); + info.dir_nref = toNodeRef; + _EntryCreated(info); } else { + // Erase the entry + from_iter->entries.erase(eiter); + // check to see if it was formerly enabled - bool was_enabled = true; - std::list<add_on_directory_info>::iterator old_iter - = fDirectories.begin(); - for (; old_iter != from_iter ; old_iter++) { - std::list<add_on_entry_info>::iterator eiter2 - = old_iter->entries.begin(); - for (; eiter2 != old_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - was_enabled = false; - break; - } - } - if (!was_enabled) { + bool wasEnabled = true; + DirectoryList::iterator old_iter = fDirectories.begin(); + for (; old_iter != from_iter; old_iter++) { + if (_HasEntry(info.name, old_iter->entries)) { + wasEnabled = false; break; } } - // if it was enabled, disable it and enable the one under us, if it - // exists - if (was_enabled) { + // If it was enabled, disable it and enable the one under us, if it + // exists. + if (wasEnabled) { AddOnDisabled(&info); - bool done = false; - for (; old_iter != fDirectories.end() ; old_iter++) { - std::list<add_on_entry_info>::iterator eiter2 - = old_iter->entries.begin(); - for (; eiter2 != old_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - AddOnEnabled(&*eiter2); - done = true; - break; - } + for (; old_iter != fDirectories.end(); old_iter++) { + eiter = old_iter->entries.begin(); + if (_FindEntry(info.name, old_iter->entries, eiter)) { + AddOnEnabled(&*eiter); + break; } - if (done) - break; } } @@ -436,56 +381,75 @@ // check to see if we are newly enabled bool isEnabled = true; - std::list<add_on_directory_info>::iterator new_iter - = fDirectories.begin(); - for (; new_iter != to_iter ; new_iter++) { - std::list<add_on_entry_info>::iterator eiter2 - = new_iter->entries.begin(); - for (; eiter2 != new_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - isEnabled = false; - break; - } + DirectoryList::iterator new_iter = fDirectories.begin(); + for (; new_iter != to_iter; new_iter++) { + if (_HasEntry(info.name, new_iter->entries)) { + isEnabled = false; + break; } - if (!isEnabled) - break; } // if it is newly enabled, check under us for an enabled one, and // disable that first if (isEnabled) { - bool done = false; - for (; new_iter != fDirectories.end() ; new_iter++) { - std::list<add_on_entry_info>::iterator eiter2 - = new_iter->entries.begin(); - for (; eiter2 != new_iter->entries.end() ; eiter2++) { - if (strcmp(eiter2->name, info.name) == 0) { - AddOnDisabled(&*eiter2); - done = true; - break; - } + for (; new_iter != fDirectories.end(); new_iter++) { + eiter = new_iter->entries.begin(); + if (_FindEntry(info.name, new_iter->entries, eiter)) { + AddOnDisabled(&*eiter); + break; } - if (done) - break; } AddOnEnabled(&info); } + // put the new entry into the target directory + to_iter->entries.push_back(info); } +} - // put the new entry into the target directory - to_iter->entries.push_back(info); + +void +AddOnMonitorHandler::StatChanged(ino_t node, dev_t device) +{ + // This notification is received for the add-ons themselves. + + // TODO: Add the entry to the pending list, disable/enable it + // when the modification time remains stable. + + node_ref entryNodeRef; + make_node_ref(device, node, &entryNodeRef); + + DirectoryList::iterator diter = fDirectories.begin(); + for (; diter != fDirectories.end(); diter++) { + EntryList::iterator eiter = diter->entries.begin(); + for (; eiter != diter->entries.end(); eiter++) { + if (eiter->addon_nref == entryNodeRef) { + // Trigger reloading of the add-on + const add_on_entry_info* info = &*eiter; + AddOnDisabled(info); + AddOnRemoved(info); + AddOnCreated(info); + AddOnEnabled(info); + return; + } + } + } } +// #pragma mark - private + + //! Process pending entries. void AddOnMonitorHandler::_HandlePulse() { BDirectory directory; - std::list<add_on_entry_info>::iterator iter = fPendingEntries.begin(); + EntryList::iterator iter = fPendingEntries.begin(); while (iter != fPendingEntries.end()) { add_on_entry_info info = *iter; + // Initialize directory, or re-use from previous iteration, if + // directory node_ref remained the same from the last pending entry. node_ref dirNodeRef; if (directory.GetNodeRef(&dirNodeRef) != B_OK || dirNodeRef != info.dir_nref) { @@ -514,58 +478,150 @@ // we are going to deal with the stable entry, so remove it iter = fPendingEntries.erase(iter); - // put the new entry into the directory info - std::list<add_on_directory_info>::iterator diter = fDirectories.begin(); - for (; diter != fDirectories.end() ; diter++) { - if (diter->nref == dirNodeRef) { - diter->entries.push_back(info); - break; - } + _EntryCreated(info); + } +} + + +void +AddOnMonitorHandler::_EntryCreated(add_on_entry_info& info) +{ + // put the new entry into the directory info + DirectoryList::iterator diter = fDirectories.begin(); + for (; diter != fDirectories.end(); diter++) { + if (diter->nref == info.dir_nref) { + _AddNewEntry(diter->entries, info); + break; } + } - // report it - AddOnCreated(&info); + // report it + AddOnCreated(&info); - // Start at the top again, and search until the directory we put - // the new add-on in. If we find an add-on with the same name then - // the new add-on should not be enabled. - bool enabled = true; - std::list<add_on_directory_info>::iterator diter2 - = fDirectories.begin(); - for (; diter2 != diter ; diter2++) { - std::list<add_on_entry_info>::iterator eiter - = diter2->entries.begin(); - for (; eiter != diter2->entries.end() ; eiter++) { - if (strcmp(eiter->name, info.name) == 0) { - enabled = false; - break; - } - } - if (!enabled) - break; + // Start at the top again, and search until the directory we put + // the new add-on in. If we find an add-on with the same name then + // the new add-on should not be enabled. + bool enabled = true; + DirectoryList::iterator diter2 = fDirectories.begin(); + for (; diter2 != diter; diter2++) { + if (_HasEntry(info.name, diter2->entries)) { + enabled = false; + break; } - if (!enabled) { - // if we are not enabled, go on to the next pending entry - continue; - } + } + if (!enabled) + return; - // The new add-on should be enabled, but first we check to see - // if there is an add-on below us. If we find one, we disable it. - bool shadowing = false; - for (diter++ ; diter != fDirectories.end() ; diter++) { - std::list<add_on_entry_info>::iterator eiter = diter->entries.begin(); - for (; eiter != diter->entries.end() ; eiter++) { - if (strcmp(eiter->name, info.name) == 0) { - AddOnDisabled(&*eiter); - shadowing = true; - break; - } - } - if (shadowing) - break; + // The new add-on should be enabled, but first we check to see + // if there is an add-on shadowed by the new one. If we find one, + // we disable it. + for (diter++ ; diter != fDirectories.end(); diter++) { + EntryList::iterator eiter = diter->entries.begin(); + if (_FindEntry(info.name, diter->entries, eiter)) { + AddOnDisabled(&*eiter); + break; } + } - // finally, enable the new entry - AddOnEnabled(&info); + // enable the new entry + AddOnEnabled(&info); +} + + +bool +AddOnMonitorHandler::_FindEntry(const node_ref& entry, const EntryList& list, + EntryList::iterator& it) const +{ + for (; it != list.end(); it++) { + if (it->nref == entry) + return true; } + return false; } + + +bool +AddOnMonitorHandler::_FindEntry(const char* name, const EntryList& list, + EntryList::iterator& it) const +{ + for (; it != list.end(); it++) { + if (strcmp(it->name, name) == 0) + return true; + } + return false; +} + + +bool +AddOnMonitorHandler::_HasEntry(const node_ref& entry, EntryList& list) const +{ + EntryList::iterator it = list.begin(); + return _FindEntry(entry, list, it); +} + + +bool +AddOnMonitorHandler::_HasEntry(const char* name, EntryList& list) const +{ + EntryList::iterator it = list.begin(); + return _FindEntry(name, list, it); +} + + +bool +AddOnMonitorHandler::_FindDirectory(ino_t directory, dev_t device, + DirectoryList::iterator& it) const +{ + node_ref nodeRef; + make_node_ref(device, directory, &nodeRef); + return _FindDirectory(nodeRef, it, fDirectories.end()); +} + + +bool +AddOnMonitorHandler::_FindDirectory(const node_ref& directoryNodeRef, + DirectoryList::iterator& it) const +{ + return _FindDirectory(directoryNodeRef, it, fDirectories.end()); +} + + +bool +AddOnMonitorHandler::_FindDirectory(ino_t directory, dev_t device, + DirectoryList::iterator& it, + const DirectoryList::const_iterator& end) const +{ + node_ref nodeRef; + make_node_ref(device, directory, &nodeRef); + return _FindDirectory(nodeRef, it, end); +} + + +bool +AddOnMonitorHandler::_FindDirectory(const node_ref& directoryNodeRef, + DirectoryList::iterator& it, + const DirectoryList::const_iterator& end) const +{ + for (; it != end; it++) { + if (it->nref == directoryNodeRef) + return true; + } + return false; +} + + +void +AddOnMonitorHandler::_AddNewEntry(EntryList& list, add_on_entry_info& info) +{ + BDirectory directory(&info.dir_nref); + BEntry entry(&directory, info.name, true); + + node_ref addOnRef; + if (entry.GetNodeRef(&addOnRef) == B_OK) { + watch_node(&addOnRef, B_WATCH_STAT, this); + info.addon_nref = addOnRef; + } else + info.addon_nref = info.nref; + + list.push_back(info); +}