home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lynx-2.4 / WWW / Library / Implementation / HTAnchor.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-28  |  20.2 KB  |  845 lines

  1. /*    Hypertext "Anchor" Object                HTAnchor.c
  2. **    ==========================
  3. **
  4. ** An anchor represents a region of a hypertext document which is linked to
  5. ** another anchor in the same or a different document.
  6. **
  7. ** History
  8. **
  9. **         Nov 1990  Written in Objective-C for the NeXT browser (TBL)
  10. **    24-Oct-1991 (JFG), written in C, browser-independant 
  11. **    21-Nov-1991 (JFG), first complete version
  12. **
  13. **    (c) Copyright CERN 1991 - See Copyright.html
  14. */
  15.  
  16. #define HASH_SIZE 101        /* Arbitrary prime. Memory/speed tradeoff */
  17.  
  18. #include "HTUtils.h"
  19. #include "tcp.h"
  20. #include <ctype.h>
  21. #include "HTAnchor.h"
  22. #include "HTParse.h"
  23.  
  24. #include "LYLeaks.h"
  25.  
  26. #ifdef NOT_DEFINED
  27. /*
  28.  *    This is the hashing function used to determine which list in the
  29.  *        adult_table a parent anchor should be put in.  This is a
  30.  *        much simpler function than the original used.
  31.  */
  32. #define HASH_FUNCTION(cp_address) ((unsigned short int)strlen(cp_address) *\
  33.     (unsigned short int)TOUPPER(*cp_address) % HASH_SIZE)
  34. #endif /* NOT_DEFINED */
  35. /*
  36.  *    This is the original function.  We'll use it again. - FM
  37.  */ 
  38. PRIVATE int HASH_FUNCTION ARGS1 (CONST char *, cp_address)
  39. {
  40.     int hash;
  41.     unsigned char *p;
  42.  
  43.     for(p=(unsigned char *)cp_address, hash=0; *p; p++)
  44.         hash = (int) (hash * 3 + (*(unsigned char*)p)) % HASH_SIZE;
  45.  
  46.     return hash;
  47. }
  48.  
  49. typedef struct _HyperDoc Hyperdoc;
  50. #ifdef VMS
  51. struct _HyperDoc {
  52.     int junk;    /* VMS cannot handle pointers to undefined structs */
  53. };
  54. #endif
  55.  
  56. PRIVATE HTList **adult_table=0;  /* Point to table of lists of all parents */
  57.  
  58. /*                Creation Methods
  59. **                ================
  60. **
  61. **    Do not use "new" by itself outside this module. In order to enforce
  62. **    consistency, we insist that you furnish more information about the
  63. **    anchor you are creating : use newWithParent or newWithAddress.
  64. */
  65.  
  66. PRIVATE HTParentAnchor * HTParentAnchor_new
  67.   NOARGS
  68. {
  69.   HTParentAnchor *newAnchor = 
  70.     (HTParentAnchor *) calloc (1, sizeof (HTParentAnchor));  /* zero-filled */
  71.   newAnchor->parent = newAnchor;
  72.   newAnchor->isISMAPScript = FALSE; /* Lynx appends ?0,0 if TRUE. - FM */
  73.   return newAnchor;
  74. }
  75.  
  76. PRIVATE HTChildAnchor * HTChildAnchor_new
  77.   NOARGS
  78. {
  79.   return (HTChildAnchor *) calloc (1, sizeof (HTChildAnchor));  /* zero-filled */
  80. }
  81.  
  82.  
  83. /*    Case insensitive string comparison
  84. **    ----------------------------------
  85. ** On entry,
  86. **    s    Points to one string, null terminated
  87. **    t    points to the other.
  88. ** On exit,
  89. **    returns    YES if the strings are equivalent ignoring case
  90. **        NO if they differ in more than  their case.
  91. */
  92.  
  93. PRIVATE BOOL equivalent
  94.   ARGS2 (CONST char *,s, CONST char *,t)
  95. {
  96.   if (s && t) {  /* Make sure they point to something */
  97.     for ( ; *s && *t ; s++, t++) {
  98.         if (TOUPPER(*s) != TOUPPER(*t))
  99.       return NO;
  100.     }
  101.     return TOUPPER(*s) == TOUPPER(*t);
  102.   } else
  103.     return s == t;  /* Two NULLs are equivalent, aren't they ? */
  104. }
  105.  
  106.  
  107. /*    Case sensitive string comparison
  108. **    ----------------------------------
  109. ** On entry,
  110. **    s    Points to one string, null terminated
  111. **    t    points to the other.
  112. ** On exit,
  113. **    returns    YES if the strings are identical or both NULL
  114. **        NO if they differ.
  115. */
  116.  
  117. PRIVATE BOOL identical
  118.   ARGS2 (CONST char *,s, CONST char *,t)
  119. {
  120.   if (s && t) {  /* Make sure they point to something */
  121.     for ( ; *s && *t ; s++, t++) {
  122.         if (*s != *t)
  123.       return NO;
  124.     }
  125.     return *s == *t;
  126.   } else
  127.     return s == t;  /* Two NULLs are identical, aren't they ? */
  128. }
  129.  
  130.  
  131. /*    Create new or find old sub-anchor
  132. **    ---------------------------------
  133. **
  134. **    Me one is for a new anchor being edited into an existing
  135. **    document. The parent anchor must already exist.
  136. */
  137.  
  138. PUBLIC HTChildAnchor * HTAnchor_findChild
  139.   ARGS2 (HTParentAnchor *,parent, CONST char *,tag)
  140. {
  141.   HTChildAnchor *child;
  142.   HTList *kids;
  143.  
  144.   if (! parent) {
  145.     if (TRACE) fprintf(stderr, "HTAnchor_findChild called with NULL parent.\n");
  146.     return NULL;
  147.   }
  148.   if (kids = parent->children) {  /* parent has children : search them */
  149.     if (tag && *tag) {        /* TBL */
  150.     while (NULL != (child=(HTChildAnchor*)HTList_nextObject(kids))) {
  151. #ifdef NOT_DEFINED
  152.         if (equivalent(child->tag, tag)) { /* Case insensitive 920226 */
  153. #endif /* NOT_DEFINED */
  154.         if (identical(child->tag, tag)) { /* Case sensitive - FM */
  155.         if (TRACE) fprintf (stderr,
  156.            "Child anchor %p of parent %p with name `%s' already exists.\n",
  157.             (void*)child, (void*)parent, tag);
  158.         return child;
  159.         }
  160.     }
  161.      }  /*  end if tag is void */
  162.   } else  /* parent doesn't have any children yet : create family */
  163.     parent->children = HTList_new ();
  164.  
  165.   child = HTChildAnchor_new ();
  166.   if (TRACE) fprintf(stderr, "new Anchor %p named `%s' is child of %p\n",
  167.        (void*)child, (int)tag ? tag : (CONST char *)"" , (void*)parent); /* int for apollo */
  168.   HTList_addObject (parent->children, child);
  169.   child->parent = parent;
  170.   StrAllocCopy(child->tag, tag);
  171.   return child;
  172. }
  173.  
  174.  
  175. /*    Create or find a child anchor with a possible link
  176. **    --------------------------------------------------
  177. **
  178. **    Create new anchor with a given parent and possibly
  179. **    a name, and possibly a link to a _relatively_ named anchor.
  180. **    (Code originally in ParseHTML.h)
  181. */
  182. PUBLIC HTChildAnchor * HTAnchor_findChildAndLink
  183.   ARGS4(
  184.        HTParentAnchor *,parent,    /* May not be 0 */
  185.        CONST char *,tag,    /* May be "" or 0 */
  186.        CONST char *,href,    /* May be "" or 0 */
  187.        HTLinkType *,ltype    /* May be 0 */
  188.        )
  189. {
  190.   HTChildAnchor * child = HTAnchor_findChild(parent, tag);
  191.  
  192.   if(TRACE)
  193.       fprintf(stderr,"Entered HTAnchor_findChildAndLink\n");
  194.  
  195.   if (href && *href) {
  196.     char * relative_to = HTAnchor_address((HTAnchor *) parent);
  197.     DocAddress parsed_doc;
  198.     HTAnchor * dest;
  199.  
  200.     parsed_doc.address = HTParse(href, relative_to, PARSE_ALL);
  201.     parsed_doc.post_data = 0;
  202.     parsed_doc.post_content_type = 0;
  203.     dest = HTAnchor_findAddress(&parsed_doc);
  204.  
  205.     HTAnchor_link((HTAnchor *) child, dest, ltype);
  206.     free(parsed_doc.address);
  207.     free(relative_to);
  208.   }
  209.   return child;
  210. }
  211.  
  212.  
  213. /*    Create new or find old named anchor
  214. **    -----------------------------------
  215. **
  216. **    Me one is for a reference which is found in a document, and might
  217. **    not be already loaded.
  218. **    Note: You are not guaranteed a new anchor -- you might get an old one,
  219. **    like with fonts.
  220. */
  221.  
  222. HTAnchor * HTAnchor_findAddress ARGS1 (CONST DocAddress *,newdoc)
  223. {
  224.   char *tag = HTParse (newdoc->address, "", PARSE_ANCHOR);  /* Anchor tag specified ? */
  225.  
  226.   if(TRACE)
  227.     fprintf(stderr,"Entered HTAnchor_findAddress\n");
  228.  
  229.   /* If the address represents a sub-anchor, we recursively load its parent,
  230.      then we create a child anchor within that document. */
  231.   if (*tag) {
  232.     DocAddress parsed_doc;
  233.     HTParentAnchor * foundParent;
  234.     HTChildAnchor * foundAnchor;
  235.  
  236.     parsed_doc.address = HTParse(newdoc->address, "", 
  237.         PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION);
  238.     parsed_doc.post_data = newdoc->post_data;
  239.     parsed_doc.post_content_type = newdoc->post_content_type;
  240.     
  241.     foundParent = (HTParentAnchor *) HTAnchor_findAddress (&parsed_doc);
  242.     foundAnchor = HTAnchor_findChild (foundParent, tag);
  243.     free (parsed_doc.address);
  244.     free (tag);
  245.     return (HTAnchor *) foundAnchor;
  246.   }
  247.   
  248.   else { /* If the address has no anchor tag, 
  249.         check whether we have this node */
  250.     int hash;
  251.     CONST char *p;
  252.     HTList * adults;
  253.     HTList *grownups;
  254.     HTParentAnchor * foundAnchor;
  255.  
  256.     free (tag);
  257.     
  258.     /* Select list from hash table */
  259.     hash = HASH_FUNCTION(newdoc->address);
  260.     if (!adult_table)
  261.         adult_table = (HTList**) calloc(HASH_SIZE, sizeof(HTList*));
  262.     if (!adult_table[hash])
  263.         adult_table[hash] = HTList_new();
  264.     adults = adult_table[hash];
  265.  
  266.     /* Search list for anchor */
  267.     grownups = adults;
  268.     while (NULL != (foundAnchor=(HTParentAnchor*)HTList_nextObject(grownups))) {
  269. #ifdef NOT_DEFINED
  270.        if (equivalent(foundAnchor->address, newdoc->address) &&
  271.        equivalent(foundAnchor->post_data, newdoc->post_data)) {
  272. #endif /* NOT_DEFINED */
  273.        if (identical(foundAnchor->address, newdoc->address) &&
  274.        identical(foundAnchor->post_data, newdoc->post_data)) {
  275.     if (TRACE) fprintf(stderr, "Anchor %p with address `%s' already exists.\n",
  276.               (void*) foundAnchor, newdoc->address);
  277.     return (HTAnchor *) foundAnchor;
  278.       }
  279.     }
  280.     
  281.     /* Node not found : create new anchor */
  282.     foundAnchor = HTParentAnchor_new ();
  283.     if (TRACE) fprintf(stderr, "New anchor %p has hash %d and address `%s'\n",
  284.         (void*)foundAnchor, hash, newdoc->address);
  285.     StrAllocCopy(foundAnchor->address, newdoc->address);
  286.     if(newdoc->post_data)
  287.     StrAllocCopy(foundAnchor->post_data, newdoc->post_data);
  288.     if(newdoc->post_content_type)
  289.     StrAllocCopy(foundAnchor->post_content_type, newdoc->post_content_type);
  290.     HTList_addObject (adults, foundAnchor);
  291.     return (HTAnchor *) foundAnchor;
  292.   }
  293. }
  294.  
  295.  
  296. /*    Delete an anchor and possibly related things (auto garbage collection)
  297. **    --------------------------------------------
  298. **
  299. **    The anchor is only deleted if the corresponding document is not loaded.
  300. **    All outgoing links from parent and children are deleted, and this anchor
  301. **    is removed from the sources list of all its targets.
  302. **    We also try to delete the targets whose documents are not loaded.
  303. **    If this anchor's source list is empty, we delete it and its children.
  304. */
  305.  
  306. PRIVATE void deleteLinks
  307.   ARGS1 (HTAnchor *,me)
  308. {
  309.     /*
  310.      *    Memory leaks fixed.
  311.      *    05-27-94 Lynx 2-3-1 Garrett Arch Blythe
  312.      */
  313.  
  314.     /*
  315.      *    Anchor is NULL, do nothing.
  316.      */
  317.     if(!me)    {
  318.         return;
  319.     }
  320.  
  321.     /*
  322.      *    Unregister me with our mainLink destination anchor's parent.
  323.      */
  324.     if(me->mainLink.dest)    {
  325.         auto HTParentAnchor *parent = me->mainLink.dest->parent;
  326.  
  327.         /*
  328.          *    Remove me from the parent's sources so that the
  329.          *        parent knows one less anchor is it's dest.
  330.          */
  331.         if(!HTList_isEmpty(parent->sources))    {
  332.             /*
  333.              *    Really should only need to deregister once.
  334.              */
  335.             HTList_removeObject(parent->sources, (void *)me);
  336.         }
  337.  
  338.         /*
  339.          *    Test here to avoid calling overhead.
  340.          *    If the parent has no loaded document, then we should
  341.           *        tell it to attempt to delete itself.
  342.          *    Don't do this jass if the anchor passed in is the same
  343.          *        as the anchor to delete.
  344.          *    Also, don't do this if the destination parent is our
  345.          *        parent.
  346.          */
  347.         if(!parent->document && parent != (HTParentAnchor *)me
  348.             && me->parent != parent)    {
  349.             HTAnchor_delete(parent);
  350.         }
  351.  
  352.         /*
  353.          *    At this point, we haven't a mainLink.  Set it to be
  354.          *        so.
  355.          *    Leave the HTAtom pointed to by type up to other code to
  356.          *        handle (reusable, near static).
  357.          */
  358.         me->mainLink.dest = NULL;
  359.         me->mainLink.type = NULL;
  360.     }
  361.  
  362.     /*
  363.      *    Check for extra destinations in our links list.
  364.      */
  365.     if(!HTList_isEmpty(me->links))    {
  366.         auto HTLink *target;
  367.         auto HTParentAnchor *parent;
  368.  
  369.         /*
  370.          *    Take out our extra non mainLinks one by one, calling
  371.          *        their parents to know that they are no longer
  372.          *        the destination of me's anchor.
  373.           */
  374.         while(target = (HTLink *)HTList_removeLastObject(me->links))
  375.         {
  376.             parent = target->dest->parent;
  377.  
  378.             if(!HTList_isEmpty(parent->sources))    {
  379.                 /*
  380.                  *    Only need to tell destination parent
  381.                  *        anchor once.
  382.                  */
  383.                 HTList_removeObject(parent->sources,
  384.                     (void *)me);
  385.             }
  386.  
  387.             /*
  388.              *    Avoid calling overhead.
  389.              *    If the parent hasn't a loaded document, then
  390.              *        we will attempt to have the parent
  391.              *        delete itself.
  392.              *    Don't call twice if this is the same anchor
  393.              *        that we are trying to delete.
  394.              *    Also, don't do this if we are trying to delete
  395.              *        our parent.
  396.              */
  397.             if(!parent->document && (HTParentAnchor *)me
  398.                 != parent && me->parent != parent)    {
  399.                 HTAnchor_delete(parent);
  400.             }
  401.         }
  402.  
  403.         /*
  404.          *    At this point, me no longer has any destination in
  405.          *        the links list.  Get rid of it.
  406.          */
  407.         if(me->links)    {
  408.             HTList_delete(me->links);
  409.             me->links = NULL;
  410.         }
  411.     }
  412.  
  413.     /*
  414.      *    Catch in case links list exists but nothing in it.
  415.      */
  416.     if(me->links)    {
  417.         HTList_delete(me->links);
  418.         me->links = NULL;
  419.     }
  420. }
  421.  
  422. PUBLIC BOOL HTAnchor_delete
  423.   ARGS1 (HTParentAnchor *,me)
  424. {
  425.     /*
  426.      *    Memory leaks fixed.
  427.      *    05-27-94 Lynx 2-3-1 Garrett Arch Blythe
  428.      */
  429.     auto HTChildAnchor *child;
  430.  
  431.     /*
  432.      *    Do nothing if nothing to do.
  433.      */
  434.     if(!me)    {
  435.         return(NO);
  436.     }
  437.  
  438.     /*
  439.      *    Don't delete if document is loaded or being loaded.
  440.      */
  441.     if(me->document || me->underway) {
  442.         return(NO);
  443.     }
  444.  
  445.     /*
  446.      *    Recursively try to delete destination anchors of this parent.
  447.      *    In any event, this will tell all destination anchors that we
  448.      *        no longer consider them a destination.
  449.      */
  450.     deleteLinks((HTAnchor *)me);
  451.  
  452.     /*
  453.      *    There are still incoming links to this one (we are the
  454.      *        destination of another anchor).
  455.      *    Don't actually delete this anchor, but children are OK to
  456.      *        delete their links.
  457.      */
  458.     if(!HTList_isEmpty(me->sources))    {
  459.         /*
  460.          *    Delete all outgoing links from children, do not
  461.          *        delete the children though.
  462.          */
  463.         if(!HTList_isEmpty(me->children))    {
  464.             while(child = (HTChildAnchor *)HTList_removeLastObject(
  465.                 me->children))    {
  466.                 if(child != NULL)    {
  467.                     deleteLinks((HTAnchor *)child);
  468.                 }
  469.             }
  470.         }
  471.  
  472.         /*
  473.          *    Can't delete parent, still have sources.
  474.          */
  475.         return(NO);
  476.     }
  477.  
  478.     /*
  479.      *    No more incoming links : kill everything
  480.      *    First, recursively delete children and their links.
  481.      */
  482.     if(!HTList_isEmpty(me->children))    {
  483.         while(child = (HTChildAnchor *)HTList_removeLastObject(me->
  484.             children))    {
  485.             if(child)    {
  486.                 deleteLinks((HTAnchor *)child);
  487.                 if(child->tag)    {
  488.                     free(child->tag);
  489.                 }
  490.                 free(child);
  491.             }
  492.         }
  493.     }
  494.  
  495.     /*
  496.      *    Delete our empty list of children.
  497.      */
  498.     if(me->children)    {
  499.         HTList_delete(me->children);
  500.     }
  501.     /*
  502.      *    Delete our empty list of sources.
  503.      */
  504.     if(me->sources)    {
  505.         HTList_delete(me->sources);
  506.     }
  507.     /*
  508.      *    Delete the methods list.
  509.      */
  510.     if(me->methods)    {
  511.         /*
  512.           *    Leave what the methods point to up in memory for
  513.          *        other code (near static stuff).
  514.          */
  515.         HTList_delete(me->methods);
  516.     }
  517.  
  518.     /*
  519.      *    Free up all allocated members.
  520.      */
  521.     if(me->isIndexAction)    {
  522.         free(me->isIndexAction);
  523.     }
  524.  
  525.     if(me->isIndexPrompt)    {
  526.         free(me->isIndexPrompt);
  527.     }
  528.  
  529.     if(me->title)    {
  530.         free(me->title);
  531.     }
  532.     if(me->physical)    {
  533.         free(me->physical);
  534.     }
  535.     if(me->post_data)    {
  536.         free(me->post_data);
  537.     }
  538.     if(me->post_content_type)    {
  539.         free(me->post_content_type);
  540.     }
  541.     if(me->owner)    {
  542.         free(me->owner);
  543.     }
  544.  
  545.     /*
  546.      *    Remove ourselves from the hash table's list.
  547.      */
  548.     if(adult_table)    {
  549.         auto unsigned short int usi_hash = HASH_FUNCTION(me->address);
  550.         if(adult_table[usi_hash])    {
  551.             HTList_removeObject(adult_table[usi_hash], (void *)me);
  552.         }
  553.     }
  554.  
  555.     /*
  556.      *    Original code wanted a way to clean out the HTFormat if no
  557.      *        longer needed (ref count?).  I'll leave it alone since
  558.      *        those HTAtom objects are a little harder to know where
  559.      *        they are being referenced all at one time. (near static)
  560.       */
  561.  
  562.     /* free the address too */
  563.     /* LJM 12-5-94 */
  564.     if(me->address)
  565.         free(me->address);
  566.     /*
  567.      *    Finally, kill the parent anchor passed in.
  568.      */
  569.     free(me);
  570. }
  571.  
  572.  
  573. /*        Move an anchor to the head of the list of its siblings
  574. **        ------------------------------------------------------
  575. **
  576. **    This is to ensure that an anchor which might have already existed
  577. **    is put in the correct order as we load the document.
  578. */
  579.  
  580. void HTAnchor_makeLastChild
  581.   ARGS1(HTChildAnchor *,me)
  582. {
  583.   if (me->parent != (HTParentAnchor *) me) {  /* Make sure it's a child */
  584.     HTList * siblings = me->parent->children;
  585.     HTList_removeObject (siblings, me);
  586.     HTList_addObject (siblings, me);
  587.   }
  588. }
  589.  
  590. /*    Data access functions
  591. **    ---------------------
  592. */
  593.  
  594. PUBLIC HTParentAnchor * HTAnchor_parent
  595.   ARGS1 (HTAnchor *,me)
  596. {
  597.   return me ? me->parent : NULL;
  598. }
  599.  
  600. void HTAnchor_setDocument
  601.   ARGS2 (HTParentAnchor *,me, HyperDoc *,doc)
  602. {
  603.   if (me)
  604.     me->document = doc;
  605. }
  606.  
  607. HyperDoc * HTAnchor_document
  608.   ARGS1 (HTParentAnchor *,me)
  609. {
  610.   return me ? me->document : NULL;
  611. }
  612.  
  613.  
  614. /* We don't want code to change an address after anchor creation... yet ?
  615. void HTAnchor_setAddress
  616.   ARGS2 (HTAnchor *,me, char *,addr)
  617. {
  618.   if (me)
  619.     StrAllocCopy (me->parent->address, addr);
  620. }
  621. */
  622.  
  623. char * HTAnchor_address
  624.   ARGS1 (HTAnchor *,me)
  625. {
  626.   char *addr = NULL;
  627.   if (me) {
  628.     if (((HTParentAnchor *) me == me->parent) ||
  629.         !((HTChildAnchor *) me)->tag) {  /* it's an adult or no tag */
  630.       StrAllocCopy (addr, me->parent->address);
  631.     }
  632.     else {  /* it's a named child */
  633.       addr = malloc (2 + strlen (me->parent->address)
  634.              + strlen (((HTChildAnchor *) me)->tag));
  635.       if (addr == NULL) outofmem(__FILE__, "HTAnchor_address");
  636.       sprintf (addr, "%s#%s", me->parent->address,
  637.            ((HTChildAnchor *) me)->tag);
  638.     }
  639.   }
  640.   return addr;
  641. }
  642.  
  643.  
  644.  
  645. void HTAnchor_setFormat
  646.   ARGS2 (HTParentAnchor *,me, HTFormat ,form)
  647. {
  648.   if (me)
  649.     me->format = form;
  650. }
  651.  
  652. HTFormat HTAnchor_format
  653.   ARGS1 (HTParentAnchor *,me)
  654. {
  655.   return me ? me->format : NULL;
  656. }
  657.  
  658.  
  659.  
  660. void HTAnchor_setIndex
  661.   ARGS2 (HTParentAnchor *,me, char *,address)
  662. {
  663.   if (me) {
  664.     me->isIndex = YES;
  665.     StrAllocCopy(me->isIndexAction, address);
  666.   }
  667. }
  668.  
  669. void HTAnchor_setPrompt
  670.   ARGS2 (HTParentAnchor *,me, char *,prompt)
  671. {
  672.   if (me) {
  673.     StrAllocCopy(me->isIndexPrompt, prompt);
  674.   }
  675. }
  676.  
  677. BOOL HTAnchor_isIndex
  678.   ARGS1 (HTParentAnchor *,me)
  679. {
  680.   return me ? me->isIndex : NO;
  681. }
  682.  
  683.  
  684.  
  685. BOOL HTAnchor_hasChildren
  686.   ARGS1 (HTParentAnchor *,me)
  687. {
  688.   return me ? ! HTList_isEmpty(me->children) : NO;
  689. }
  690.  
  691. /*    Title handling
  692. */
  693. CONST char * HTAnchor_title
  694.   ARGS1 (HTParentAnchor *,me)
  695. {
  696.   return me ? me->title : 0;
  697. }
  698.  
  699. void HTAnchor_setTitle
  700.   ARGS2(HTParentAnchor *,me, CONST char *,title)
  701. {
  702.   StrAllocCopy(me->title, title);
  703. }
  704.  
  705. void HTAnchor_appendTitle
  706.   ARGS2(HTParentAnchor *,me, CONST char *,title)
  707. {
  708.   StrAllocCat(me->title, title);
  709. }
  710.  
  711. /*    Owner handling
  712. */
  713. CONST char * HTAnchor_owner
  714.   ARGS1 (HTParentAnchor *,me)
  715. {
  716.   return (me ? me->owner : 0);
  717. }
  718.  
  719. void HTAnchor_setOwner
  720.   ARGS2(HTParentAnchor *,me, CONST char *,owner)
  721. {
  722.   StrAllocCopy(me->owner, owner);
  723. }
  724.  
  725. /*    Link me Anchor to another given one
  726. **    -------------------------------------
  727. */
  728.  
  729. BOOL HTAnchor_link
  730.   ARGS3(HTAnchor *,source, HTAnchor *,destination, HTLinkType *,type)
  731. {
  732.   if (! (source && destination))
  733.     return NO;  /* Can't link to/from non-existing anchor */
  734.   if (TRACE) fprintf(stderr,
  735.                "Linking anchor %p to anchor %p\n", source, destination);
  736.   if (! source->mainLink.dest) {
  737.     source->mainLink.dest = destination;
  738.     source->mainLink.type = type;
  739.   } else {
  740.     HTLink * newLink = (HTLink *) calloc (1, sizeof (HTLink));
  741.     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_link");
  742.     newLink->dest = destination;
  743.     newLink->type = type;
  744.     if (! source->links)
  745.       source->links = HTList_new ();
  746.     HTList_addObject (source->links, newLink);
  747.   }
  748.   if (!destination->parent->sources)
  749.     destination->parent->sources = HTList_new ();
  750.   HTList_addObject (destination->parent->sources, source);
  751.   return YES;  /* Success */
  752. }
  753.  
  754.  
  755. /*    Manipulation of links
  756. **    ---------------------
  757. */
  758.  
  759. HTAnchor * HTAnchor_followMainLink
  760.   ARGS1 (HTAnchor *,me)
  761. {
  762.   return me->mainLink.dest;
  763. }
  764.  
  765. HTAnchor * HTAnchor_followTypedLink
  766.   ARGS2 (HTAnchor *,me, HTLinkType *,type)
  767. {
  768.   if (me->mainLink.type == type)
  769.     return me->mainLink.dest;
  770.   if (me->links) {
  771.     HTList *links = me->links;
  772.     HTLink *link;
  773.     while (NULL != (link=(HTLink*)HTList_nextObject(links)))
  774.       if (link->type == type)
  775.     return link->dest;
  776.   }
  777.   return NULL;  /* No link of me type */
  778. }
  779.  
  780.  
  781. /*    Make main link
  782. */
  783. BOOL HTAnchor_makeMainLink
  784.   ARGS2 (HTAnchor *,me, HTLink *,movingLink)
  785. {
  786.   /* Check that everything's OK */
  787.   if (! (me && HTList_removeObject (me->links, movingLink)))
  788.     return NO;  /* link not found or NULL anchor */
  789.   else {
  790.     /* First push current main link onto top of links list */
  791.     HTLink *newLink = (HTLink*) calloc (1, sizeof (HTLink));
  792.     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_makeMainLink");
  793.     memcpy ((void *)newLink, (CONST char *)&me->mainLink, sizeof (HTLink));
  794.     HTList_addObject (me->links, newLink);
  795.  
  796.     /* Now make movingLink the new main link, and free it */
  797.     memcpy ((void *)&me->mainLink, (CONST void *)movingLink, sizeof (HTLink));
  798.     free (movingLink);
  799.     return YES;
  800.   }
  801. }
  802.  
  803.  
  804. /*    Methods List
  805. **    ------------
  806. */
  807.  
  808. PUBLIC HTList * HTAnchor_methods ARGS1(HTParentAnchor *, me)
  809. {
  810.     if (!me->methods) {
  811.         me->methods = HTList_new();
  812.     }
  813.     return me->methods;
  814. }
  815.  
  816. /*    Protocol
  817. **    --------
  818. */
  819.  
  820. PUBLIC void * HTAnchor_protocol ARGS1(HTParentAnchor *, me)
  821. {
  822.     return me->protocol;
  823. }
  824.  
  825. PUBLIC void HTAnchor_setProtocol ARGS2(HTParentAnchor *, me,
  826.     void*,    protocol)
  827. {
  828.     me->protocol = protocol;
  829. }
  830.  
  831. /*    Physical Address
  832. **    ----------------
  833. */
  834.  
  835. PUBLIC char * HTAnchor_physical ARGS1(HTParentAnchor *, me)
  836. {
  837.     return me->physical;
  838. }
  839.  
  840. PUBLIC void HTAnchor_setPhysical ARGS2(HTParentAnchor *, me,
  841.     char *,    physical)
  842. {
  843.     StrAllocCopy(me->physical, physical);
  844. }
  845.