/* ### * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "action.hh" #include "funcdata.hh" #include "coreaction.hh" /// Specify the name, group, and properties of the Action /// \param f is the collection of property flags /// \param nm is the Action name /// \param g is the Action group Action::Action(uint4 f,const string &nm,const string &g) { flags = f; status = status_start; breakpoint = 0; name = nm; basegroup = g; count_tests = 0; count_apply = 0; } /// If enabled, issue a warning that this Action has been applied /// \param glb is the controlling Architecture void Action::issueWarning(Architecture *glb) { if ((flags&(rule_warnings_on|rule_warnings_given)) == rule_warnings_on) { flags |= rule_warnings_given; glb->printMessage("WARNING: Applied action "+name); } } /// Check if there was an active \e start break point on this action /// \return true if there was a start breakpoint bool Action::checkStartBreak(void) { if ((breakpoint&(break_start|tmpbreak_start))!=0) { breakpoint &= ~(tmpbreak_start); // Clear breakpoint if temporary return true; // Breakpoint was active } return false; // Breakpoint was not active } #ifdef OPACTION_DEBUG /// If this Action matches the given name, enable debugging. /// \param nm is the Action name to match /// \return true if debugging was enabled bool Action::turnOnDebug(const string &nm) { if (nm == name) { flags |= rule_debug; return true; } return false; } /// If this Action matches the given name, disable debugging. /// \param nm is the Action name to match /// \return true if debugging was disabled bool Action::turnOffDebug(const string &nm) { if (nm == name) { flags &= ~rule_debug; return true; } return false; } #endif /// Print out the collected statistics for the Action to stream /// \param s is the output stream void Action::printStatistics(ostream &s) const { s << name << dec << " Tested=" << count_tests << " Applied=" << count_apply << endl; } /// \param data is the new function \b this Action may affect void Action::reset(Funcdata &data) { status = status_start; flags &= ~rule_warnings_given; // Indicate a warning has not been given yet } /// Reset all the counts to zero void Action::resetStats(void) { count_tests = 0; count_apply = 0; } /// Check if there was an active \e action breakpoint on this Action /// \return true if there was an action breakpoint bool Action::checkActionBreak(void) { if ((breakpoint&(break_action|tmpbreak_action))!=0) { breakpoint &= ~(tmpbreak_action); // Clear temporary breakpoint return true; // Breakpoint was active } return false; // Breakpoint was not active } /// The description is suitable for a console mode listing of actions /// \param s is the output stream /// \param num is a starting index to associate with the action (and its sub-actions) /// \param depth is amount of indent necessary before printing /// \return the next available index int4 Action::print(ostream &s,int4 num,int4 depth) const { s << setw(4) << dec << num; s << (char *) (((flags&rule_repeatapply)!=0) ? " repeat " : " "); s << (char) (((flags&rule_onceperfunc)!=0) ? '!' : ' '); s << (char) (((breakpoint&(break_start|tmpbreak_start))!=0) ? 'S' : ' '); s << (char) (((breakpoint&(break_action|tmpbreak_action))!=0) ? 'A' : ' '); for(int4 i=0;ibreakpoint |= tp; return true; } Rule *rule = getSubRule(specify); if (rule != (Rule *)0) { rule->setBreak(tp); return true; } return false; } void Action::clearBreakPoints(void) { breakpoint = 0; } /// If enabled, a warning will be printed whenever this action applies. /// The warning can be toggled for \b this Action or some sub-action by /// specifying its name. /// \param val is the toggle value for the warning /// \param specify is the name of the action or sub-action to toggle /// \return true if the warning was successfully toggled bool Action::setWarning(bool val,const string &specify) { Action *res = getSubAction(specify); if (res != (Action *)0) { if (val) res->turnOnWarnings(); else res->turnOffWarnings(); return true; } Rule *rule = getSubRule(specify); if (rule != (Rule *)0) { if (val) rule->turnOnWarnings(); else rule->turnOffWarnings(); return true; } return false; } /// An individual Rule can be disabled by name, within \b this Action. It must /// be specified by a ':' separated name \e path, from the root Action down /// to the specific Rule. /// \param specify is the name path /// \return \b true if the Rule is successfully disabled bool Action::disableRule(const string &specify) { Rule *rule = getSubRule(specify); if (rule != (Rule *)0) { rule->setDisable(); return true; } return false; } /// An individual Rule can be enabled by name, within \b this Action. It must /// be specified by a ':' separated name \e path, from the root Action down /// to the specific Rule. /// \param specify is the name path /// \return \b true if the Rule is successfully enabled bool Action::enableRule(const string &specify) { Rule *rule = getSubRule(specify); if (rule != (Rule *)0) { rule->clearDisable(); return true; } return false; } /// Pull the next token from a ':' separated list of Action and Rule names /// \param token will be filled with string up to the next ':' /// \param remain will be whats left of the list of removing the token and ':' /// \param is the list to pull the token from static void next_specifyterm(string &token,string &remain,const string &specify) { string::size_type res = specify.find(':'); if (res != string::npos) { token = specify.substr(0,res); remain = specify.substr(res+1); } else { token = specify; remain.clear(); } } /// If this Action matches the given name, it is returned. If the /// name matches a sub-action, this is returned. /// \param specify is the action name to match /// \return the matching Action or sub-action Action *Action::getSubAction(const string &specify) { if (name == specify) return this; return (Action *)0; } /// Find a Rule, as a component of \b this Action, with the given name. /// \param specify is the name of the rule /// \return the matching sub-rule Rule *Action::getSubRule(const string &specify) { return (Rule *)0; } /// Run \b this Action until completion or a breakpoint occurs. Depending /// on the behavior properties of this instance, the apply() method may get /// called many times or none. Generally the number of changes made by /// the action is returned, but if a breakpoint occurs -1 is returned. /// A successive call to perform() will "continue" from the break point. /// \param data is the function being acted on /// \return the number of changes or -1 int4 Action::perform(Funcdata &data) { int4 res; do { switch(status) { case status_start: count = 0; // No changes made yet by this action if (checkStartBreak()) { status = status_breakstarthit; return -1; // Indicate partial completion } count_tests += 1; case status_breakstarthit: case status_repeat: lcount = count; case status_mid: #ifdef OPACTION_DEBUG data.debugActivate(); #endif res = apply(data); // Start or continue action #ifdef OPACTION_DEBUG data.debugModPrint(getName()); #endif if (res < 0) { // negative indicates partial completion status = status_mid; return res; } else if (lcount < count) { // Action has been applied issueWarning(data.getArch()); count_apply += 1; if (checkActionBreak()) { status = status_actionbreak; return -1; // Indicate action breakpoint } #ifdef OPACTION_DEBUG else if (data.debugBreak()) { status = status_actionbreak; data.debugHandleBreak(); return -1; } #endif } break; case status_end: return 0; // Rule applied, do not repeat until reset break; case status_actionbreak: // Returned -1 last time, but we do not reapply break; // we either repeat, or return our count } status = status_repeat; } while((lcount0)||((flags&rule_onceperfunc)!=0)) status = status_end; else status = status_start; } else status = status_start; return count; } ActionGroup::~ActionGroup(void) { vector::iterator iter; for(iter=list.begin();iter!=list.end();++iter) delete *iter; } /// To be used only during the construction of \b this ActionGroup. This routine /// adds an Action to the end of this group's list. /// \param ac is the Action to add void ActionGroup::addAction(Action *ac) { list.push_back(ac); } void ActionGroup::clearBreakPoints(void) { vector::const_iterator iter; for(iter=list.begin();iter!= list.end();++iter) (*iter)->clearBreakPoints(); Action::clearBreakPoints(); } Action *ActionGroup::clone(const ActionGroupList &grouplist) const { ActionGroup *res = (ActionGroup *)0; vector::const_iterator iter; Action *ac; for(iter=list.begin();iter!=list.end();++iter) { ac = (*iter)->clone(grouplist); if (ac != (Action *)0) { if (res == (ActionGroup *)0) res = new ActionGroup(flags,getName()); res->addAction(ac); } } return res; } void ActionGroup::reset(Funcdata &data) { vector::iterator iter; Action::reset(data); for(iter=list.begin();iter!=list.end();++iter) (*iter)->reset(data); // Reset each subrule } void ActionGroup::resetStats(void) { vector::iterator iter; Action::resetStats(); for(iter=list.begin();iter!=list.end();++iter) (*iter)->resetStats(); } int4 ActionGroup::print(ostream &s,int4 num,int4 depth) const { vector::const_iterator titer; num = Action::print(s,num,depth); s << endl; for(titer=list.begin();titer!=list.end();++titer) { num = (*titer)->print(s,num,depth+1); if (state == titer) s << " <-- "; s << endl; } return num; } void ActionGroup::printState(ostream &s) const { Action *subact; Action::printState(s); if (status==status_mid) { subact = *state; subact->printState(s); } } Action *ActionGroup::getSubAction(const string &specify) { string token,remain; next_specifyterm(token,remain,specify); if (name == token) { if (remain.empty()) return this; } else remain = specify; // Still have to match entire specify vector::iterator iter; Action *lastaction = (Action *)0; int4 matchcount = 0; for(iter=list.begin();iter!=list.end();++iter) { Action *testaction = (*iter)->getSubAction(remain); if (testaction != (Action *)0) { lastaction = testaction; matchcount += 1; if (matchcount > 1) return (Action *)0; } } return lastaction; } Rule *ActionGroup::getSubRule(const string &specify) { string token,remain; next_specifyterm(token,remain,specify); if (name == token) { if (remain.empty()) return (Rule *)0; } else remain = specify; // Still have to match entire specify vector::iterator iter; Rule *lastrule = (Rule *)0; int4 matchcount = 0; for(iter=list.begin();iter!=list.end();++iter) { Rule *testrule = (*iter)->getSubRule(remain); if (testrule != (Rule *)0) { lastrule = testrule; matchcount += 1; if (matchcount > 1) return (Rule *)0; } } return lastrule; } int4 ActionGroup::apply(Funcdata &data) { int4 res; if (status != status_mid) state = list.begin(); // Initialize the derived action for(;state!=list.end();++state) { res = (*state)->perform(data); if (res>0) { // A change was made count += res; if (checkActionBreak()) { // Check if this is an action breakpoint ++state; return -1; } } else if (res<0) // Partial completion of member return -1; // equates to partial completion of group action } return 0; // Indicate successful completion } Action *ActionRestartGroup::clone(const ActionGroupList &grouplist) const { ActionGroup *res = (ActionGroup *)0; vector::const_iterator iter; Action *ac; for(iter=list.begin();iter!=list.end();++iter) { ac = (*iter)->clone(grouplist); if (ac != (Action *)0) { if (res == (ActionGroup *)0) res = new ActionRestartGroup(flags,getName(),maxrestarts); res->addAction(ac); } } return res; } void ActionRestartGroup::reset(Funcdata &data) { curstart = 0; ActionGroup::reset(data); } int4 ActionRestartGroup::apply(Funcdata &data) { int4 res; if (curstart == -1) return 0; // Already completed for(;;) { res = ActionGroup::apply(data); if (res != 0) return res; if (!data.hasRestartPending()) { curstart = -1; return 0; } if (data.isJumptableRecoveryOn()) // Don't restart within jumptable recovery return 0; curstart += 1; if (curstart > maxrestarts) { data.warningHeader("Exceeded maximum restarts with more pending"); curstart = -1; return 0; } data.getArch()->clearAnalysis(&data); // Reset everything but ourselves vector::iterator iter; for(iter=list.begin();iter!=list.end();++iter) (*iter)->reset(data); // Reset each subrule status = status_start; } } #ifdef OPACTION_DEBUG bool ActionGroup::turnOnDebug(const string &nm) { if (Action::turnOnDebug(nm)) return true; vector::iterator iter; for(iter = list.begin();iter!=list.end();++iter) if ((*iter)->turnOnDebug(nm)) return true; return false; } bool ActionGroup::turnOffDebug(const string &nm) { if (Action::turnOffDebug(nm)) return true; vector::iterator iter; for(iter = list.begin();iter!=list.end();++iter) if ((*iter)->turnOffDebug(nm)) return true; return false; } #endif void ActionGroup::printStatistics(ostream &s) const { Action::printStatistics(s); vector::const_iterator iter; for(iter = list.begin();iter!=list.end();++iter) (*iter)->printStatistics(s); } /// \param g is the groupname to which \b this Rule belongs /// \param fl is the set of properties /// \param nm is the name of the Rule Rule::Rule(const string &g,uint4 fl,const string &nm) { flags = fl; name = nm; breakpoint = 0; basegroup = g; count_tests = 0; count_apply = 0; } /// This method is called whenever \b this Rule applies. If warnings have been /// enabled for the Rule via turnOnWarnings(), this method will print a message /// indicating the Rule has been applied. Even with repeat calls, the message /// will only be printed once (until reset() is called) /// \param glb is the Architecture holding the console to print to void Rule::issueWarning(Architecture *glb) { if ((flags&(warnings_on|warnings_given)) == warnings_on) { flags |= warnings_given; glb->printMessage("WARNING: Applied rule "+name); } } /// Any state that is specific to a particular function is cleared by this method. /// This method can be used to initialize a Rule based on a new function it will apply to /// \param data is the \e new function about to be transformed void Rule::reset(Funcdata &data) { flags &= ~warnings_given; // Indicate that warning has not yet been given } /// Counts of when this Rule has been attempted/applied are reset to zero. /// Derived Rules may reset their own statistics. void Rule::resetStats(void) { count_tests = 0; count_apply = 0; } #ifdef OPACTION_DEBUG /// If \b this Rule has the given name, then enable debugging. /// \param nm is the given name to match /// \return true if debugging was enabled bool Rule::turnOnDebug(const string &nm) { if (nm == name) { flags |= rule_debug; return true; } return false; } /// If \b this Rule has the given name, then disable debugging. /// \param nm is the given name to match /// \return true if debugging was disabled bool Rule::turnOffDebug(const string &nm) { if (nm == name) { flags &= ~rule_debug; return true; } return false; } #endif /// Print the accumulated counts associated with applying this Rule to stream. /// This method is intended for console mode debugging. Derived Rules may /// override this to display their own statistics. /// \param s is the output stream void Rule::printStatistics(ostream &s) const { s << name << dec << " Tested=" << count_tests << " Applied=" << count_apply << endl; } /// Populate the given array with all possible OpCodes this Rule might apply to. /// By default, this method returns all possible OpCodes /// \param oplist is the array to populate void Rule::getOpList(vector &oplist) const { uint4 i; for(i=0;i::iterator iter; for(iter=allrules.begin();iter!=allrules.end();++iter) delete *iter; } /// This method should only be invoked during construction of this ActionPool /// A single Rule is added to the pool. The Rule's OpCode is inspected by this method. /// \param rl is the Rule to add void ActionPool::addRule(Rule *rl) { vector oplist; vector::iterator iter; allrules.push_back(rl); rl->getOpList(oplist); for(iter=oplist.begin();iter!=oplist.end();++iter) perop[*iter].push_back(rl); // Add rule to list for each op it registers for } int4 ActionPool::print(ostream &s,int4 num,int4 depth) const { vector::const_iterator iter; Rule *rl; int4 i; num = Action::print(s,num,depth); s << endl; depth += 1; for(iter=allrules.begin();iter!=allrules.end();++iter) { rl = *iter; s << setw(4) << dec << num; s << (char) ( rl->isDisabled() ? 'D' : ' '); s << (char) ( ((rl->getBreakPoint()&(break_action|tmpbreak_action))!=0) ? 'A' : ' '); for(i=0;igetName(); s << endl; num += 1; } return num; } void ActionPool::printState(ostream &s) const { PcodeOp *op; Action::printState(s); if (status==status_mid) { op = (*op_state).second; s << ' ' << op->getSeqNum(); } } Rule *ActionPool::getSubRule(const string &specify) { string token,remain; next_specifyterm(token,remain,specify); if (name == token) { if (remain.empty()) return (Rule *)0; // Match, but not a rule } else remain = specify; // Still have to match entire specify vector::iterator iter; Rule *lastrule = (Rule *)0; int4 matchcount = 0; for(iter=allrules.begin();iter!=allrules.end();++iter) { Rule *testrule = *iter; if (testrule->getName() == remain) { lastrule = testrule; matchcount += 1; if (matchcount > 1) return (Rule *)0; } } return lastrule; } /// This method attempts to apply each Rule to the current PcodeOp /// Action breakpoints are checked if the Rule successfully applies. /// 0 is returned for no breakpoint, -1 if a breakpoint occurs. /// If a breakpoint did occur, an additional call to processOp() will /// pick up where it left off before the breakpoint. The PcodeOp iterator is advanced. /// \param op is the current PcodeOp /// \param data is the function being transformed /// \return 0 if no breakpoint, -1 otherwise int4 ActionPool::processOp(PcodeOp *op,Funcdata &data) { Rule *rl; int4 res; uint4 opc; if (op->isDead()) { op_state++; data.opDeadAndGone(op); rule_index = 0; return 0; } opc = op->code(); while(rule_index < perop[opc].size()) { rl = perop[opc][rule_index++]; if (rl->isDisabled()) continue; #ifdef OPACTION_DEBUG data.debugActivate(); #endif rl->count_tests += 1; res = rl->applyOp(op,data); #ifdef OPACTION_DEBUG data.debugModPrint(rl->getName()); #endif if (res>0) { rl->count_apply += 1; count += res; rl->issueWarning(data.getArch()); // Check if we need to issue a warning if (rl->checkActionBreak()) return -1; #ifdef OPACTION_DEBUG if (data.debugBreak()) { data.debugHandleBreak(); return -1; } #endif if (op->isDead()) break; if (opc != op->code()) { // Set of rules to apply to this op has changed opc = op->code(); rule_index = 0; } } else if (opc != op->code()) { data.getArch()->printMessage("ERROR: Rule " + rl->getName() + " changed op without returning result of 1!"); opc = op->code(); rule_index = 0; } } op_state++; rule_index = 0; return 0; } int4 ActionPool::apply(Funcdata &data) { if (status != status_mid) { op_state = data.beginOpAll(); // Initialize the derived action rule_index = 0; } for(;op_state!=data.endOpAll();) if (0!=processOp((*op_state).second,data)) return -1; return 0; // Indicate successful completion } void ActionPool::clearBreakPoints(void) { vector::const_iterator iter; for(iter=allrules.begin();iter!=allrules.end();++iter) (*iter)->clearBreakPoints(); Action::clearBreakPoints(); } Action *ActionPool::clone(const ActionGroupList &grouplist) const { ActionPool *res = (ActionPool *)0; vector::const_iterator iter; Rule *rl; for(iter=allrules.begin();iter!=allrules.end();++iter) { rl = (*iter)->clone(grouplist); if (rl != (Rule *)0) { if (res == (ActionPool *)0) res = new ActionPool(flags,getName()); res->addRule(rl); } } return res; } void ActionPool::reset(Funcdata &data) { vector::iterator iter; Action::reset(data); for(iter=allrules.begin();iter!=allrules.end();++iter) (*iter)->reset(data); } void ActionPool::resetStats(void) { vector::iterator iter; Action::resetStats(); for(iter=allrules.begin();iter!=allrules.end();++iter) (*iter)->resetStats(); } #ifdef OPACTION_DEBUG bool ActionPool::turnOnDebug(const string &nm) { vector::iterator iter; if (Action::turnOnDebug(nm)) return true; for(iter=allrules.begin();iter!=allrules.end();++iter) if ((*iter)->turnOnDebug(nm)) return true; return false; } bool ActionPool::turnOffDebug(const string &nm) { vector::iterator iter; if (Action::turnOffDebug(nm)) return true; for(iter=allrules.begin();iter!=allrules.end();++iter) if ((*iter)->turnOffDebug(nm)) return true; return false; } #endif void ActionPool::printStatistics(ostream &s) const { vector::const_iterator iter; Action::printStatistics(s); for(iter=allrules.begin();iter!=allrules.end();++iter) (*iter)->printStatistics(s); } const char ActionDatabase::universalname[] = "universal"; ActionDatabase::~ActionDatabase(void) { map::iterator iter; for(iter = actionmap.begin();iter!=actionmap.end();++iter) delete (*iter).second; } /// Clear out (possibly altered) root Actions. Reset the default groups. /// Set the default root action "decompile" void ActionDatabase::resetDefaults(void) { Action *universalAction = (Action *)0; map::iterator iter; iter = actionmap.find(universalname); if (iter != actionmap.end()) universalAction = (*iter).second; for(iter = actionmap.begin();iter!=actionmap.end();++iter) { Action *curAction = (*iter).second; if (curAction != universalAction) delete curAction; // Clear out any old (modified) root actions } actionmap.clear(); registerAction(universalname, universalAction); buildDefaultGroups(); setCurrent("decompile"); // The default root action } const ActionGroupList &ActionDatabase::getGroup(const string &grp) const { map::const_iterator iter; iter = groupmap.find(grp); if (iter == groupmap.end()) throw LowlevelError("Action group does not exist: "+grp); return (*iter).second; } /// The Action is specified by name. A grouplist must already exist for this name. /// If the Action doesn't already exist, it will be derived from the \e universal /// action via this grouplist. /// \param actname is the name of the \e root Action Action *ActionDatabase::setCurrent(const string &actname) { currentactname = actname; currentact = deriveAction(universalname,actname); return currentact; } /// A particular group is either added or removed from the grouplist defining /// a particular \e root Action. The \e root Action is then (re)derived from the universal /// \param grp is the name of the \e root Action /// \param basegrp is name of group (within the grouplist) to toggle /// \param val is \b true if the group should be added or \b false if it should be removed /// \return the modified \e root Action Action *ActionDatabase::toggleAction(const string &grp, const string &basegrp,bool val) { Action *act = getAction(universalname); if (val) addToGroup(grp,basegrp); else removeFromGroup(grp,basegrp); const ActionGroupList &curgrp(getGroup(grp)); // Group should already exist Action *newact = act->clone(curgrp); registerAction(grp,newact); if (grp == currentactname) currentact = newact; return newact; } /// (Re)set the grouplist for a particular \e root Action. Do not use this routine /// to redefine an existing \e root Action. /// \param grp is the name of the \e root Action /// \param argv is a list of static char pointers, which must end with a NULL pointer, or a zero length string. void ActionDatabase::setGroup(const string &grp,const char **argv) { ActionGroupList &curgrp( groupmap[ grp ] ); curgrp.list.clear(); // Clear out any old members for(int4 i=0;;++i) { if (argv[i] == (char *)0) break; if (argv[i][0] == '\0') break; curgrp.list.insert( argv[i] ); } isDefaultGroups = false; } /// Copy an existing \e root Action by copying its grouplist, giving it a new name. /// This is suitable for a copy then modify strategy to create a new \e root Action. /// Do not use to redefine a \e root Action that has already been instantiated /// \param oldname is the name of an existing \e root Action /// \param newname is the name of the copy void ActionDatabase::cloneGroup(const string &oldname,const string &newname) { const ActionGroupList &curgrp(getGroup(oldname)); // Should already exist groupmap[ newname ] = curgrp; // Copy the group isDefaultGroups = false; } /// Add a group to the grouplist for a particular \e root Action. /// Do not use to redefine a \e root Action that has already been instantiated. /// \param grp is the name of the \e root Action /// \param basegroup is the group to add /// \return \b true for a new addition, \b false is the group was already present bool ActionDatabase::addToGroup(const string &grp, const string &basegroup) { isDefaultGroups = false; ActionGroupList &curgrp( groupmap[ grp ] ); return curgrp.list.insert( basegroup ).second; } /// The group is removed from the grouplist of a \e root Action. /// Do not use to redefine a \e root Action that has already been instantiated. /// \param grp is the name of the \e root Action /// \param basegrp is the group to remove /// \return \b true if the group existed and was removed bool ActionDatabase::removeFromGroup(const string &grp, const string &basegrp) { isDefaultGroups = false; ActionGroupList &curgrp( groupmap[ grp ] ); return (curgrp.list.erase(basegrp) > 0); } /// \param nm is the name of the \e root Action Action *ActionDatabase::getAction(const string &nm) const { map::const_iterator iter; iter = actionmap.find(nm); if (iter == actionmap.end()) throw LowlevelError("No registered action: "+nm); return (*iter).second; } /// Internal method for associated a \e root Action name with its Action object. /// The database takes over memory management of the object. /// \param nm is the name to register as /// \param act is the Action object void ActionDatabase::registerAction(const string &nm,Action *act) { map::iterator iter; iter = actionmap.find(nm); if (iter != actionmap.end()) { delete (*iter).second; (*iter).second = act; } else { actionmap[nm] = act; } } /// Internal method to build the Action object corresponding to a \e root Action /// The new Action object is created by selectively cloning components /// from an existing object based on a grouplist. /// \param baseaction is the name of the model Action object to derive \e from /// \param grp is the name of the grouplist steering the clone Action *ActionDatabase::deriveAction(const string &baseaction, const string &grp) { map::iterator iter; iter = actionmap.find(grp); if (iter != actionmap.end()) return (*iter).second; // Already derived this action const ActionGroupList &curgrp(getGroup(grp)); // Group should already exist Action *act = getAction(baseaction); Action *newact = act->clone( curgrp ); // Register the action with the name of the group it was derived from registerAction(grp,newact); return newact; }