~ubuntu-branches/ubuntu/edgy/lynx/edgy

« back to all changes in this revision

Viewing changes to WWW/Library/Implementation/HTAnchor.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2004-09-16 12:14:10 UTC
  • Revision ID: james.westby@ubuntu.com-20040916121410-cz1gu92c4nqfeyrg
Tags: upstream-2.8.5
ImportĀ upstreamĀ versionĀ 2.8.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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-independent
 
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 <HTAnchor.h>
 
20
#include <HTParse.h>
 
21
#include <HTString.h>
 
22
#include <UCAux.h>
 
23
#include <UCMap.h>
 
24
 
 
25
#include <GridText.h>
 
26
#include <LYUtils.h>
 
27
#include <LYCharSets.h>
 
28
#include <LYLeaks.h>
 
29
 
 
30
#ifdef NOT_DEFINED
 
31
/*
 
32
 *      This is the hashing function used to determine which list in the
 
33
 *              adult_table a parent anchor should be put in.  This is a
 
34
 *              much simpler function than the original used.
 
35
 */
 
36
#define HASH_FUNCTION(cp_address) ((unsigned short int)strlen(cp_address) *\
 
37
        (unsigned short int)TOUPPER(*cp_address) % HASH_SIZE)
 
38
#endif /* NOT_DEFINED */
 
39
/*
 
40
 *      This is the original function.  We'll use it again. - FM
 
41
 */
 
42
PRIVATE int HASH_FUNCTION ARGS1(
 
43
        CONST char *,   cp_address)
 
44
{
 
45
    int hash;
 
46
    CONST unsigned char *p;
 
47
 
 
48
    for (p = (CONST unsigned char *)cp_address, hash = 0; *p; p++)
 
49
        hash = (int) (hash * 3 + (*(CONST unsigned char *)p)) % HASH_SIZE;
 
50
 
 
51
    return(hash);
 
52
}
 
53
 
 
54
typedef struct _HyperDoc Hyperdoc;
 
55
#ifdef VMS
 
56
struct _HyperDoc {
 
57
        int junk;       /* VMS cannot handle pointers to undefined structs */
 
58
};
 
59
#endif /* VMS */
 
60
 
 
61
/* Table of lists of all parents */
 
62
PRIVATE HTList adult_table[HASH_SIZE] = { {NULL, NULL} };
 
63
 
 
64
 
 
65
/*                              Creation Methods
 
66
**                              ================
 
67
**
 
68
**      Do not use "new" by itself outside this module.  In order to enforce
 
69
**      consistency, we insist that you furnish more information about the
 
70
**      anchor you are creating : use newWithParent or newWithAddress.
 
71
*/
 
72
PRIVATE HTParentAnchor0 * HTParentAnchor0_new ARGS2(
 
73
        CONST char *,   address,
 
74
        short,          hash)
 
75
{
 
76
    HTParentAnchor0 *newAnchor = typecalloc(HTParentAnchor0);
 
77
    if (newAnchor == NULL)
 
78
        outofmem(__FILE__, "HTParentAnchor0_new");
 
79
 
 
80
    newAnchor->parent = newAnchor;      /* self */
 
81
    StrAllocCopy(newAnchor->address, address);
 
82
    newAnchor->adult_hash = hash;
 
83
 
 
84
    return(newAnchor);
 
85
}
 
86
 
 
87
PRIVATE HTParentAnchor * HTParentAnchor_new ARGS1(
 
88
        HTParentAnchor0 *,      parent)
 
89
{
 
90
    HTParentAnchor *newAnchor = typecalloc(HTParentAnchor);
 
91
    if (newAnchor == NULL)
 
92
        outofmem(__FILE__, "HTParentAnchor_new");
 
93
 
 
94
    newAnchor->parent = parent;         /* cross reference */
 
95
    parent->info = newAnchor;           /* cross reference */
 
96
    newAnchor->address = parent->address;  /* copy pointer */
 
97
 
 
98
    newAnchor->isISMAPScript = FALSE;   /* Lynx appends ?0,0 if TRUE. - FM */
 
99
    newAnchor->isHEAD = FALSE;          /* HEAD request if TRUE. - FM */
 
100
    newAnchor->safe = FALSE;            /* Safe. - FM */
 
101
    newAnchor->no_cache = FALSE;        /* no-cache? - FM */
 
102
    newAnchor->inBASE = FALSE;          /* duplicated from HTML.c/h */
 
103
    newAnchor->content_length = 0;      /* Content-Length. - FM */
 
104
    return(newAnchor);
 
105
}
 
106
 
 
107
PRIVATE HTChildAnchor * HTChildAnchor_new ARGS1(
 
108
        HTParentAnchor0 *,      parent)
 
109
{
 
110
    HTChildAnchor *p = typecalloc(HTChildAnchor);
 
111
    if (p == NULL)
 
112
        outofmem(__FILE__, "HTChildAnchor_new");
 
113
 
 
114
    p->parent = parent;         /* parent reference */
 
115
    return p;
 
116
}
 
117
 
 
118
PRIVATE HTChildAnchor * HText_pool_ChildAnchor_new ARGS1(
 
119
        HTParentAnchor *,       parent)
 
120
{
 
121
    HTChildAnchor *p = (HTChildAnchor *)HText_pool_calloc((HText*)(parent->document),
 
122
                                                sizeof(HTChildAnchor));
 
123
    if (p == NULL)
 
124
        outofmem(__FILE__, "HText_pool_ChildAnchor_new");
 
125
 
 
126
    p->parent = parent->parent; /* parent reference */
 
127
    return p;
 
128
}
 
129
 
 
130
#ifdef CASE_INSENSITIVE_ANCHORS
 
131
/* Case insensitive string comparison */
 
132
#define HT_EQUIV(a,b) (TOUPPER(a) == TOUPPER(b))
 
133
#else
 
134
/* Case sensitive string comparison */
 
135
#define HT_EQUIV(a,b) ((a) == (b))
 
136
#endif
 
137
 
 
138
/*      Null-terminated string comparison
 
139
**      ---------------------------------
 
140
** On entry,
 
141
**      s       Points to one string, null terminated
 
142
**      t       points to the other.
 
143
** On exit,
 
144
**      returns YES if the strings are equivalent
 
145
**              NO if they differ.
 
146
*/
 
147
PRIVATE BOOL HTSEquivalent ARGS2(
 
148
        CONST char *,   s,
 
149
        CONST char *,   t)
 
150
{
 
151
    if (s && t) {  /* Make sure they point to something */
 
152
        for (; *s && *t; s++, t++) {
 
153
            if (!HT_EQUIV(*s, *t)) {
 
154
                return(NO);
 
155
            }
 
156
        }
 
157
        return(HT_EQUIV(*s, *t));
 
158
    } else {
 
159
        return(s == t);         /* Two NULLs are equivalent, aren't they ? */
 
160
    }
 
161
}
 
162
 
 
163
/*      Binary string comparison
 
164
**      ------------------------
 
165
** On entry,
 
166
**      s       Points to one bstring
 
167
**      t       points to the other.
 
168
** On exit,
 
169
**      returns YES if the strings are equivalent
 
170
**              NO if they differ.
 
171
*/
 
172
PRIVATE BOOL HTBEquivalent ARGS2(
 
173
        CONST bstring *,        s,
 
174
        CONST bstring *,        t)
 
175
{
 
176
    if (s && t && BStrLen(s) == BStrLen(t)) {
 
177
        int j;
 
178
        int len = BStrLen(s);
 
179
        for (j = 0; j < len; ++j) {
 
180
            if (!HT_EQUIV(BStrData(s)[j], BStrData(t)[j])) {
 
181
                return(NO);
 
182
            }
 
183
        }
 
184
        return(YES);
 
185
    } else {
 
186
        return(s == t);         /* Two NULLs are equivalent, aren't they ? */
 
187
    }
 
188
}
 
189
 
 
190
/*
 
191
 *  Three-way compare function
 
192
 */
 
193
PRIVATE int compare_anchors ARGS2(
 
194
        void *, l,
 
195
        void *, r)
 
196
{
 
197
    CONST char* a = ((HTChildAnchor *)l)->tag;
 
198
    CONST char* b = ((HTChildAnchor *)r)->tag;
 
199
    /* both tags are not NULL */
 
200
 
 
201
#ifdef CASE_INSENSITIVE_ANCHORS
 
202
        return strcasecomp(a, b); /* Case insensitive */
 
203
#else
 
204
        return strcmp(a, b);      /* Case sensitive - FM */
 
205
#endif /* CASE_INSENSITIVE_ANCHORS */
 
206
}
 
207
 
 
208
 
 
209
/*      Create new or find old sub-anchor
 
210
**      ---------------------------------
 
211
**
 
212
**      This one is for a named child.
 
213
**      The parent anchor must already exist.
 
214
*/
 
215
PRIVATE HTChildAnchor * HTAnchor_findNamedChild ARGS2(
 
216
        HTParentAnchor0 *,      parent,
 
217
        CONST char *,           tag)
 
218
{
 
219
    HTChildAnchor *child;
 
220
 
 
221
    if (parent && tag && *tag) {  /* TBL */
 
222
        if (parent->children) {
 
223
            /*
 
224
            **  Parent has children.  Search them.
 
225
            */
 
226
            HTChildAnchor sample;
 
227
            sample.tag = (char*)tag;    /* for compare_anchors() only */
 
228
 
 
229
            child = (HTChildAnchor *)HTBTree_search(parent->children, &sample);
 
230
            if (child != NULL) {
 
231
                CTRACE((tfp, "Child anchor %p of parent %p with name `%s' already exists.\n",
 
232
                                (void *)child, (void *)parent, tag));
 
233
                return(child);
 
234
            }
 
235
        } else {  /* parent doesn't have any children yet : create family */
 
236
            parent->children = HTBTree_new(compare_anchors);
 
237
        }
 
238
 
 
239
        child = HTChildAnchor_new(parent);
 
240
        CTRACE((tfp, "HTAnchor: New Anchor %p named `%s' is child of %p\n",
 
241
                (void *)child,
 
242
                NonNull(tag),
 
243
                (void *)child->parent));
 
244
 
 
245
        StrAllocCopy(child->tag, tag);   /* should be set before HTBTree_add */
 
246
        HTBTree_add(parent->children, child);
 
247
        return(child);
 
248
 
 
249
    } else {
 
250
        CTRACE((tfp, "HTAnchor_findNamedChild called with NULL parent.\n"));
 
251
        return(NULL);
 
252
    }
 
253
 
 
254
}
 
255
 
 
256
/*
 
257
**      This one is for a new unnamed child being edited into an existing
 
258
**      document.  The parent anchor and the document must already exist.
 
259
**      (Just add new unnamed child).
 
260
*/
 
261
PRIVATE HTChildAnchor * HTAnchor_addChild ARGS1(
 
262
        HTParentAnchor *,       parent)
 
263
{
 
264
    HTChildAnchor *child;
 
265
 
 
266
    if (!parent) {
 
267
        CTRACE((tfp, "HTAnchor_addChild called with NULL parent.\n"));
 
268
        return(NULL);
 
269
    }
 
270
 
 
271
    child = HText_pool_ChildAnchor_new(parent);
 
272
    CTRACE((tfp, "HTAnchor: New unnamed Anchor %p is child of %p\n",
 
273
                (void *)child,
 
274
                (void *)child->parent));
 
275
 
 
276
    child->tag = 0;
 
277
    HTList_linkObject(&parent->children_notag, child, &child->_add_children_notag);
 
278
 
 
279
    return(child);
 
280
}
 
281
 
 
282
 
 
283
PRIVATE HTParentAnchor0 * HTAnchor_findAddress_in_adult_table PARAMS((
 
284
        CONST DocAddress *      newdoc));
 
285
 
 
286
PRIVATE BOOL HTAnchor_link PARAMS((
 
287
        HTChildAnchor *         child,
 
288
        HTAnchor *              destination,
 
289
        HTLinkType *            type));
 
290
 
 
291
 
 
292
/*      Create or find a child anchor with a possible link
 
293
**      --------------------------------------------------
 
294
**
 
295
**      Create new anchor with a given parent and possibly
 
296
**      a name, and possibly a link to a _relatively_ named anchor.
 
297
**      (Code originally in ParseHTML.h)
 
298
*/
 
299
PUBLIC HTChildAnchor * HTAnchor_findChildAndLink ARGS4(
 
300
        HTParentAnchor *,       parent, /* May not be 0   */
 
301
        CONST char *,           tag,    /* May be "" or 0 */
 
302
        CONST char *,           href,   /* May be "" or 0 */
 
303
        HTLinkType *,           ltype)  /* May be 0       */
 
304
{
 
305
    HTChildAnchor * child;
 
306
    CTRACE((tfp,"Entered HTAnchor_findChildAndLink:  tag=`%s',%s href=`%s'\n",
 
307
               NonNull(tag),
 
308
               (ltype == HTInternalLink) ? " (internal link)" : "",
 
309
               NonNull(href) ));
 
310
 
 
311
    if (tag && *tag) {
 
312
        child = HTAnchor_findNamedChild(parent->parent, tag);
 
313
    } else {
 
314
        child = HTAnchor_addChild(parent);
 
315
    }
 
316
 
 
317
    if (href && *href) {
 
318
        CONST char *fragment = NULL;
 
319
        HTParentAnchor0 * dest;
 
320
 
 
321
        if (ltype == HTInternalLink && *href == '#') {
 
322
            dest = parent->parent;
 
323
        } else {
 
324
            CONST char *relative_to = (parent->inBASE && *href != '#') ?
 
325
                                parent->content_base : parent->address;
 
326
            DocAddress parsed_doc;
 
327
            parsed_doc.address = HTParse(href, relative_to,
 
328
                                         PARSE_ALL_WITHOUT_ANCHOR);
 
329
 
 
330
            parsed_doc.post_data = NULL;
 
331
            parsed_doc.post_content_type = NULL;
 
332
            if (ltype && parent->post_data && ltype == HTInternalLink) {
 
333
                /* for internal links, find a destination with the same
 
334
                   post data if the source of the link has post data. - kw
 
335
                   Example: LYNXIMGMAP: */
 
336
                parsed_doc.post_data = parent->post_data;
 
337
                parsed_doc.post_content_type = parent->post_content_type;
 
338
            }
 
339
            parsed_doc.bookmark = NULL;
 
340
            parsed_doc.isHEAD = FALSE;
 
341
            parsed_doc.safe = FALSE;
 
342
 
 
343
            dest = HTAnchor_findAddress_in_adult_table(&parsed_doc);
 
344
            FREE(parsed_doc.address);
 
345
        }
 
346
 
 
347
        /*
 
348
        ** [from HTAnchor_findAddress()]
 
349
        ** If the address represents a sub-anchor, we load its parent (above),
 
350
        ** then we create a named child anchor within that parent.
 
351
        */
 
352
        fragment = (*href == '#') ?  href+1 : HTParseAnchor(href);
 
353
 
 
354
        if (*fragment)
 
355
            dest = (HTParentAnchor0 *)HTAnchor_findNamedChild(dest, fragment);
 
356
 
 
357
 
 
358
        if (tag && *tag) {
 
359
            if (child->dest) { /* DUPLICATE_ANCHOR_NAME_WORKAROUND  - kw */
 
360
                CTRACE((tfp,
 
361
                           "*** Duplicate ChildAnchor %p named `%s'",
 
362
                           child, tag));
 
363
                if ((HTAnchor *)dest != child->dest || ltype != child->type) {
 
364
                    CTRACE((tfp,
 
365
                           ", different dest %p or type, creating unnamed child\n",
 
366
                           child->dest));
 
367
                    child = HTAnchor_addChild(parent);
 
368
                }
 
369
            }
 
370
        }
 
371
        HTAnchor_link(child, (HTAnchor *)dest, ltype);
 
372
    }
 
373
    return child;
 
374
}
 
375
 
 
376
 
 
377
/*      Create new or find old parent anchor
 
378
**      ------------------------------------
 
379
**
 
380
**      Me one is for a reference which is found in a document, and might
 
381
**      not be already loaded.
 
382
**      Note: You are not guaranteed a new anchor -- you might get an old one,
 
383
**      like with fonts.
 
384
*/
 
385
PUBLIC HTParentAnchor * HTAnchor_findAddress ARGS1(
 
386
        CONST DocAddress *,     newdoc)
 
387
{
 
388
    /* Anchor tag specified ? */
 
389
    CONST char *tag = HTParseAnchor(newdoc->address);
 
390
 
 
391
    CTRACE((tfp,"Entered HTAnchor_findAddress\n"));
 
392
 
 
393
    /*
 
394
    **  If the address represents a sub-anchor, we load its parent,
 
395
    **  then we create a named child anchor within that parent.
 
396
    */
 
397
    if (*tag) {
 
398
        DocAddress parsed_doc;
 
399
        HTParentAnchor0 * foundParent;
 
400
        HTChildAnchor * foundAnchor;
 
401
 
 
402
        parsed_doc.address = HTParse(newdoc->address, "",
 
403
                                     PARSE_ALL_WITHOUT_ANCHOR);
 
404
        parsed_doc.post_data = newdoc->post_data;
 
405
        parsed_doc.post_content_type = newdoc->post_content_type;
 
406
        parsed_doc.bookmark = newdoc->bookmark;
 
407
        parsed_doc.isHEAD = newdoc->isHEAD;
 
408
        parsed_doc.safe = newdoc->safe;
 
409
 
 
410
        foundParent = HTAnchor_findAddress_in_adult_table(&parsed_doc);
 
411
        foundAnchor = HTAnchor_findNamedChild (foundParent, tag);
 
412
        FREE(parsed_doc.address);
 
413
        return HTAnchor_parent((HTAnchor *)foundParent);
 
414
    }
 
415
    return HTAnchor_parent((HTAnchor *)HTAnchor_findAddress_in_adult_table(newdoc));
 
416
}
 
417
 
 
418
 
 
419
/*  The address has no anchor tag, for sure.
 
420
 */
 
421
PRIVATE HTParentAnchor0 * HTAnchor_findAddress_in_adult_table ARGS1(
 
422
        CONST DocAddress *,     newdoc)
 
423
{
 
424
    /*
 
425
    **  Check whether we have this node.
 
426
    */
 
427
    int hash;
 
428
    HTList * adults;
 
429
    HTList *grownups;
 
430
    HTParentAnchor0 * foundAnchor;
 
431
    BOOL need_extra_info = (newdoc->post_data || newdoc->post_content_type ||
 
432
                newdoc->bookmark || newdoc->isHEAD || newdoc->safe);
 
433
 
 
434
    /*
 
435
     *  We need not free adult_table[] atexit -
 
436
     *  it should be perfectly empty after free'ing all HText's.
 
437
     *  (There is an error if it is not empty at exit). -LP
 
438
     */
 
439
 
 
440
    /*
 
441
    **  Select list from hash table,
 
442
    */
 
443
    hash = HASH_FUNCTION(newdoc->address);
 
444
    adults = &(adult_table[hash]);
 
445
 
 
446
    /*
 
447
    **  Search list for anchor.
 
448
    */
 
449
    grownups = adults;
 
450
    while (NULL != (foundAnchor =
 
451
                    (HTParentAnchor0 *)HTList_nextObject(grownups))) {
 
452
        if (HTSEquivalent(foundAnchor->address, newdoc->address) &&
 
453
 
 
454
            ((!foundAnchor->info && !need_extra_info) ||
 
455
             (foundAnchor->info &&
 
456
              HTBEquivalent(foundAnchor->info->post_data, newdoc->post_data) &&
 
457
              foundAnchor->info->isHEAD == newdoc->isHEAD)))
 
458
        {
 
459
            CTRACE((tfp, "Anchor %p with address `%s' already exists.\n",
 
460
                        (void *)foundAnchor, newdoc->address));
 
461
            return foundAnchor;
 
462
        }
 
463
    }
 
464
 
 
465
    /*
 
466
    **  Node not found: create new anchor.
 
467
    */
 
468
    foundAnchor = HTParentAnchor0_new(newdoc->address, hash);
 
469
    CTRACE((tfp, "New anchor %p has hash %d and address `%s'\n",
 
470
                (void *)foundAnchor, hash, newdoc->address));
 
471
 
 
472
    if (need_extra_info) {
 
473
        /* rare case, create a big structure */
 
474
        HTParentAnchor *p = HTParentAnchor_new(foundAnchor);
 
475
 
 
476
        if (newdoc->post_data)
 
477
            BStrCopy(p->post_data, newdoc->post_data);
 
478
        if (newdoc->post_content_type)
 
479
            StrAllocCopy(p->post_content_type,
 
480
                     newdoc->post_content_type);
 
481
        if (newdoc->bookmark)
 
482
            StrAllocCopy(p->bookmark, newdoc->bookmark);
 
483
        p->isHEAD = newdoc->isHEAD;
 
484
        p->safe = newdoc->safe;
 
485
    }
 
486
    HTList_linkObject(adults, foundAnchor, &foundAnchor->_add_adult);
 
487
 
 
488
    return foundAnchor;
 
489
}
 
490
 
 
491
/*      Create new or find old named anchor - simple form
 
492
**      -------------------------------------------------
 
493
**
 
494
**     Like HTAnchor_findAddress, but simpler to use for simple cases.
 
495
**      No post data etc. can be supplied. - kw
 
496
*/
 
497
PUBLIC HTParentAnchor * HTAnchor_findSimpleAddress ARGS1(
 
498
        CONST char *,   url)
 
499
{
 
500
    DocAddress urldoc;
 
501
 
 
502
    urldoc.address = (char *)url; /* ignore warning, it IS treated like const - kw */
 
503
    urldoc.post_data = NULL;
 
504
    urldoc.post_content_type = NULL;
 
505
    urldoc.bookmark = NULL;
 
506
    urldoc.isHEAD = FALSE;
 
507
    urldoc.safe = FALSE;
 
508
    return HTAnchor_findAddress(&urldoc);
 
509
}
 
510
 
 
511
 
 
512
/*      Link me Anchor to another given one
 
513
**      -------------------------------------
 
514
*/
 
515
PRIVATE BOOL HTAnchor_link ARGS3(
 
516
        HTChildAnchor *,        child,
 
517
        HTAnchor *,             destination,
 
518
        HTLinkType *,           type)
 
519
{
 
520
    if (!(child && destination))
 
521
        return(NO);  /* Can't link to/from non-existing anchor */
 
522
 
 
523
    CTRACE((tfp, "Linking child %p to anchor %p\n", child, destination));
 
524
    if (child->dest) {
 
525
        CTRACE((tfp, "*** child anchor already has destination, exiting!\n"));
 
526
        return(NO);
 
527
    }
 
528
 
 
529
    child->dest = destination;
 
530
    child->type = type;
 
531
 
 
532
    if (child->parent != destination->parent)
 
533
        /* link only foreign children */
 
534
        HTList_linkObject(&destination->parent->sources, child, &child->_add_sources);
 
535
 
 
536
    return(YES);  /* Success */
 
537
}
 
538
 
 
539
 
 
540
/*      Delete an anchor and possibly related things (auto garbage collection)
 
541
**      --------------------------------------------
 
542
**
 
543
**      The anchor is only deleted if the corresponding document is not loaded.
 
544
**      All outgoing links from children are deleted, and children are
 
545
**      removed from the sources lists of theirs targets.
 
546
**      We also try to delete the targets whose documents are not loaded.
 
547
**      If this anchor's sources list is empty, we delete it and its children.
 
548
*/
 
549
 
 
550
/*
 
551
 *      Recursively try to delete destination anchor of this child.
 
552
 *      In any event, this will tell destination anchor that we
 
553
 *      no longer consider it a destination.
 
554
 */
 
555
PRIVATE void deleteLinks ARGS1(
 
556
        HTChildAnchor *,        me)
 
557
{
 
558
    /*
 
559
     *  Unregister me with our destination anchor's parent.
 
560
     */
 
561
    if (me->dest) {
 
562
        HTParentAnchor0 *parent = me->dest->parent;
 
563
 
 
564
        /*
 
565
         *  Start.  Set the dest pointer to zero.
 
566
         */
 
567
         me->dest = NULL;
 
568
 
 
569
        /*
 
570
         *  Remove me from the parent's sources so that the
 
571
         *  parent knows one less anchor is its dest.
 
572
         */
 
573
        if ((me->parent != parent) && !HTList_isEmpty(&parent->sources)) {
 
574
            /*
 
575
             *  Really should only need to deregister once.
 
576
             */
 
577
            HTList_unlinkObject(&parent->sources, (void *)me);
 
578
        }
 
579
 
 
580
        /*
 
581
         *  Recursive call.
 
582
         *  Test here to avoid calling overhead.
 
583
         *  Don't delete if document is loaded or being loaded.
 
584
         */
 
585
        if ((me->parent != parent) && !parent->underway &&
 
586
            (!parent->info || !parent->info->document)) {
 
587
            HTAnchor_delete(parent);
 
588
        }
 
589
 
 
590
        /*
 
591
         *  At this point, we haven't a destination.  Set it to be
 
592
         *  so.
 
593
         *  Leave the HTAtom pointed to by type up to other code to
 
594
         *  handle (reusable, near static).
 
595
         */
 
596
        me->type = NULL;
 
597
    }
 
598
}
 
599
 
 
600
 
 
601
PRIVATE void HTParentAnchor_free PARAMS((
 
602
        HTParentAnchor *        me));
 
603
 
 
604
PUBLIC BOOL HTAnchor_delete ARGS1(
 
605
        HTParentAnchor0 *,      me)
 
606
{
 
607
    /*
 
608
     *  Memory leaks fixed.
 
609
     * 05-27-94 Lynx 2-3-1 Garrett Arch Blythe
 
610
     */
 
611
    HTBTElement *ele;
 
612
    HTChildAnchor *child;
 
613
 
 
614
    /*
 
615
     *  Do nothing if nothing to do.
 
616
     */
 
617
    if (!me) {
 
618
        return(NO);
 
619
    }
 
620
 
 
621
    /*
 
622
     *  Don't delete if document is loaded or being loaded.
 
623
     */
 
624
    if (me->underway || (me->info && me->info->document)) {
 
625
        return(NO);
 
626
    }
 
627
 
 
628
    /*
 
629
     *  Mark ourselves busy, so that recursive calls of this function
 
630
     *  on this HTParentAnchor0 will not free it from under our feet. - kw
 
631
     */
 
632
    me->underway = TRUE;
 
633
 
 
634
    {
 
635
        /*
 
636
         *  Delete all outgoing links from named children.
 
637
         *  Do not delete named children itself (may have incoming links).
 
638
         */
 
639
        if (me->children) {
 
640
            ele = HTBTree_next(me->children, NULL);
 
641
            while (ele != NULL) {
 
642
                child = (HTChildAnchor *)HTBTree_object(ele);
 
643
                if (child->dest)
 
644
                    deleteLinks(child);
 
645
                ele = HTBTree_next(me->children, ele);
 
646
            }
 
647
        }
 
648
    }
 
649
    me->underway = FALSE;
 
650
 
 
651
 
 
652
    /*
 
653
     * There are still incoming links to this one (we are the
 
654
     * destination of another anchor).
 
655
     */
 
656
    if (!HTList_isEmpty(&me->sources)) {
 
657
        /*
 
658
         *  Can't delete parent, still have sources.
 
659
         */
 
660
        return(NO);
 
661
    }
 
662
 
 
663
    /*
 
664
     *  No more incoming and outgoing links : kill everything
 
665
     *  First, delete named children.
 
666
     */
 
667
    if (me->children) {
 
668
        ele = HTBTree_next(me->children, NULL);
 
669
        while (ele != NULL) {
 
670
            child = (HTChildAnchor *)HTBTree_object(ele);
 
671
            FREE(child->tag);
 
672
            FREE(child);
 
673
            ele = HTBTree_next(me->children, ele);
 
674
        }
 
675
        HTBTree_free(me->children);
 
676
    }
 
677
 
 
678
    /*
 
679
     *  Delete the ParentAnchor, if any. (Document was already deleted).
 
680
     */
 
681
    if (me->info) {
 
682
        HTParentAnchor_free(me->info);
 
683
        FREE(me->info);
 
684
    }
 
685
 
 
686
    /*
 
687
     *  Remove ourselves from the hash table's list.
 
688
     */
 
689
    HTList_unlinkObject(&(adult_table[me->adult_hash]), (void *)me);
 
690
 
 
691
    /*
 
692
     *  Free the address.
 
693
     */
 
694
    FREE(me->address);
 
695
 
 
696
    /*
 
697
     *  Finally, kill the parent anchor passed in.
 
698
     */
 
699
    FREE(me);
 
700
 
 
701
    return(YES);
 
702
}
 
703
 
 
704
/*
 
705
 *  Unnamed children (children_notag) have no sence without HText -
 
706
 *  delete them and their links if we are about to free HText.
 
707
 *  Document currently exists. Called within HText_free().
 
708
 */
 
709
PUBLIC void HTAnchor_delete_links ARGS1(
 
710
        HTParentAnchor *,       me)
 
711
{
 
712
    HTList *cur;
 
713
    HTChildAnchor *child;
 
714
 
 
715
    /*
 
716
     *  Do nothing if nothing to do.
 
717
     */
 
718
    if (!me || !me->document) {
 
719
        return;
 
720
    }
 
721
 
 
722
    /*
 
723
     *  Mark ourselves busy, so that recursive calls
 
724
     *  on this HTParentAnchor0 will not free it from under our feet. - kw
 
725
     */
 
726
    me->parent->underway = TRUE;
 
727
 
 
728
    /*
 
729
     *  Delete all outgoing links from unnamed children.
 
730
     */
 
731
    if (!HTList_isEmpty(&me->children_notag)) {
 
732
        cur = &me->children_notag;
 
733
        while ((child =
 
734
                (HTChildAnchor *)HTList_unlinkLastObject(cur)) != 0) {
 
735
            deleteLinks(child);
 
736
            /* child allocated in HText pool, HText_free() will free it later*/
 
737
        }
 
738
    }
 
739
    me->parent->underway = FALSE;
 
740
}
 
741
 
 
742
 
 
743
PRIVATE void HTParentAnchor_free ARGS1(
 
744
        HTParentAnchor *,       me)
 
745
{
 
746
    /*
 
747
     *  Delete the methods list.
 
748
     */
 
749
    if (me->methods) {
 
750
        /*
 
751
         *  Leave what the methods point to up in memory for
 
752
         *  other code (near static stuff).
 
753
         */
 
754
        HTList_delete(me->methods);
 
755
        me->methods = NULL;
 
756
    }
 
757
 
 
758
    /*
 
759
     *  Free up all allocated members.
 
760
     */
 
761
    FREE(me->charset);
 
762
    FREE(me->isIndexAction);
 
763
    FREE(me->isIndexPrompt);
 
764
    FREE(me->title);
 
765
    FREE(me->physical);
 
766
    BStrFree(me->post_data);
 
767
    FREE(me->post_content_type);
 
768
    FREE(me->bookmark);
 
769
    FREE(me->owner);
 
770
    FREE(me->RevTitle);
 
771
    FREE(me->citehost);
 
772
#ifdef USE_SOURCE_CACHE
 
773
    HTAnchor_clearSourceCache(me);
 
774
#endif
 
775
    if (me->FileCache) {
 
776
        FILE *fd;
 
777
        if ((fd = fopen(me->FileCache, "r")) != NULL) {
 
778
            fclose(fd);
 
779
            remove(me->FileCache);
 
780
        }
 
781
        FREE(me->FileCache);
 
782
    }
 
783
    FREE(me->SugFname);
 
784
    FREE(me->cache_control);
 
785
    FREE(me->content_type);
 
786
    FREE(me->content_language);
 
787
    FREE(me->content_encoding);
 
788
    FREE(me->content_base);
 
789
    FREE(me->content_disposition);
 
790
    FREE(me->content_location);
 
791
    FREE(me->content_md5);
 
792
    FREE(me->message_id);
 
793
    FREE(me->subject);
 
794
    FREE(me->date);
 
795
    FREE(me->expires);
 
796
 
 
797
    FREE(me->last_modified);
 
798
    FREE(me->ETag);
 
799
    FREE(me->server);
 
800
#ifdef USE_COLOR_STYLE
 
801
    FREE(me->style);
 
802
#endif
 
803
 
 
804
    /*
 
805
     *  Original code wanted a way to clean out the HTFormat if no
 
806
     *  longer needed (ref count?).  I'll leave it alone since
 
807
     *  those HTAtom objects are a little harder to know where
 
808
     *  they are being referenced all at one time. (near static)
 
809
     */
 
810
 
 
811
    FREE(me->UCStages);
 
812
    ImageMapList_free(me->imaps);
 
813
}
 
814
 
 
815
#ifdef USE_SOURCE_CACHE
 
816
PUBLIC void HTAnchor_clearSourceCache ARGS1(
 
817
        HTParentAnchor *,       me)
 
818
{
 
819
    /*
 
820
     * Clean up the source cache, if any.
 
821
     */
 
822
    if (me->source_cache_file) {
 
823
        CTRACE((tfp, "SourceCache: Removing file %s\n",
 
824
               me->source_cache_file));
 
825
        LYRemoveTemp(me->source_cache_file);
 
826
        FREE(me->source_cache_file);
 
827
    }
 
828
    if (me->source_cache_chunk) {
 
829
        CTRACE((tfp, "SourceCache: Removing memory chunk %p\n",
 
830
               (void *)me->source_cache_chunk));
 
831
        HTChunkFree(me->source_cache_chunk);
 
832
        me->source_cache_chunk = NULL;
 
833
    }
 
834
}
 
835
#endif /* USE_SOURCE_CACHE */
 
836
 
 
837
/*      Data access functions
 
838
**      ---------------------
 
839
*/
 
840
PUBLIC HTParentAnchor * HTAnchor_parent ARGS1(
 
841
        HTAnchor *,     me)
 
842
{
 
843
    if (!me)
 
844
        return NULL;
 
845
 
 
846
    if (me->parent->info)
 
847
        return me->parent->info;
 
848
 
 
849
    /* else: create a new structure */
 
850
    return HTParentAnchor_new(me->parent);
 
851
}
 
852
 
 
853
PUBLIC void HTAnchor_setDocument ARGS2(
 
854
        HTParentAnchor *,       me,
 
855
        HyperDoc *,             doc)
 
856
{
 
857
    if (me)
 
858
        me->document = doc;
 
859
}
 
860
 
 
861
PUBLIC HyperDoc * HTAnchor_document ARGS1(
 
862
        HTParentAnchor *,       me)
 
863
{
 
864
    return( me ? me->document : NULL);
 
865
}
 
866
 
 
867
 
 
868
PUBLIC char * HTAnchor_address ARGS1(
 
869
        HTAnchor *,     me)
 
870
{
 
871
    char *addr = NULL;
 
872
 
 
873
    if (me) {
 
874
        if (((HTParentAnchor0 *)me == me->parent) ||
 
875
            ((HTParentAnchor *)me == me->parent->info) ||
 
876
            !((HTChildAnchor *)me)->tag) {  /* it's an adult or no tag */
 
877
            StrAllocCopy(addr, me->parent->address);
 
878
        } else {  /* it's a named child */
 
879
            HTSprintf0(&addr, "%s#%s",
 
880
                       me->parent->address, ((HTChildAnchor *)me)->tag);
 
881
        }
 
882
    }
 
883
    return(addr);
 
884
}
 
885
 
 
886
PUBLIC void HTAnchor_setFormat ARGS2(
 
887
        HTParentAnchor *,       me,
 
888
        HTFormat,               form)
 
889
{
 
890
    if (me)
 
891
        me->format = form;
 
892
}
 
893
 
 
894
PUBLIC HTFormat HTAnchor_format ARGS1(
 
895
        HTParentAnchor *,       me)
 
896
{
 
897
    return( me ? me->format : NULL);
 
898
}
 
899
 
 
900
PUBLIC void HTAnchor_setIndex ARGS2(
 
901
        HTParentAnchor *,       me,
 
902
        CONST char *,           address)
 
903
{
 
904
    if (me) {
 
905
        me->isIndex = YES;
 
906
        StrAllocCopy(me->isIndexAction, address);
 
907
    }
 
908
}
 
909
 
 
910
PUBLIC void HTAnchor_setPrompt ARGS2(
 
911
        HTParentAnchor *,       me,
 
912
        CONST char *,           prompt)
 
913
{
 
914
    if (me) {
 
915
        StrAllocCopy(me->isIndexPrompt, prompt);
 
916
    }
 
917
}
 
918
 
 
919
PUBLIC BOOL HTAnchor_isIndex ARGS1(
 
920
        HTParentAnchor *,       me)
 
921
{
 
922
    return( me ? me->isIndex : NO);
 
923
}
 
924
 
 
925
/*      Whether Anchor has been designated as an ISMAP link
 
926
**      (normally by presence of an ISMAP attribute on A or IMG) - KW
 
927
*/
 
928
PUBLIC BOOL HTAnchor_isISMAPScript ARGS1(
 
929
        HTAnchor *,     me)
 
930
{
 
931
    return( (me && me->parent->info) ? me->parent->info->isISMAPScript : NO);
 
932
}
 
933
 
 
934
#if defined(USE_COLOR_STYLE)
 
935
/*      Style handling.
 
936
*/
 
937
PUBLIC CONST char * HTAnchor_style ARGS1(
 
938
        HTParentAnchor *,       me)
 
939
{
 
940
        return( me ? me->style : NULL);
 
941
}
 
942
 
 
943
PUBLIC void HTAnchor_setStyle ARGS2(
 
944
        HTParentAnchor *,       me,
 
945
        CONST char *,           style)
 
946
{
 
947
    if (me) {
 
948
        StrAllocCopy(me->style, style);
 
949
    }
 
950
}
 
951
#endif
 
952
 
 
953
 
 
954
/*      Title handling.
 
955
*/
 
956
PUBLIC CONST char * HTAnchor_title ARGS1(
 
957
        HTParentAnchor *,       me)
 
958
{
 
959
    return( me ? me->title : NULL);
 
960
}
 
961
 
 
962
PUBLIC void HTAnchor_setTitle ARGS2(
 
963
        HTParentAnchor *,       me,
 
964
        CONST char *,           title)
 
965
{
 
966
    int i;
 
967
 
 
968
    if (me) {
 
969
        if (title) {
 
970
            StrAllocCopy(me->title, title);
 
971
            for (i = 0; me->title[i]; i++) {
 
972
                if (UCH(me->title[i]) == 1 ||
 
973
                    UCH(me->title[i]) == 2) {
 
974
                    me->title[i] = ' ';
 
975
                }
 
976
            }
 
977
        } else {
 
978
            CTRACE((tfp,"HTAnchor_setTitle: New title is NULL! "));
 
979
            if (me->title) {
 
980
                CTRACE((tfp,"Old title was \"%s\".\n", me->title));
 
981
                FREE(me->title);
 
982
            } else {
 
983
                CTRACE((tfp,"Old title was NULL.\n"));
 
984
            }
 
985
        }
 
986
    }
 
987
}
 
988
 
 
989
PUBLIC void HTAnchor_appendTitle ARGS2(
 
990
        HTParentAnchor *,       me,
 
991
        CONST char *,           title)
 
992
{
 
993
    int i;
 
994
 
 
995
    if (me) {
 
996
        StrAllocCat(me->title, title);
 
997
        for (i = 0; me->title[i]; i++) {
 
998
            if (UCH(me->title[i]) == 1 ||
 
999
                UCH(me->title[i]) == 2) {
 
1000
                me->title[i] = ' ';
 
1001
            }
 
1002
        }
 
1003
    }
 
1004
}
 
1005
 
 
1006
/*      Bookmark handling.
 
1007
*/
 
1008
PUBLIC CONST char * HTAnchor_bookmark ARGS1(
 
1009
        HTParentAnchor *,       me)
 
1010
{
 
1011
    return( me ? me->bookmark : NULL);
 
1012
}
 
1013
 
 
1014
PUBLIC void HTAnchor_setBookmark ARGS2(
 
1015
        HTParentAnchor *,       me,
 
1016
        CONST char *,           bookmark)
 
1017
{
 
1018
    if (me)
 
1019
        StrAllocCopy(me->bookmark, bookmark);
 
1020
}
 
1021
 
 
1022
/*      Owner handling.
 
1023
*/
 
1024
PUBLIC CONST char * HTAnchor_owner ARGS1(
 
1025
        HTParentAnchor *,       me)
 
1026
{
 
1027
    return( me ? me->owner : NULL);
 
1028
}
 
1029
 
 
1030
PUBLIC void HTAnchor_setOwner ARGS2(
 
1031
        HTParentAnchor *,       me,
 
1032
        CONST char *,           owner)
 
1033
{
 
1034
    if (me) {
 
1035
        StrAllocCopy(me->owner, owner);
 
1036
    }
 
1037
}
 
1038
 
 
1039
/*      TITLE handling in LINKs with REV="made" or REV="owner". - FM
 
1040
*/
 
1041
PUBLIC CONST char * HTAnchor_RevTitle ARGS1(
 
1042
        HTParentAnchor *,       me)
 
1043
{
 
1044
    return( me ? me->RevTitle : NULL);
 
1045
}
 
1046
 
 
1047
PUBLIC void HTAnchor_setRevTitle ARGS2(
 
1048
        HTParentAnchor *,       me,
 
1049
        CONST char *,           title)
 
1050
{
 
1051
    int i;
 
1052
 
 
1053
    if (me) {
 
1054
        StrAllocCopy(me->RevTitle, title);
 
1055
        for (i = 0; me->RevTitle[i]; i++) {
 
1056
            if (UCH(me->RevTitle[i]) == 1 ||
 
1057
                UCH(me->RevTitle[i]) == 2) {
 
1058
                me->RevTitle[i] = ' ';
 
1059
            }
 
1060
        }
 
1061
    }
 
1062
}
 
1063
 
 
1064
#ifndef DISABLE_BIBP
 
1065
/*      Citehost for bibp links from LINKs with REL="citehost". - RDC
 
1066
*/
 
1067
PUBLIC CONST char * HTAnchor_citehost ARGS1(
 
1068
        HTParentAnchor *,       me)
 
1069
{
 
1070
    return( me ? me->citehost : NULL);
 
1071
}
 
1072
 
 
1073
PUBLIC void HTAnchor_setCitehost ARGS2(
 
1074
        HTParentAnchor *,       me,
 
1075
        CONST char *,           citehost)
 
1076
{
 
1077
    if (me) {
 
1078
        StrAllocCopy(me->citehost, citehost);
 
1079
    }
 
1080
}
 
1081
#endif /* !DISABLE_BIBP */
 
1082
 
 
1083
/*      Suggested filename handling. - FM
 
1084
**      (will be loaded if we had a Content-Disposition
 
1085
**       header or META element with filename=name.suffix)
 
1086
*/
 
1087
PUBLIC CONST char * HTAnchor_SugFname ARGS1(
 
1088
        HTParentAnchor *,       me)
 
1089
{
 
1090
    return( me ? me->SugFname : NULL);
 
1091
}
 
1092
 
 
1093
/*      Content-Encoding handling. - FM
 
1094
**      (will be loaded if we had a Content-Encoding
 
1095
**       header.)
 
1096
*/
 
1097
PUBLIC CONST char * HTAnchor_content_encoding ARGS1(
 
1098
        HTParentAnchor *,       me)
 
1099
{
 
1100
    return( me ? me->content_encoding : NULL);
 
1101
}
 
1102
 
 
1103
/*      Content-Type handling. - FM
 
1104
*/
 
1105
PUBLIC CONST char * HTAnchor_content_type ARGS1(
 
1106
        HTParentAnchor *,       me)
 
1107
{
 
1108
    return( me ? me->content_type : NULL);
 
1109
}
 
1110
 
 
1111
/*      Last-Modified header handling. - FM
 
1112
*/
 
1113
PUBLIC CONST char * HTAnchor_last_modified ARGS1(
 
1114
        HTParentAnchor *,       me)
 
1115
{
 
1116
    return( me ? me->last_modified : NULL);
 
1117
}
 
1118
 
 
1119
/*      Date header handling. - FM
 
1120
*/
 
1121
PUBLIC CONST char * HTAnchor_date ARGS1(
 
1122
        HTParentAnchor *,       me)
 
1123
{
 
1124
    return( me ? me->date : NULL);
 
1125
}
 
1126
 
 
1127
/*      Server header handling. - FM
 
1128
*/
 
1129
PUBLIC CONST char * HTAnchor_server ARGS1(
 
1130
        HTParentAnchor *,       me)
 
1131
{
 
1132
    return( me ? me->server : NULL);
 
1133
}
 
1134
 
 
1135
/*      Safe header handling. - FM
 
1136
*/
 
1137
PUBLIC BOOL HTAnchor_safe ARGS1(
 
1138
        HTParentAnchor *,       me)
 
1139
{
 
1140
    return (BOOL) ( me ? me->safe : FALSE);
 
1141
}
 
1142
 
 
1143
/*      Content-Base header handling. - FM
 
1144
*/
 
1145
PUBLIC CONST char * HTAnchor_content_base ARGS1(
 
1146
        HTParentAnchor *,       me)
 
1147
{
 
1148
    return( me ? me->content_base : NULL);
 
1149
}
 
1150
 
 
1151
/*      Content-Location header handling. - FM
 
1152
*/
 
1153
PUBLIC CONST char * HTAnchor_content_location ARGS1(
 
1154
        HTParentAnchor *,       me)
 
1155
{
 
1156
    return( me ? me->content_location : NULL);
 
1157
}
 
1158
 
 
1159
/*      Message-ID, used for mail replies - kw
 
1160
*/
 
1161
PUBLIC CONST char * HTAnchor_messageID ARGS1(
 
1162
        HTParentAnchor *,       me)
 
1163
{
 
1164
    return( me ? me->message_id : NULL);
 
1165
}
 
1166
 
 
1167
PUBLIC BOOL HTAnchor_setMessageID ARGS2(
 
1168
        HTParentAnchor *,       me,
 
1169
        CONST char *,           messageid)
 
1170
{
 
1171
    if (!(me && messageid && *messageid)) {
 
1172
        return FALSE;
 
1173
    }
 
1174
    StrAllocCopy(me->message_id, messageid);
 
1175
    return TRUE;
 
1176
}
 
1177
 
 
1178
/*      Subject, used for mail replies - kw
 
1179
*/
 
1180
PUBLIC CONST char * HTAnchor_subject ARGS1(
 
1181
        HTParentAnchor *,       me)
 
1182
{
 
1183
    return( me ? me->subject : NULL);
 
1184
}
 
1185
 
 
1186
PUBLIC BOOL HTAnchor_setSubject ARGS2(
 
1187
        HTParentAnchor *,       me,
 
1188
        CONST char *,           subject)
 
1189
{
 
1190
    if (!(me && subject && *subject)) {
 
1191
        return FALSE;
 
1192
    }
 
1193
    StrAllocCopy(me->subject, subject);
 
1194
    return TRUE;
 
1195
}
 
1196
 
 
1197
/*      Manipulation of links
 
1198
**      ---------------------
 
1199
*/
 
1200
PUBLIC HTAnchor * HTAnchor_followLink ARGS1(
 
1201
        HTChildAnchor *,        me)
 
1202
{
 
1203
    return( me->dest);
 
1204
}
 
1205
 
 
1206
PUBLIC HTAnchor * HTAnchor_followTypedLink ARGS2(
 
1207
        HTChildAnchor *,        me,
 
1208
        HTLinkType *,           type)
 
1209
{
 
1210
    if (me->type == type)
 
1211
        return( me->dest);
 
1212
    return(NULL);  /* No link of me type */
 
1213
}
 
1214
 
 
1215
 
 
1216
/*      Methods List
 
1217
**      ------------
 
1218
*/
 
1219
PUBLIC HTList * HTAnchor_methods ARGS1(
 
1220
        HTParentAnchor *,       me)
 
1221
{
 
1222
    if (!me->methods) {
 
1223
        me->methods = HTList_new();
 
1224
    }
 
1225
    return( me->methods);
 
1226
}
 
1227
 
 
1228
/*      Protocol
 
1229
**      --------
 
1230
*/
 
1231
PUBLIC void * HTAnchor_protocol ARGS1(
 
1232
        HTParentAnchor *,       me)
 
1233
{
 
1234
    return( me->protocol);
 
1235
}
 
1236
 
 
1237
PUBLIC void HTAnchor_setProtocol ARGS2(
 
1238
        HTParentAnchor *,       me,
 
1239
        void*,                  protocol)
 
1240
{
 
1241
    me->protocol = protocol;
 
1242
}
 
1243
 
 
1244
/*      Physical Address
 
1245
**      ----------------
 
1246
*/
 
1247
PUBLIC char * HTAnchor_physical ARGS1(
 
1248
        HTParentAnchor *,       me)
 
1249
{
 
1250
    return( me->physical);
 
1251
}
 
1252
 
 
1253
PUBLIC void HTAnchor_setPhysical ARGS2(
 
1254
        HTParentAnchor *,       me,
 
1255
        char *,                 physical)
 
1256
{
 
1257
    if (me) {
 
1258
        StrAllocCopy(me->physical, physical);
 
1259
    }
 
1260
}
 
1261
 
 
1262
/*
 
1263
**  We store charset info in the HTParentAnchor object, for several
 
1264
**  "stages".  (See UCDefs.h)
 
1265
**  A stream method is supposed to know what stage in the model it is.
 
1266
**
 
1267
**  General model       MIME     ->  parser  ->  structured  ->  HText
 
1268
**  e.g., text/html
 
1269
**      from HTTP:      HTMIME.c ->  SGML.c  ->  HTML.c      ->  GridText.c
 
1270
**     text/plain
 
1271
**      from file:      HTFile.c ->  HTPlain.c               ->  GridText.c
 
1272
**
 
1273
**  The lock/set_by is used to lock e.g. a charset set by an explicit
 
1274
**  HTTP MIME header against overriding by a HTML META tag - the MIME
 
1275
**  header has higher priority.  Defaults (from -assume_.. options etc.)
 
1276
**  will not override charset explicitly given by server.
 
1277
**
 
1278
**  Some advantages of keeping this in the HTAnchor:
 
1279
**  - Global variables are bad.
 
1280
**  - Can remember a charset given by META tag when toggling to SOURCE view.
 
1281
**  - Can remember a charset given by <A CHARSET=...> href in another doc.
 
1282
**
 
1283
**  We don't modify the HTParentAnchor's charset element
 
1284
**  here, that one will only be set when explicitly given.
 
1285
*/
 
1286
PUBLIC LYUCcharset * HTAnchor_getUCInfoStage ARGS2(
 
1287
        HTParentAnchor *,       me,
 
1288
        int,                    which_stage)
 
1289
{
 
1290
    if (me && !me->UCStages) {
 
1291
        int i;
 
1292
        int chndl = UCLYhndl_for_unspec;  /* always >= 0 */
 
1293
        UCAnchorInfo * stages = typecalloc(UCAnchorInfo);
 
1294
        if (stages == NULL)
 
1295
            outofmem(__FILE__, "HTAnchor_getUCInfoStage");
 
1296
        for (i = 0; i < UCT_STAGEMAX; i++) {
 
1297
            stages->s[i].C.MIMEname = "";
 
1298
            stages->s[i].LYhndl = -1;
 
1299
        }
 
1300
        if (me->charset) {
 
1301
            chndl = UCGetLYhndl_byMIME(me->charset);
 
1302
            if (chndl < 0)
 
1303
                chndl = UCLYhndl_for_unrec;
 
1304
            if (chndl < 0)
 
1305
                /*
 
1306
                **  UCLYhndl_for_unrec not defined :-(
 
1307
                **  fallback to UCLYhndl_for_unspec which always valid.
 
1308
                */
 
1309
                chndl = UCLYhndl_for_unspec;  /* always >= 0 */
 
1310
        }
 
1311
        memcpy(&stages->s[UCT_STAGE_MIME].C, &LYCharSet_UC[chndl],
 
1312
               sizeof(LYUCcharset));
 
1313
        stages->s[UCT_STAGE_MIME].lock = UCT_SETBY_DEFAULT;
 
1314
        stages->s[UCT_STAGE_MIME].LYhndl = chndl;
 
1315
        me->UCStages = stages;
 
1316
    }
 
1317
    if (me) {
 
1318
        return( &me->UCStages->s[which_stage].C);
 
1319
    }
 
1320
    return(NULL);
 
1321
}
 
1322
 
 
1323
PUBLIC int HTAnchor_getUCLYhndl ARGS2(
 
1324
        HTParentAnchor *,       me,
 
1325
        int,                    which_stage)
 
1326
{
 
1327
    if (me) {
 
1328
        if (!me->UCStages) {
 
1329
            /*
 
1330
             *  This will allocate and initialize, if not yet done.
 
1331
             */
 
1332
            (void) HTAnchor_getUCInfoStage(me, which_stage);
 
1333
        }
 
1334
        if (me->UCStages->s[which_stage].lock > UCT_SETBY_NONE) {
 
1335
            return( me->UCStages->s[which_stage].LYhndl);
 
1336
        }
 
1337
    }
 
1338
    return( -1);
 
1339
}
 
1340
 
 
1341
#ifdef CAN_SWITCH_DISPLAY_CHARSET
 
1342
PRIVATE void setup_switch_display_charset ARGS2(HTParentAnchor *, me, int, h)
 
1343
{
 
1344
    if (!Switch_Display_Charset(h,SWITCH_DISPLAY_CHARSET_MAYBE))
 
1345
        return;
 
1346
    HTAnchor_setUCInfoStage(me, current_char_set,
 
1347
                            UCT_STAGE_HTEXT, UCT_SETBY_MIME); /* highest priorty! */
 
1348
    HTAnchor_setUCInfoStage(me, current_char_set,
 
1349
                            UCT_STAGE_STRUCTURED, UCT_SETBY_MIME); /* highest priorty! */
 
1350
    CTRACE((tfp, "changing UCInfoStage: HTEXT/STRUCTURED stages charset='%s'.\n",
 
1351
            LYCharSet_UC[current_char_set].MIMEname));
 
1352
}
 
1353
#endif
 
1354
 
 
1355
PUBLIC LYUCcharset * HTAnchor_setUCInfoStage ARGS4(
 
1356
        HTParentAnchor *,       me,
 
1357
        int,                    LYhndl,
 
1358
        int,                    which_stage,
 
1359
        int,                    set_by)
 
1360
{
 
1361
    if (me) {
 
1362
        /*
 
1363
         *  This will allocate and initialize, if not yet done.
 
1364
         */
 
1365
        LYUCcharset * p = HTAnchor_getUCInfoStage(me, which_stage);
 
1366
        /*
 
1367
         *  Can we override?
 
1368
         */
 
1369
        if (set_by >= me->UCStages->s[which_stage].lock) {
 
1370
#ifdef CAN_SWITCH_DISPLAY_CHARSET
 
1371
            int ohandle = me->UCStages->s[which_stage].LYhndl;
 
1372
#endif
 
1373
            me->UCStages->s[which_stage].lock = set_by;
 
1374
            me->UCStages->s[which_stage].LYhndl = LYhndl;
 
1375
            if (LYhndl >= 0) {
 
1376
                memcpy(p, &LYCharSet_UC[LYhndl], sizeof(LYUCcharset));
 
1377
#ifdef CAN_SWITCH_DISPLAY_CHARSET
 
1378
                /* Allow a switch to a more suitable display charset */
 
1379
                if ( LYhndl != ohandle && which_stage == UCT_STAGE_PARSER )
 
1380
                    setup_switch_display_charset(me, LYhndl);
 
1381
#endif
 
1382
            }
 
1383
            else {
 
1384
                p->UChndl = -1;
 
1385
            }
 
1386
            return(p);
 
1387
        }
 
1388
    }
 
1389
    return(NULL);
 
1390
}
 
1391
 
 
1392
PUBLIC LYUCcharset * HTAnchor_resetUCInfoStage ARGS4(
 
1393
        HTParentAnchor *,       me,
 
1394
        int,                    LYhndl,
 
1395
        int,                    which_stage,
 
1396
        int,                    set_by)
 
1397
{
 
1398
    int ohandle;
 
1399
 
 
1400
    if (!me || !me->UCStages)
 
1401
        return(NULL);
 
1402
    me->UCStages->s[which_stage].lock = set_by;
 
1403
    ohandle = me->UCStages->s[which_stage].LYhndl;
 
1404
    me->UCStages->s[which_stage].LYhndl = LYhndl;
 
1405
#ifdef CAN_SWITCH_DISPLAY_CHARSET
 
1406
    /* Allow a switch to a more suitable display charset */
 
1407
    if (LYhndl >= 0 && LYhndl != ohandle && which_stage == UCT_STAGE_PARSER)
 
1408
        setup_switch_display_charset(me, LYhndl);
 
1409
#endif
 
1410
    return( &me->UCStages->s[which_stage].C);
 
1411
}
 
1412
 
 
1413
/*
 
1414
**  A set_by of (-1) means use the lock value from the from_stage.
 
1415
*/
 
1416
PUBLIC LYUCcharset * HTAnchor_copyUCInfoStage ARGS4(
 
1417
        HTParentAnchor *,       me,
 
1418
        int,                    to_stage,
 
1419
        int,                    from_stage,
 
1420
        int,                    set_by)
 
1421
{
 
1422
    if (me) {
 
1423
        /*
 
1424
         *  This will allocate and initialize, if not yet done.
 
1425
         */
 
1426
        LYUCcharset * p_from = HTAnchor_getUCInfoStage(me, from_stage);
 
1427
        LYUCcharset * p_to = HTAnchor_getUCInfoStage(me, to_stage);
 
1428
        /*
 
1429
         *  Can we override?
 
1430
         */
 
1431
        if (set_by == -1)
 
1432
            set_by = me->UCStages->s[from_stage].lock;
 
1433
        if (set_by == UCT_SETBY_NONE)
 
1434
            set_by = UCT_SETBY_DEFAULT;
 
1435
        if (set_by >= me->UCStages->s[to_stage].lock) {
 
1436
#ifdef CAN_SWITCH_DISPLAY_CHARSET
 
1437
            int ohandle = me->UCStages->s[to_stage].LYhndl;
 
1438
#endif
 
1439
            me->UCStages->s[to_stage].lock = set_by;
 
1440
            me->UCStages->s[to_stage].LYhndl =
 
1441
                me->UCStages->s[from_stage].LYhndl;
 
1442
#ifdef CAN_SWITCH_DISPLAY_CHARSET
 
1443
            /* Allow a switch to a more suitable display charset */
 
1444
            if ( me->UCStages->s[to_stage].LYhndl >= 0
 
1445
                 && me->UCStages->s[to_stage].LYhndl != ohandle
 
1446
                 && to_stage == UCT_STAGE_PARSER )
 
1447
                setup_switch_display_charset(me,
 
1448
                                             me->UCStages->s[to_stage].LYhndl);
 
1449
#endif
 
1450
            if (p_to != p_from)
 
1451
                memcpy(p_to, p_from, sizeof(LYUCcharset));
 
1452
            return(p_to);
 
1453
        }
 
1454
    }
 
1455
    return(NULL);
 
1456
}