static int lmount(lua_State *L) { struct sprite *s = self(L); const char * name = luaL_checkstring(L,2); int index = sprite_child(s, name); if (index < 0) { return luaL_error(L, "No child name %s", name); } lua_getuservalue(L, 1); struct sprite * child = (struct sprite *)lua_touserdata(L, 3); lua_rawgeti(L, -1, index+1); if (lua_isnil(L, -1)) { lua_pop(L, 1); } else { struct sprite * c = lua_touserdata(L, -1); if (c == child) { // mount not change return 0; } if (!c->multimount) { // try to remove parent ref lua_getuservalue(L, -1); if (lua_istable(L, -1)) { lua_pushnil(L); lua_rawseti(L, -2, 0); } lua_pop(L, 2); } else { lua_pop(L, 1); } } if (child == NULL) { sprite_mount(s, index, NULL); lua_pushnil(L); lua_rawseti(L, -2, index+1); } else { if (child->parent) { unlink_parent(L, child, 3); } sprite_mount(s, index, child); lua_pushvalue(L, 3); lua_rawseti(L, -2, index+1); if (!child->multimount) { // multimount has no parent // set child's new parent ref_parent(L, 3, 1); } } return 0; }
/* * Calculates the threading relationships for a list of messages */ Container *calculate_threads(apr_pool_t *p, MBOX_LIST *l) { apr_hash_t *h, *rootSet, *subjectSet; apr_hash_index_t *hashIndex; MBOX_LIST *current = l; const apr_array_header_t *refHdr; apr_table_entry_t *refEnt; Message *m; Container *c, *subjectPair, *realParent, *curParent, *tmp; void *hashKey, *hashVal, *subjectVal; char *subject; int msgIDLen, refLen, i; apr_ssize_t hashLen, subjectLen; /* FIXME: Use APR_HASH_KEY_STRING instead? Maybe slower. */ h = apr_hash_make(p); while (current != NULL) { m = (Message *) current->value; msgIDLen = strlen(m->msgID); c = (Container *) apr_hash_get(h, m->msgID, msgIDLen); if (c) { c->message = m; } else { c = (Container *) apr_pcalloc(p, sizeof(Container)); c->message = m; c->parent = NULL; c->child = NULL; c->next = NULL; apr_hash_set(h, m->msgID, msgIDLen, c); } realParent = NULL; if (m->references) { refHdr = apr_table_elts(m->references); refEnt = (apr_table_entry_t *) refHdr->elts; for (i = 0; i < refHdr->nelts; i++) { refLen = strlen(refEnt[i].key); curParent = (Container *) apr_hash_get(h, refEnt[i].key, refLen); /* Create a dummy node to store the message we haven't * yet seen. */ if (!curParent) { curParent = (Container *) apr_pcalloc(p, sizeof(Container)); apr_hash_set(h, refEnt[i].key, refLen, curParent); } /* Check to make sure we are not going to create a loop * by adding this parent to our list. */ if (realParent && !detect_loop(curParent, realParent) && !detect_loop(realParent, curParent)) { /* Update the parent */ if (curParent->parent) unlink_parent(curParent); curParent->parent = realParent; curParent->next = realParent->child; realParent->child = curParent; } /* We now have a new parent */ realParent = curParent; } } /* The last parent we saw is our parent UNLESS it causes a loop. */ if (realParent && !detect_loop(c, realParent) && !detect_loop(realParent, c)) { /* We need to unlink our parent's link to us. */ if (c->parent) unlink_parent(c); c->parent = realParent; c->next = realParent->child; realParent->child = c; } current = current->next; } /* Find the root set */ rootSet = apr_hash_make(p); for (hashIndex = apr_hash_first(p, h); hashIndex; hashIndex = apr_hash_next(hashIndex)) { apr_hash_this(hashIndex, (void *) &hashKey, &hashLen, &hashVal); c = (Container *) hashVal; if (!c->parent) apr_hash_set(rootSet, hashKey, hashLen, c); } /* Prune empty containers */ for (hashIndex = apr_hash_first(p, rootSet); hashIndex; hashIndex = apr_hash_next(hashIndex)) { apr_hash_this(hashIndex, (void *) &hashKey, &hashLen, &hashVal); c = (Container *) hashVal; prune_container(c); if (!c->message && !c->child) apr_hash_set(rootSet, hashKey, hashLen, NULL); } /* Merge root set by subjects */ subjectSet = apr_hash_make(p); for (hashIndex = apr_hash_first(p, rootSet); hashIndex; hashIndex = apr_hash_next(hashIndex)) { apr_hash_this(hashIndex, (void *) &hashKey, &hashLen, &hashVal); c = (Container *) hashVal; /* If we don't have a message, our child will. */ if (!c->message) c = c->child; subject = strip_subject(p, c->message); subjectLen = strlen(subject); /* FIXME: Match what JWZ says */ subjectVal = apr_hash_get(subjectSet, subject, subjectLen); if (subjectVal) { if (!c->message) apr_hash_set(subjectSet, subject, strlen(subject), hashVal); else { subjectPair = (Container *) subjectVal; if (!is_reply(c->message) && is_reply(subjectPair->message)) apr_hash_set(subjectSet, subject, strlen(subject), hashVal); } } else apr_hash_set(subjectSet, subject, strlen(subject), hashVal); } /* Subject table now populated */ for (hashIndex = apr_hash_first(p, rootSet); hashIndex; hashIndex = apr_hash_next(hashIndex)) { apr_hash_this(hashIndex, (void *) &hashKey, &hashLen, &hashVal); c = (Container *) hashVal; /* If we don't have a message, our child will. */ if (c->message) subject = strip_subject(p, c->message); else subject = strip_subject(p, c->child->message); subjectLen = strlen(subject); subjectVal = apr_hash_get(subjectSet, subject, subjectLen); subjectPair = (Container *) subjectVal; /* If we need to merge the tables */ if (subjectPair && subjectPair != c) { if (!c->message || !subjectPair->message) { /* One is dummy */ if (!c->message && !subjectPair->message) join_container(subjectPair, c); else if (c->message && !subjectPair->message) { /* It's possible that we're already a child! */ if (c->parent != subjectPair) append_container(subjectPair, c); } else { /* (!c->message && subjectPair->message) */ append_container(c, subjectPair); apr_hash_set(subjectSet, subject, subjectLen, c); delete_from_hash(p, rootSet, subjectPair); } } else { /* Both aren't dummies */ /* We are Reply */ if (is_reply(c->message) && !is_reply(subjectPair->message)) append_container(subjectPair, c); else if (!is_reply(c->message) && is_reply(subjectPair->message)) { append_container(c, subjectPair); apr_hash_set(subjectSet, subject, subjectLen, c); delete_from_hash(p, rootSet, subjectPair); } else { /* We are both replies. */ c = merge_container(p, c, subjectPair); apr_hash_set(subjectSet, subject, subjectLen, c); delete_from_hash(p, rootSet, subjectPair); } } } } /* Now, we are done threading. We want to return a sorted container * back to our caller. All children of the root set need to be in * order and then we need to issue an ordering to the root set. */ tmp = NULL; /* Sort siblings */ for (hashIndex = apr_hash_first(p, subjectSet); hashIndex; hashIndex = apr_hash_next(hashIndex)) { apr_hash_this(hashIndex, (void *) &hashKey, &hashLen, &hashVal); c = (Container *) hashVal; sort_siblings(c); if (tmp) c->next = tmp; tmp = c; } return (Container *) mbox_sort_linked_list(tmp, 3, compare_siblings, NULL, NULL); }