static void
_set_node_commit_child_delete_failed(CfgNode& node)
{
  // recursively bottom-up
  node.setCommitChildDeleteFailed();
  if (node.getParent()) {
    _set_node_commit_child_delete_failed(*(node.getParent()));
  }
}
static void
_set_commit_subtree_changed(CfgNode& node)
{
  // recursively bottom-up
  if (node.commitSubtreeChanged()) {
    // already set => terminate recursion
    return;
  }
  node.setCommitSubtreeChanged();
  if (node.getParent()) {
    _set_commit_subtree_changed(*(node.getParent()));
  }
}
static void
_get_commit_prio_subtrees(CfgNode *sroot, PrioNode& parent)
{
  if (!sroot) {
    return;
  }

  PrioNode *pn = &parent;
  // need a local copy since nodes can be detached
  vector<CfgNode *> cnodes = sroot->getChildNodes();
  if (sroot->getPriority() && (sroot->isValue() || !sroot->isTag())) {
    // enforce hierarchical constraint
    unsigned int prio = sroot->getPriority();
    unsigned int pprio = parent.getPriority();
    if (prio <= pprio) {
      // can't have that => make it higher than parent priority
      OUTPUT_USER("Warning: priority inversion [%s](%u) <= [%s](%u)\n"
                  "         changing [%s] to (%u)\n",
                  sroot->getCommitPath().to_string().c_str(), prio,
                  parent.getCommitPath().to_string().c_str(), pprio,
                  sroot->getCommitPath().to_string().c_str(),
                  pprio + 1);
      sroot->setPriority(pprio + 1);
    }

    // only non-"tag node" applies ("tag nodes" not used in prio tree)
    pn = new PrioNode(sroot);
    /* record the original parent in config tree. this will be used to
     * enforce "hierarchical constraint" in the config. skip the tag node
     * if this is a tag value since commit doesn't act on tag nodes.
     */
    CfgNode *pnode = sroot->getParent();
    pn->setCfgParent(sroot->isTag() ? pnode->getParent() : pnode);
    parent.addChildNode(pn);
    sroot->detachFromParent();
  }

  for (size_t i = 0; i < cnodes.size(); i++) {
    _get_commit_prio_subtrees(cnodes[i], *pn);
  }
}