📄 htanchor.c
字号:
/* Hypertext "Anchor" Object HTAnchor.c** ==========================**** 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-independent** 21-Nov-1991 (JFG), first complete version**** (c) Copyright CERN 1991 - See Copyright.html*/#define HASH_SIZE 101 /* Arbitrary prime. Memory/speed tradeoff */#include <HTUtils.h>#include <HTAnchor.h>#include <HTParse.h>#include <UCAux.h>#include <UCMap.h>#include <LYUtils.h>#include <LYCharSets.h>#include <LYLeaks.h>#ifdef NOT_DEFINED/* * This is the hashing function used to determine which list in the * adult_table a parent anchor should be put in. This is a * much simpler function than the original used. */#define HASH_FUNCTION(cp_address) ((unsigned short int)strlen(cp_address) *\ (unsigned short int)TOUPPER(*cp_address) % HASH_SIZE)#endif /* NOT_DEFINED *//* * This is the original function. We'll use it again. - FM */PRIVATE int HASH_FUNCTION ARGS1( CONST char *, cp_address){ int hash; CONST unsigned char *p; for (p = (CONST unsigned char *)cp_address, hash = 0; *p; p++) hash = (int) (hash * 3 + (*(CONST unsigned char *)p)) % HASH_SIZE; return(hash);}typedef struct _HyperDoc Hyperdoc;#ifdef VMSstruct _HyperDoc { int junk; /* VMS cannot handle pointers to undefined structs */};#endif /* VMS */PRIVATE 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 NOARGS{ HTParentAnchor *newAnchor = typecalloc(HTParentAnchor); if (newAnchor == NULL) outofmem(__FILE__, "HTParentAnchor_new"); newAnchor->parent = newAnchor; newAnchor->bookmark = NULL; /* Bookmark filename. - FM */ newAnchor->isISMAPScript = FALSE; /* Lynx appends ?0,0 if TRUE. - FM */ newAnchor->isHEAD = FALSE; /* HEAD request if TRUE. - FM */ newAnchor->safe = FALSE; /* Safe. - FM */#ifdef SOURCE_CACHE newAnchor->source_cache_file = NULL; newAnchor->source_cache_chunk = NULL;#endif newAnchor->FileCache = NULL; /* Path to a disk-cached copy. - FM */ newAnchor->SugFname = NULL; /* Suggested filename. - FM */ newAnchor->RevTitle = NULL; /* TITLE for a LINK with REV. - FM */ newAnchor->citehost = NULL; /* LINK REL=citehost - RDC */ newAnchor->cache_control = NULL; /* Cache-Control. - FM */ newAnchor->no_cache = FALSE; /* no-cache? - FM */ newAnchor->content_type = NULL; /* Content-Type. - FM */ newAnchor->content_language = NULL; /* Content-Language. - FM */ newAnchor->content_encoding = NULL; /* Compression algorithm. - FM */ newAnchor->content_base = NULL; /* Content-Base. - FM */ newAnchor->content_disposition = NULL; /* Content-Disposition. - FM */ newAnchor->content_location = NULL; /* Content-Location. - FM */ newAnchor->content_md5 = NULL; /* Content-MD5. - FM */ newAnchor->content_length = 0; /* Content-Length. - FM */ newAnchor->date = NULL; /* Date. - FM */ newAnchor->expires = NULL; /* Expires. - FM */ newAnchor->last_modified = NULL; /* Last-Modified. - FM */ newAnchor->ETag = NULL; /* ETag (HTTP/1.1 cache validator) */ newAnchor->server = NULL; /* Server. - FM */ return(newAnchor);}PRIVATE HTChildAnchor * HTChildAnchor_new NOARGS{ HTChildAnchor *p; p = typecalloc(HTChildAnchor); if (p == NULL) outofmem(__FILE__, "HTChildAnchor_new"); return p;}#ifdef CASE_INSENSITIVE_ANCHORS/* Case insensitive string comparison** ----------------------------------** On entry,** s Points to one string, null terminated** t points to the other.** On exit,** returns YES if the strings are equivalent ignoring case** NO if they differ in more than their case.*/PRIVATE BOOL HTEquivalent ARGS2( CONST char *, s, CONST char *, t){ if (s && t) { /* Make sure they point to something */ for (; *s && *t; s++, t++) { if (TOUPPER(*s) != TOUPPER(*t)) { return(NO); } } return( TOUPPER(*s) == TOUPPER(*t)); } else { return(s == t); /* Two NULLs are equivalent, aren't they ? */ }}#else/* Case sensitive string comparison** ----------------------------------** On entry,** s Points to one string, null terminated** t points to the other.** On exit,** returns YES if the strings are identical or both NULL** NO if they differ.*/PRIVATE BOOL HTIdentical ARGS2( CONST char *, s, CONST char *, t){ if (s && t) { /* Make sure they point to something */#ifdef SH_EX /* 1998/04/28 (Tue) 22:02:58 */ if (*s == 'P' || *t == 'P') { if (strcmp(s + 1, "Name") == 0 || strcmp(t + 1, "Name") == 0) return NO; }#endif for (; *s && *t; s++, t++) { if (*s != *t) { return(NO); } } return (BOOL) (*s == *t); } else { return (BOOL) (s == t); /* Two NULLs are identical, aren't they ? */ }}#endif /* CASE_INSENSITIVE_ANCHORS *//* Create new or find old sub-anchor** ---------------------------------**** Me one is for a new anchor being edited into an existing** document. The parent anchor must already exist.*/PUBLIC HTChildAnchor * HTAnchor_findChild ARGS2( HTParentAnchor *, parent, CONST char *, tag){ HTChildAnchor *child; HTList *kids; if (!parent) { CTRACE((tfp, "HTAnchor_findChild called with NULL parent.\n")); return(NULL); } if ((kids = parent->children) != 0) { /* ** Parent has children. Search them. */ if (tag && *tag) { /* TBL */ while (NULL != (child=(HTChildAnchor *)HTList_nextObject(kids))) {#ifdef CASE_INSENSITIVE_ANCHORS if (HTEquivalent(child->tag, tag)) /* Case insensitive */#else if (HTIdentical(child->tag, tag)) /* Case sensitive - FM */#endif /* CASE_INSENSITIVE_ANCHORS */ { CTRACE((tfp, "Child anchor %p of parent %p with name `%s' already exists.\n", (void *)child, (void *)parent, tag)); return(child); } } } /* end if tag is void */ } else { /* parent doesn't have any children yet : create family */ parent->children = HTList_new(); } child = HTChildAnchor_new(); CTRACE((tfp, "HTAnchor: New Anchor %p named `%s' is child of %p\n", (void *)child, tag ? tag : (CONST char *)"", (void *)parent)); /* int for apollo */ HTList_addObject (parent->children, child); child->parent = parent; StrAllocCopy(child->tag, tag); return(child);}/* 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.** (Code originally in ParseHTML.h)*/PUBLIC HTChildAnchor * HTAnchor_findChildAndLink ARGS4( HTParentAnchor *, parent, /* May not be 0 */ CONST char *, tag, /* May be "" or 0 */ CONST char *, href, /* May be "" or 0 */ HTLinkType *, ltype) /* May be 0 */{ HTChildAnchor * child = HTAnchor_findChild(parent, tag); CTRACE((tfp,"Entered HTAnchor_findChildAndLink\n")); if (href && *href) { char *relative_to = HTAnchor_address((HTAnchor *)parent); DocAddress parsed_doc; HTAnchor * dest; parsed_doc.address = HTParse(href, relative_to, PARSE_ALL);#ifndef DONT_TRACK_INTERNAL_LINKS if (ltype && parent->post_data && ltype == LINK_INTERNAL) { /* for internal links, find a destination with the same post data if the source of the link has post data. - kw */ parsed_doc.post_data = parent->post_data; parsed_doc.post_content_type = parent->post_content_type; } else#endif { parsed_doc.post_data = NULL; parsed_doc.post_content_type = NULL; } parsed_doc.bookmark = NULL; parsed_doc.isHEAD = FALSE; parsed_doc.safe = FALSE; dest = HTAnchor_findAddress(&parsed_doc);#define DUPLICATE_ANCHOR_NAME_WORKAROUND#ifdef DUPLICATE_ANCHOR_NAME_WORKAROUND if (tag && *tag) { HTAnchor *testdest1; int child_links; testdest1 = child->mainLink.dest; if (testdest1) { child_links = 1 + HTList_count(child->links); CTRACE((tfp, "*** Duplicate ChildAnchor %p named `%s' with %d links", child, tag, child_links)); if (dest == testdest1 && ltype == child->mainLink.type) { CTRACE((tfp,", same dest %p and type, keeping it\n", testdest1)); } else { CTRACE((tfp,", different dest %p, creating unnamed child\n", testdest1)); child = HTAnchor_findChild(parent, 0); } } }#endif HTAnchor_link((HTAnchor *)child, dest, ltype); FREE(parsed_doc.address); FREE(relative_to); } return(child);}#ifdef LY_FIND_LEAKS/*** Function for freeing the adult hash table. - FM*/PRIVATE void free_adult_table NOARGS{ int i_counter; HTList * HTAp_freeme; HTParentAnchor * parent; /* * Loop through all lists. */ for (i_counter = 0; i_counter < HASH_SIZE; i_counter++) { /* ** Loop through the list. */ while (adult_table[i_counter] != NULL) { /* ** Free off items - FM */ HTAp_freeme = adult_table[i_counter]; adult_table[i_counter] = HTAp_freeme->next; if (HTAp_freeme->object) { parent = (HTParentAnchor *)HTAp_freeme->object; CTRACE((tfp, "delete anchor:%d/%d,%d,%d %s\n", i_counter, HTList_count(HTAp_freeme) + 1, (parent->physical ? 1 : 0), (int)parent->underway, (parent->address ? parent->address : "(no address)"))); parent->underway = FALSE; HTAnchor_delete(parent); } FREE(HTAp_freeme); } } FREE(adult_table);}#endif /* LY_FIND_LEAKS *//* 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 ARGS1( CONST DocAddress *, newdoc){ /* Anchor tag specified ? */ char *tag = HTParse(newdoc->address, "", PARSE_ANCHOR); CTRACE((tfp,"Entered HTAnchor_findAddress\n")); /* ** If the address represents a sub-anchor, we recursively load its ** parent, then we create a child anchor within that document. */ if (*tag) { DocAddress parsed_doc; HTParentAnchor * foundParent; HTChildAnchor * foundAnchor; parsed_doc.address = HTParse(newdoc->address, "", PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION); parsed_doc.post_data = newdoc->post_data; parsed_doc.post_content_type = newdoc->post_content_type; parsed_doc.bookmark = newdoc->bookmark; parsed_doc.isHEAD = newdoc->isHEAD; parsed_doc.safe = newdoc->safe; foundParent = (HTParentAnchor *)HTAnchor_findAddress(&parsed_doc); foundAnchor = HTAnchor_findChild (foundParent, tag); FREE(parsed_doc.address); FREE(tag); return (HTAnchor *)foundAnchor; } else { /* ** If the address has no anchor tag, ** check whether we have this node. */ int hash; HTList * adults; HTList *grownups; HTParentAnchor * foundAnchor; FREE(tag); /* ** Select list from hash table, */ hash = HASH_FUNCTION(newdoc->address); if (!adult_table) { adult_table = typecallocn(HTList *, HASH_SIZE); if (!adult_table) outofmem(__FILE__, "HTAnchor_findAddress");#ifdef LY_FIND_LEAKS atexit(free_adult_table);#endif } if (!adult_table[hash]) adult_table[hash] = HTList_new(); adults = adult_table[hash]; /* ** Search list for anchor. */ grownups = adults; while (NULL != (foundAnchor = (HTParentAnchor *)HTList_nextObject(grownups))) {#ifdef CASE_INSENSITIVE_ANCHORS if (HTEquivalent(foundAnchor->address, newdoc->address) && HTEquivalent(foundAnchor->post_data, newdoc->post_data) && foundAnchor->isHEAD == newdoc->isHEAD)#else if (HTIdentical(foundAnchor->address, newdoc->address) && HTIdentical(foundAnchor->post_data, newdoc->post_data) && foundAnchor->isHEAD == newdoc->isHEAD)#endif /* CASE_INSENSITIVE_ANCHORS */ { CTRACE((tfp, "Anchor %p with address `%s' already exists.\n", (void *)foundAnchor, newdoc->address)); return (HTAnchor *)foundAnchor; } } /* ** Node not found: create new anchor. */ foundAnchor = HTParentAnchor_new(); CTRACE((tfp, "New anchor %p has hash %d and address `%s'\n", (void *)foundAnchor, hash, newdoc->address)); StrAllocCopy(foundAnchor->address, newdoc->address); if (newdoc->post_data) StrAllocCopy(foundAnchor->post_data, newdoc->post_data); if (newdoc->post_content_type) StrAllocCopy(foundAnchor->post_content_type, newdoc->post_content_type); if (newdoc->bookmark) StrAllocCopy(foundAnchor->bookmark, newdoc->bookmark); foundAnchor->isHEAD = newdoc->isHEAD; foundAnchor->safe = newdoc->safe; HTList_addObject (adults, foundAnchor); return (HTAnchor *)foundAnchor; }}/* Create new or find old named anchor - simple form** -------------------------------------------------**** Like the previous one, but simpler to use for simple cases.** No post data etc. can be supplied. - kw*/PUBLIC HTAnchor * HTAnchor_findSimpleAddress ARGS1( CONST char *, url){ DocAddress urldoc; urldoc.address = (char *)url; /* ignore warning, it IS treated like const - kw */ urldoc.post_data = NULL; urldoc.post_content_type = NULL; urldoc.bookmark = NULL; urldoc.isHEAD = FALSE; urldoc.safe = FALSE; return HTAnchor_findAddress(&urldoc);}/* 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.*/PRIVATE void deleteLinks ARGS1( HTAnchor *, me){ /* * Memory leaks fixed. * 05-27-94 Lynx 2-3-1 Garrett Arch Blythe */ /* * Anchor is NULL, do nothing. */ if (!me) { return; } /* * Unregister me with our mainLink destination anchor's parent. */ if (me->mainLink.dest) { HTParentAnchor *parent = me->mainLink.dest->parent; /* * Set the mainLink pointer to zero NOW. If we don't, * and we get somehow called recursively again for this * same old me during the HTAnchor_delete below, weird * things can occasionally happen. - kw */ me->mainLink.dest = NULL; /* * Remove me from the parent's sources so that the * parent knows one less anchor is it's dest. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -