📄 htanchor.c
字号:
/* HTAnchor.c** HYPERTEXT "ANCHOR" OBJECT**** (c) COPYRIGHT MIT 1995.** Please first read the full copyright statement in the file COPYRIGH.** @(#) $Id: HTAnchor.c,v 1.89 2001/03/06 16:25:09 kahan Exp $**** An anchor represents a region of a hypertext document which is** linked to another anchor in the same or a different document.**** History** Nov 1990 Written in Objective-C for the NeXT browser (TBL)** 24-Oct-1991 (JFG), written in C, browser-independant ** 21-Nov-1991 (JFG), first complete version** 3-May-1995 (HF), Added a lot of methods and other stuff made an object** July 1996 Patch for adding hash of children Michael Farrar*//* Library include files */#include "wwwsys.h"#include "WWWUtil.h"#include "HTFormat.h"#include "HTParse.h"#include "HTMethod.h"#include "HTWWWStr.h"#include "HTAncMan.h" /* Implemented here */#define PARENT_HASH_SIZE HT_XL_HASH_SIZE#define CHILD_HASH_SIZE HT_L_HASH_SIZEPRIVATE HTList **adult_table=0; /* Point to table of lists of all parents *//* ------------------------------------------------------------------------- *//* Creation Methods *//* ------------------------------------------------------------------------- *//*** Do not use "new" by itself outside this module. In order to enforce** consistency, we insist that you furnish more information about the** anchor you are creating : use newWithParent or newWithAddress.*/PRIVATE HTParentAnchor * HTParentAnchor_new (void){ HTParentAnchor *newAnchor; if ((newAnchor = (HTParentAnchor *) HT_CALLOC(1, sizeof (HTParentAnchor))) == NULL) HT_OUTOFMEM("HTParentAnchor_new"); newAnchor->parent = newAnchor; newAnchor->content_type = WWW_UNKNOWN; newAnchor->mainLink.method = METHOD_INVALID; newAnchor->content_length = -1; /* howcome 6 dec 95 */ newAnchor->date = (time_t) -1; newAnchor->expires = (time_t) -1; newAnchor->last_modified = (time_t) -1; newAnchor->age = (time_t) -1; return newAnchor;}PRIVATE HTChildAnchor * HTChildAnchor_new (void){ HTChildAnchor *child; if ((child = (HTChildAnchor *) HT_CALLOC(1, sizeof(HTChildAnchor))) == NULL) HT_OUTOFMEM("HTChildAnchor_new"); return child;}/* Create new or find old child anchor** -----------------------------------**** Me one is for a new anchor being edited into an existing** document. The parent anchor must already exist. All** children without tags (no NAME attribut) points to the same NULL** child.** Children are now hashed for performance reasons. Thanks to** Michael Farrar*/PUBLIC HTChildAnchor * HTAnchor_findChild (HTParentAnchor * parent, const char * tag){ HTChildAnchor * child = NULL; HTList * kids = NULL; if (!parent) { HTTRACE(ANCH_TRACE, "Child Anchor Bad argument\n"); return NULL; } /* Find a hash for this tag (if any) */ { int hash = 0; /* ** If tag is empty then use hash value 0 */ if (tag) { const char * ptr = tag; for(; *ptr; ptr++) hash = (int) ((hash*3 + (*(unsigned char*)ptr)) % CHILD_HASH_SIZE); } if (!parent->children) { if (!(parent->children = (HTList **) HT_CALLOC(CHILD_HASH_SIZE, sizeof(HTList *)))) HT_OUTOFMEM("HTAnchor_findChild"); } if (!parent->children[hash]) parent->children[hash] = HTList_new(); kids = parent->children[hash]; } /* First search list of children to see if tag is already there */ if (tag && *tag) { HTList * cur = kids; while ((child = (HTChildAnchor *) HTList_nextObject(cur))) { if (child->tag && !strcmp(child->tag, tag)) { HTTRACE(ANCH_TRACE, "Child Anchor %p of parent %p with name `%s' already exists.\n" _ (void *) child _ (void *) parent _ tag); return child; } } } /* If not found then create a new child anchor */ child = HTChildAnchor_new(); HTList_addObject(kids, (void *) child); child->parent = parent; if (tag) StrAllocCopy(child->tag, tag); HTTRACE(ANCH_TRACE, "Child Anchor New Anchor %p named `%s' is child of %p\n" _ (void *) child _ tag ? tag : (const char *) "" _ (void *)parent); return child;}/* Create new or find old named anchor** -----------------------------------**** Me one is for a reference which is found in a document, and might** not be already loaded.** Note: You are not guaranteed a new anchor -- you might get an old one,** like with fonts.*/PUBLIC HTAnchor * HTAnchor_findAddress (const char * address){ char *tag = HTParse (address, "", PARSE_VIEW); /* Any tags? */ /* If the address represents a sub-anchor, we recursively load its parent, then we create a child anchor within that document. */ if (*tag) { char *addr = HTParse(address, "", PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION); HTParentAnchor * parent = (HTParentAnchor*) HTAnchor_findAddress(addr); HTChildAnchor * child = HTAnchor_findChild(parent, tag); HT_FREE(addr); HT_FREE(tag); return (HTAnchor *) child; } else { /* Else check whether we have this node */ int hash; const char *p; HTList * adults; HTList *grownups; HTParentAnchor * foundAnchor; char *newaddr = NULL; StrAllocCopy(newaddr, address); /* Get our own copy */ HT_FREE(tag); newaddr = HTSimplify(&newaddr); /* Select list from hash table */ for(p=newaddr, hash=0; *p; p++) hash = (int) ((hash * 3 + (*(unsigned char*)p)) % PARENT_HASH_SIZE); if (!adult_table) { if ((adult_table = (HTList* *) HT_CALLOC(PARENT_HASH_SIZE, sizeof(HTList*))) == NULL) HT_OUTOFMEM("HTAnchor_findAddress"); } if (!adult_table[hash]) adult_table[hash] = HTList_new(); adults = adult_table[hash]; /* Search list for anchor */ grownups = adults; while ((foundAnchor = (HTParentAnchor *) HTList_nextObject(grownups))){ if (!strcmp(foundAnchor->address, newaddr)) { HTTRACE(ANCH_TRACE, "Find Parent. %p with address `%s' already exists.\n" _ (void*) foundAnchor _ newaddr); HT_FREE(newaddr); /* We already have it */ return (HTAnchor *) foundAnchor; } } /* Node not found : create new anchor. */ foundAnchor = HTParentAnchor_new(); foundAnchor->address = newaddr; /* Remember our copy */ HTList_addObject (adults, foundAnchor); HTTRACE(ANCH_TRACE, "Find Parent. %p with hash %d and address `%s' created\n" _ (void*)foundAnchor _ hash _ newaddr); return (HTAnchor *) foundAnchor; }}/* Create or find a child anchor with a possible link** --------------------------------------------------**** Create new anchor with a given parent and possibly** a name, and possibly a link to a _relatively_ named anchor.** All parameters EXCEPT parent can be NULL*/PUBLIC HTChildAnchor * HTAnchor_findChildAndLink (HTParentAnchor * parent, const char * tag, const char * href, HTLinkType ltype){ HTChildAnchor * child = HTAnchor_findChild(parent, tag); if (child && href && *href) { char * relative_to = HTAnchor_expandedAddress((HTAnchor *) parent); char * parsed_address = HTParse(href, relative_to, PARSE_ALL); HTAnchor * dest = HTAnchor_findAddress(parsed_address); HTLink_add((HTAnchor *) child, dest, ltype, METHOD_INVALID); HT_FREE(parsed_address); HT_FREE(relative_to); } return child;}/* ------------------------------------------------------------------------- *//* Link Methods *//* ------------------------------------------------------------------------- *//*** Upgrade the link to the main destination and and downgrade the** current main link to the list*/PUBLIC HTLink * HTAnchor_mainLink (HTAnchor * me){ return me ? &(me->mainLink) : NULL;}PUBLIC BOOL HTAnchor_setMainLink (HTAnchor * me, HTLink * movingLink){ if (!(me && me->links && movingLink && HTList_removeObject(me->links, movingLink))) return NO; else { /* First push current main link onto top of links list */ HTLink * newLink = HTLink_new(); memcpy ((void *) newLink, & me->mainLink, sizeof (HTLink)); HTList_addObject (me->links, newLink); /* Now make movingLink the new main link, and delete it */ memcpy ((void *) &me->mainLink, movingLink, sizeof (HTLink)); HTLink_delete(movingLink); return YES; }}/*** Handling sub links*/PUBLIC HTList * HTAnchor_subLinks (HTAnchor * anchor){ return anchor ? anchor->links : NULL;}PUBLIC BOOL HTAnchor_setSubLinks (HTAnchor * anchor, HTList * list){ if (anchor) { anchor->links = list; return YES; } return NO;}/*** Returns the main destination of this anchor*/PUBLIC HTAnchor * HTAnchor_followMainLink (HTAnchor * me){ return me ? HTLink_destination(&me->mainLink) : NULL;}/*** Returns a link with a given link type or NULL if nothing found*/PUBLIC HTLink * HTAnchor_findLinkType (HTAnchor * me, HTLinkType type){ if (me) { HTLink * link = HTAnchor_mainLink(me); HTList * sublinks = HTAnchor_subLinks(me); if (link && HTLink_type(link) == type) return link; else if (sublinks) { while ((link = (HTLink *) HTList_nextObject (sublinks))) if (HTLink_type(link) == type) return link; } } return NULL;}/* ------------------------------------------------------------------------- *//* Deletion Methods *//* ------------------------------------------------------------------------- *//* Delete an anchor and possibly related things (auto garbage collection)** --------------------------------------------**** The anchor is only deleted if the corresponding document is not loaded.** All outgoing links from parent and children are deleted, and this** anchor is removed from the sources list of all its targets.** We also try to delete the targets whose documents are not loaded.** If this anchor's source list is empty, we delete it and its children.*//* Deletes all the memory allocated in a parent anchor and returns any** hyperdoc object hanging of this anchor*/PRIVATE void * delete_parent (HTParentAnchor * me){ void * doc = me->document; /* Remove link and address information */ if (me->links) { HTList *cur = me->links; HTLink *pres; while ((pres = (HTLink *) HTList_nextObject(cur))) HTLink_delete(pres); HTList_delete(me->links); } /* Remove children */ if (me->children) { int cnt = 0; for (; cnt<CHILD_HASH_SIZE; cnt++) { if (me->children[cnt]) HTList_delete(me->children[cnt]); } HT_FREE(me->children); } HTList_delete (me->sources); HTList_delete (me->variants); HT_FREE(me->physical); HT_FREE(me->address); /* Then remove entity header information (metainformation) */ HTAnchor_clearHeader(me); HT_FREE(me); return doc;}/* Delete a parent anchor and all its children. If a hyperdoc object** is found hanging off the parent anchor then this is returned*/PRIVATE void * delete_family (HTAnchor * me){ HTParentAnchor * parent = NULL; if (!me) { HTTRACE(ANCH_TRACE, "AnchorDelete No anchor found\n"); return NULL; } parent = me->parent; HTTRACE(ANCH_TRACE, "AnchorDelete Remove parent %p and children\n" _ parent); /* Delete children */ if (parent->children) { int cnt = 0; for (; cnt<CHILD_HASH_SIZE; cnt++) { HTList * kids = parent->children[cnt]; if (kids) { HTChildAnchor * child; while ((child=(HTChildAnchor*)HTList_removeLastObject(kids))) { HT_FREE(child->tag); if (child->links) { HTList * cur = child->links; HTLink * pres; while ((pres = (HTLink *) HTList_nextObject(cur))) HTLink_delete(pres); HTList_delete(child->links); } HT_FREE(child); } HTList_delete(kids); parent->children[cnt] = NULL; } } } return delete_parent(parent);}/* DELETE ALL ANCHORS** ------------------** Deletes all anchors and return a list of all the HyperDocs found.** It is for the application to delete any HyperDocs.** If NULL then no hyperdocs are returned** Return YES if OK, else NO*/PUBLIC BOOL HTAnchor_deleteAll (HTList * documents){ int cnt; HTList *cur; if (!adult_table) return NO; for (cnt=0; cnt<PARENT_HASH_SIZE; cnt++) { if ((cur = adult_table[cnt])) { HTParentAnchor *pres; while ((pres = (HTParentAnchor *) HTList_nextObject(cur)) != NULL){ void * doc = delete_family((HTAnchor *) pres); if (doc && documents) HTList_addObject(documents, doc); } } HTList_delete(adult_table[cnt]); } HT_FREE(adult_table); return YES;}/*** Deletes all the metadata associated with anchors but doesn't** delete the anchor link structure itself. This is much safer** than deleting the complete anchor structure as this represents the** complete Web the application has been in touch with*/PUBLIC BOOL HTAnchor_clearAll (HTList * documents){ int cnt; HTList * cur; if (!adult_table) return NO; for (cnt=0; cnt<PARENT_HASH_SIZE; cnt++) { if ((cur = adult_table[cnt])) { HTParentAnchor * pres; while ((pres = (HTParentAnchor *) HTList_nextObject(cur))) { /* Then remove entity header information */ HTAnchor_clearHeader(pres); /* Delete the physical address */ HT_FREE(pres->physical); /* Register if we have a document on this anchor */ if (documents && pres->document) HTList_addObject(documents, pres->document); } } } return YES;}PRIVATE void delete_links (HTAnchor * me){ if (! me) return; /* Recursively try to delete target anchors */ if (me->mainLink.dest) { HTParentAnchor *parent = me->mainLink.dest->parent; HTList_removeObject (parent->sources, me); if (! parent->document) /* Test here to avoid calling overhead */ HTAnchor_delete (parent); } if (me->links) { /* Extra destinations */ HTLink *target; while ((target = (HTLink *) HTList_removeLastObject (me->links))) { HTParentAnchor *parent = target->dest->parent; HTList_removeObject (parent->sources, me); if (! parent->document) /* Test here to avoid calling overhead */ HTAnchor_delete (parent); } }}PUBLIC BOOL HTAnchor_delete (HTParentAnchor * me){ /* Don't delete if document is loaded */ if (!me || me->document) { HTTRACE(ANCH_TRACE, "Anchor...... Not deleted\n"); return NO; } /* Recursively try to delete target anchors */ delete_links ((HTAnchor *) me); if (!HTList_isEmpty(me->sources)) { /* There are still incoming links */ /* ** Delete all outgoing links from children, if any */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -