Intial commit
[profile/ivi/w3m.git] / anchor.c
1 /* $Id: anchor.c,v 1.33 2006/04/08 11:33:16 inu Exp $ */
2 #include "fm.h"
3 #include "myctype.h"
4 #include "regex.h"
5
6 #define FIRST_ANCHOR_SIZE 30
7
8 AnchorList *
9 putAnchor(AnchorList *al, char *url, char *target, Anchor **anchor_return,
10           char *referer, char *title, unsigned char key, int line, int pos)
11 {
12     int n, i, j;
13     Anchor *a;
14     BufferPoint bp;
15     if (al == NULL) {
16         al = New(AnchorList);
17         al->anchors = NULL;
18         al->nanchor = al->anchormax = 0;
19         al->acache = -1;
20     }
21     if (al->anchormax == 0) {
22         /* first time; allocate anchor buffer */
23         al->anchors = New_N(Anchor, FIRST_ANCHOR_SIZE);
24         al->anchormax = FIRST_ANCHOR_SIZE;
25     }
26     if (al->nanchor == al->anchormax) { /* need realloc */
27         al->anchormax *= 2;
28         al->anchors = New_Reuse(Anchor, al->anchors, al->anchormax);
29     }
30     bp.line = line;
31     bp.pos = pos;
32     n = al->nanchor;
33     if (!n || bpcmp(al->anchors[n - 1].start, bp) < 0)
34         i = n;
35     else
36         for (i = 0; i < n; i++) {
37             if (bpcmp(al->anchors[i].start, bp) >= 0) {
38                 for (j = n; j > i; j--)
39                     al->anchors[j] = al->anchors[j - 1];
40                 break;
41             }
42         }
43     a = &al->anchors[i];
44     a->url = url;
45     a->target = target;
46     a->referer = referer;
47     a->title = title;
48     a->accesskey = key;
49     a->slave = FALSE;
50     a->start = bp;
51     a->end = bp;
52     al->nanchor++;
53     if (anchor_return)
54         *anchor_return = a;
55     return al;
56 }
57
58
59 Anchor *
60 registerHref(Buffer *buf, char *url, char *target, char *referer, char *title,
61              unsigned char key, int line, int pos)
62 {
63     Anchor *a;
64     buf->href = putAnchor(buf->href, url, target, &a, referer, title, key,
65                           line, pos);
66     return a;
67 }
68
69 Anchor *
70 registerName(Buffer *buf, char *url, int line, int pos)
71 {
72     Anchor *a;
73     buf->name = putAnchor(buf->name, url, NULL, &a, NULL, NULL, '\0', line,
74                           pos);
75     return a;
76 }
77
78 Anchor *
79 registerImg(Buffer *buf, char *url, char *title, int line, int pos)
80 {
81     Anchor *a;
82     buf->img = putAnchor(buf->img, url, NULL, &a, NULL, title, '\0', line,
83                          pos);
84     return a;
85 }
86
87 Anchor *
88 registerForm(Buffer *buf, FormList *flist, struct parsed_tag *tag, int line,
89              int pos)
90 {
91     Anchor *a;
92     FormItemList *fi;
93
94     fi = formList_addInput(flist, tag);
95     if (fi == NULL)
96         return NULL;
97     buf->formitem = putAnchor(buf->formitem, (char *)fi, flist->target, &a,
98                               NULL, NULL, '\0', line, pos);
99     return a;
100 }
101
102 int
103 onAnchor(Anchor *a, int line, int pos)
104 {
105     BufferPoint bp;
106     bp.line = line;
107     bp.pos = pos;
108
109     if (bpcmp(bp, a->start) < 0)
110         return -1;
111     if (bpcmp(a->end, bp) <= 0)
112         return 1;
113     return 0;
114 }
115
116 Anchor *
117 retrieveAnchor(AnchorList *al, int line, int pos)
118 {
119     Anchor *a;
120     size_t b, e;
121     int cmp;
122
123     if (al == NULL || al->nanchor == 0)
124         return NULL;
125
126     if (al->acache < 0 || al->acache >= al->nanchor)
127         al->acache = 0;
128
129     for (b = 0, e = al->nanchor - 1; b <= e; al->acache = (b + e) / 2) {
130         a = &al->anchors[al->acache];
131         cmp = onAnchor(a, line, pos);
132         if (cmp == 0)
133             return a;
134         else if (cmp > 0)
135             b = al->acache + 1;
136         else if (al->acache == 0)
137             return NULL;
138         else
139             e = al->acache - 1;
140     }
141     return NULL;
142 }
143
144 Anchor *
145 retrieveCurrentAnchor(Buffer *buf)
146 {
147     if (buf->currentLine == NULL)
148         return NULL;
149     return retrieveAnchor(buf->href, buf->currentLine->linenumber, buf->pos);
150 }
151
152 Anchor *
153 retrieveCurrentImg(Buffer *buf)
154 {
155     if (buf->currentLine == NULL)
156         return NULL;
157     return retrieveAnchor(buf->img, buf->currentLine->linenumber, buf->pos);
158 }
159
160 Anchor *
161 retrieveCurrentForm(Buffer *buf)
162 {
163     if (buf->currentLine == NULL)
164         return NULL;
165     return retrieveAnchor(buf->formitem,
166                           buf->currentLine->linenumber, buf->pos);
167 }
168
169 Anchor *
170 searchAnchor(AnchorList *al, char *str)
171 {
172     int i;
173     Anchor *a;
174     if (al == NULL)
175         return NULL;
176     for (i = 0; i < al->nanchor; i++) {
177         a = &al->anchors[i];
178         if (a->hseq < 0)
179           continue;
180         if (!strcmp(a->url, str))
181             return a;
182     }
183     return NULL;
184 }
185
186 Anchor *
187 searchURLLabel(Buffer *buf, char *url)
188 {
189     return searchAnchor(buf->name, url);
190 }
191
192 #ifdef USE_NNTP
193 static Anchor *
194 _put_anchor_news(Buffer *buf, char *p1, char *p2, int line, int pos)
195 {
196     Str tmp;
197
198     if (*p1 == '<') {
199         p1++;
200         if (*(p2 - 1) == '>')
201             p2--;
202     }
203     tmp = wc_Str_conv_strict(Strnew_charp_n(p1, p2 - p1), InnerCharset,
204                              buf->document_charset);
205     tmp = Sprintf("news:%s", file_quote(tmp->ptr));
206     return registerHref(buf, tmp->ptr, NULL, NO_REFERER, NULL, '\0', line,
207                         pos);
208 }
209 #endif                          /* USE_NNTP */
210
211 static Anchor *
212 _put_anchor_all(Buffer *buf, char *p1, char *p2, int line, int pos)
213 {
214     Str tmp;
215
216     tmp = wc_Str_conv_strict(Strnew_charp_n(p1, p2 - p1), InnerCharset,
217                              buf->document_charset);
218     return registerHref(buf, url_quote(tmp->ptr), NULL, NO_REFERER, NULL,
219                         '\0', line, pos);
220 }
221
222 static void
223 reseq_anchor0(AnchorList *al, short *seqmap)
224 {
225     int i;
226     Anchor *a;
227
228     if (!al)
229         return;
230
231     for (i = 0; i < al->nanchor; i++) {
232         a = &al->anchors[i];
233         if (a->hseq >= 0) {
234             a->hseq = seqmap[a->hseq];
235         }
236     }
237 }
238
239 /* renumber anchor */
240 static void
241 reseq_anchor(Buffer *buf)
242 {
243     int i, j, n, nmark = (buf->hmarklist) ? buf->hmarklist->nmark : 0;
244     short *seqmap;
245     Anchor *a, *a1;
246     HmarkerList *ml = NULL;
247
248     if (!buf->href)
249         return;
250
251     n = nmark;
252     for (i = 0; i < buf->href->nanchor; i++) {
253         a = &buf->href->anchors[i];
254         if (a->hseq == -2)
255             n++;
256     }
257
258     if (n == nmark)
259         return;
260
261     seqmap = NewAtom_N(short, n);
262
263     for (i = 0; i < n; i++)
264         seqmap[i] = i;
265
266     n = nmark;
267     for (i = 0; i < buf->href->nanchor; i++) {
268         a = &buf->href->anchors[i];
269         if (a->hseq == -2) {
270             a->hseq = n;
271             a1 = closest_next_anchor(buf->href, NULL, a->start.pos,
272                                      a->start.line);
273             a1 = closest_next_anchor(buf->formitem, a1, a->start.pos,
274                                      a->start.line);
275             if (a1 && a1->hseq >= 0) {
276                 seqmap[n] = seqmap[a1->hseq];
277                 for (j = a1->hseq; j < nmark; j++)
278                     seqmap[j]++;
279             }
280             ml = putHmarker(ml, a->start.line, a->start.pos, seqmap[n]);
281             n++;
282         }
283     }
284
285     for (i = 0; i < nmark; i++) {
286         ml = putHmarker(ml, buf->hmarklist->marks[i].line,
287                         buf->hmarklist->marks[i].pos, seqmap[i]);
288     }
289     buf->hmarklist = ml;
290
291     reseq_anchor0(buf->href, seqmap);
292     reseq_anchor0(buf->formitem, seqmap);
293 }
294
295 static char *
296 reAnchorPos(Buffer *buf, Line *l, char *p1, char *p2,
297             Anchor *(*anchorproc) (Buffer *, char *, char *, int, int))
298 {
299     Anchor *a;
300     int spos, epos;
301     int i, hseq = -2;
302
303     spos = p1 - l->lineBuf;
304     epos = p2 - l->lineBuf;
305     for (i = spos; i < epos; i++) {
306         if (l->propBuf[i] & (PE_ANCHOR | PE_FORM))
307             return p2;
308     }
309     for (i = spos; i < epos; i++)
310         l->propBuf[i] |= PE_ANCHOR;
311     while (spos > l->len && l->next && l->next->bpos) {
312         spos -= l->len;
313         epos -= l->len;
314         l = l->next;
315     }
316     while (1) {
317         a = anchorproc(buf, p1, p2, l->linenumber, spos);
318         a->hseq = hseq;
319         if (hseq == -2) {
320             reseq_anchor(buf);
321             hseq = a->hseq;
322         }
323         a->end.line = l->linenumber;
324         if (epos > l->len && l->next && l->next->bpos) {
325             a->end.pos = l->len;
326             spos = 0;
327             epos -= l->len;
328             l = l->next;
329         }
330         else {
331             a->end.pos = epos;
332             break;
333         }
334     }
335     return p2;
336 }
337
338 void
339 reAnchorWord(Buffer *buf, Line *l, int spos, int epos)
340 {
341     reAnchorPos(buf, l, &l->lineBuf[spos], &l->lineBuf[epos], _put_anchor_all);
342 }
343
344 /* search regexp and register them as anchors */
345 /* returns error message if any               */
346 static char *
347 reAnchorAny(Buffer *buf, char *re,
348             Anchor *(*anchorproc) (Buffer *, char *, char *, int, int))
349 {
350     Line *l;
351     char *p = NULL, *p1, *p2;
352
353     if (re == NULL || *re == '\0') {
354         return NULL;
355     }
356     if ((re = regexCompile(re, 1)) != NULL) {
357         return re;
358     }
359     for (l = MarkAllPages ? buf->firstLine : buf->topLine; l != NULL &&
360          (MarkAllPages || l->linenumber < buf->topLine->linenumber + LASTLINE);
361          l = l->next) {
362         if (p && l->bpos)
363             goto next_line;
364         p = l->lineBuf;
365         for (;;) {
366             if (regexMatch(p, &l->lineBuf[l->size] - p, p == l->lineBuf) == 1) {
367                 matchedPosition(&p1, &p2);
368                 p = reAnchorPos(buf, l, p1, p2, anchorproc);
369             }
370             else
371                 break;
372         }
373       next_line:
374         if (MarkAllPages && l->next == NULL && buf->pagerSource &&
375             !(buf->bufferprop & BP_CLOSE))
376             getNextPage(buf, PagerMax);
377     }
378     return NULL;
379 }
380
381 char *
382 reAnchor(Buffer *buf, char *re)
383 {
384     return reAnchorAny(buf, re, _put_anchor_all);
385 }
386
387 #ifdef USE_NNTP
388 char *
389 reAnchorNews(Buffer *buf, char *re)
390 {
391     return reAnchorAny(buf, re, _put_anchor_news);
392 }
393
394 char *
395 reAnchorNewsheader(Buffer *buf)
396 {
397     Line *l;
398     char *p, *p1, *p2;
399     static char *header_mid[] = {
400         "Message-Id:", "References:", "In-Reply-To:", NULL
401     };
402     static char *header_group[] = {
403         "Newsgroups:", NULL
404     };
405     char **header, **q;
406     int i, search = FALSE;
407
408     if (!buf || !buf->firstLine)
409         return NULL;
410     for (i = 0; i <= 1; i++) {
411         if (i == 0) {
412             regexCompile("<[!-;=?-~]+@[a-zA-Z0-9\\.\\-_]+>", 1);
413             header = header_mid;
414         }
415         else {
416             regexCompile("[a-zA-Z0-9\\.\\-_]+", 1);
417             header = header_group;
418         }
419         for (l = buf->firstLine; l != NULL && l->real_linenumber == 0;
420              l = l->next) {
421             if (l->bpos)
422                 continue;
423             p = l->lineBuf;
424             if (!IS_SPACE(*p)) {
425                 search = FALSE;
426                 for (q = header; *q; q++) {
427                     if (!strncasecmp(p, *q, strlen(*q))) {
428                         search = TRUE;
429                         p = strchr(p, ':') + 1;
430                         break;
431                     }
432                 }
433             }
434             if (!search)
435                 continue;
436             for (;;) {
437                 if (regexMatch(p, &l->lineBuf[l->size] - p, p == l->lineBuf)
438                     == 1) {
439                     matchedPosition(&p1, &p2);
440                     p = reAnchorPos(buf, l, p1, p2, _put_anchor_news);
441                 }
442                 else
443                     break;
444             }
445         }
446     }
447     reseq_anchor(buf);
448     return NULL;
449 }
450 #endif                          /* USE_NNTP */
451
452 #define FIRST_MARKER_SIZE 30
453 HmarkerList *
454 putHmarker(HmarkerList *ml, int line, int pos, int seq)
455 {
456     if (ml == NULL) {
457         ml = New(HmarkerList);
458         ml->marks = NULL;
459         ml->nmark = 0;
460         ml->markmax = 0;
461         ml->prevhseq = -1;
462     }
463     if (ml->markmax == 0) {
464         ml->markmax = FIRST_MARKER_SIZE;
465         ml->marks = NewAtom_N(BufferPoint, ml->markmax);
466         bzero(ml->marks, sizeof(BufferPoint) * ml->markmax);
467     }
468     if (seq + 1 > ml->nmark)
469         ml->nmark = seq + 1;
470     if (ml->nmark >= ml->markmax) {
471         ml->markmax = ml->nmark * 2;
472         ml->marks = New_Reuse(BufferPoint, ml->marks, ml->markmax);
473     }
474     ml->marks[seq].line = line;
475     ml->marks[seq].pos = pos;
476     ml->marks[seq].invalid = 0;
477     return ml;
478 }
479
480 Anchor *
481 closest_next_anchor(AnchorList *a, Anchor *an, int x, int y)
482 {
483     int i;
484
485     if (a == NULL || a->nanchor == 0)
486         return an;
487     for (i = 0; i < a->nanchor; i++) {
488         if (a->anchors[i].hseq < 0)
489             continue;
490         if (a->anchors[i].start.line > y ||
491             (a->anchors[i].start.line == y && a->anchors[i].start.pos > x)) {
492             if (an == NULL || an->start.line > a->anchors[i].start.line ||
493                 (an->start.line == a->anchors[i].start.line &&
494                  an->start.pos > a->anchors[i].start.pos))
495                 an = &a->anchors[i];
496         }
497     }
498     return an;
499 }
500
501 Anchor *
502 closest_prev_anchor(AnchorList *a, Anchor *an, int x, int y)
503 {
504     int i;
505
506     if (a == NULL || a->nanchor == 0)
507         return an;
508     for (i = 0; i < a->nanchor; i++) {
509         if (a->anchors[i].hseq < 0)
510             continue;
511         if (a->anchors[i].end.line < y ||
512             (a->anchors[i].end.line == y && a->anchors[i].end.pos <= x)) {
513             if (an == NULL || an->end.line < a->anchors[i].end.line ||
514                 (an->end.line == a->anchors[i].end.line &&
515                  an->end.pos < a->anchors[i].end.pos))
516                 an = &a->anchors[i];
517         }
518     }
519     return an;
520 }
521
522 void
523 shiftAnchorPosition(AnchorList *al, HmarkerList *hl, int line, int pos,
524                     int shift)
525 {
526     Anchor *a;
527     size_t b, e, s = 0;
528     int cmp;
529
530     if (al == NULL || al->nanchor == 0)
531         return;
532
533     s = al->nanchor / 2;
534     for (b = 0, e = al->nanchor - 1; b <= e; s = (b + e + 1) / 2) {
535         a = &al->anchors[s];
536         cmp = onAnchor(a, line, pos);
537         if (cmp == 0)
538             break;
539         else if (cmp > 0)
540             b = s + 1;
541         else if (s == 0)
542             break;
543         else
544             e = s - 1;
545     }
546     for (; s < al->nanchor; s++) {
547         a = &al->anchors[s];
548         if (a->start.line > line)
549             break;
550         if (a->start.pos > pos) {
551             a->start.pos += shift;
552             if (hl->marks[a->hseq].line == line)
553                 hl->marks[a->hseq].pos = a->start.pos;
554         }
555         if (a->end.pos >= pos)
556             a->end.pos += shift;
557     }
558 }
559
560 #ifdef USE_IMAGE
561 void
562 addMultirowsImg(Buffer *buf, AnchorList *al)
563 {
564     int i, j, k, col, ecol, pos;
565     Image *img;
566     Anchor a_img, a_href, a_form, *a;
567     Line *l, *ls;
568
569     if (al == NULL || al->nanchor == 0)
570         return;
571     for (i = 0; i < al->nanchor; i++) {
572         a_img = al->anchors[i];
573         img = a_img.image;
574         if (a_img.hseq < 0 || !img || img->rows <= 1)
575             continue;
576         for (l = buf->firstLine; l != NULL; l = l->next) {
577             if (l->linenumber == img->y)
578                 break;
579         }
580         if (!l)
581             continue;
582         if (a_img.y == a_img.start.line)
583             ls = l;
584         else {
585             for (ls = l; ls != NULL;
586                  ls = (a_img.y < a_img.start.line) ? ls->next : ls->prev) {
587                 if (ls->linenumber == a_img.start.line)
588                     break;
589             }
590             if (!ls)
591                 continue;
592         }
593         a = retrieveAnchor(buf->href, a_img.start.line, a_img.start.pos);
594         if (a)
595             a_href = *a;
596         else
597             a_href.url = NULL;
598         a = retrieveAnchor(buf->formitem, a_img.start.line, a_img.start.pos);
599         if (a)
600             a_form = *a;
601         else
602             a_form.url = NULL;
603         col = COLPOS(ls, a_img.start.pos);
604         ecol = COLPOS(ls, a_img.end.pos);
605         for (j = 0; l && j < img->rows; l = l->next, j++) {
606             if (a_img.start.line == l->linenumber)
607                 continue;
608             pos = columnPos(l, col);
609             a = registerImg(buf, a_img.url, a_img.title, l->linenumber, pos);
610             a->hseq = -a_img.hseq;
611             a->slave = TRUE;
612             a->image = img;
613             a->end.pos = pos + ecol - col;
614             for (k = pos; k < a->end.pos; k++)
615                 l->propBuf[k] |= PE_IMAGE;
616             if (a_href.url) {
617                 a = registerHref(buf, a_href.url, a_href.target,
618                                  a_href.referer, a_href.title,
619                                  a_href.accesskey, l->linenumber, pos);
620                 a->hseq = a_href.hseq;
621                 a->slave = TRUE;
622                 a->end.pos = pos + ecol - col;
623                 for (k = pos; k < a->end.pos; k++)
624                     l->propBuf[k] |= PE_ANCHOR;
625             }
626             if (a_form.url) {
627                 buf->formitem = putAnchor(buf->formitem, a_form.url,
628                                           a_form.target, &a, NULL, NULL, '\0',
629                                           l->linenumber, pos);
630                 a->hseq = a_form.hseq;
631                 a->end.pos = pos + ecol - col;
632             }
633         }
634         img->rows = 0;
635     }
636 }
637 #endif
638
639 void
640 addMultirowsForm(Buffer *buf, AnchorList *al)
641 {
642     int i, j, k, col, ecol, pos;
643     Anchor a_form, *a;
644     FormItemList *fi;
645     Line *l, *ls;
646
647     if (al == NULL || al->nanchor == 0)
648         return;
649     for (i = 0; i < al->nanchor; i++) {
650         a_form = al->anchors[i];
651         al->anchors[i].rows = 1;
652         if (a_form.hseq < 0 || a_form.rows <= 1)
653             continue;
654         for (l = buf->firstLine; l != NULL; l = l->next) {
655             if (l->linenumber == a_form.y)
656                 break;
657         }
658         if (!l)
659             continue;
660         if (a_form.y == a_form.start.line)
661             ls = l;
662         else {
663             for (ls = l; ls != NULL;
664                  ls = (a_form.y < a_form.start.line) ? ls->next : ls->prev) {
665                 if (ls->linenumber == a_form.start.line)
666                     break;
667             }
668             if (!ls)
669                 continue;
670         }
671         fi = (FormItemList *)a_form.url;
672         col = COLPOS(ls, a_form.start.pos);
673         ecol = COLPOS(ls, a_form.end.pos);
674         for (j = 0; l && j < a_form.rows; l = l->next, j++) {
675             pos = columnPos(l, col);
676             if (j == 0) {
677                 buf->hmarklist->marks[a_form.hseq].line = l->linenumber;
678                 buf->hmarklist->marks[a_form.hseq].pos = pos;
679             }
680             if (a_form.start.line == l->linenumber)
681                 continue;
682             buf->formitem = putAnchor(buf->formitem, a_form.url,
683                                       a_form.target, &a, NULL, NULL, '\0',
684                                       l->linenumber, pos);
685             a->hseq = a_form.hseq;
686             a->y = a_form.y;
687             a->end.pos = pos + ecol - col;
688             l->lineBuf[pos - 1] = '[';
689             l->lineBuf[a->end.pos] = ']';
690             for (k = pos; k < a->end.pos; k++)
691                 l->propBuf[k] |= PE_FORM;
692         }
693     }
694 }
695
696 char *
697 getAnchorText(Buffer *buf, AnchorList *al, Anchor *a)
698 {
699     int hseq, i;
700     Line *l;
701     Str tmp = NULL;
702     char *p, *ep;
703
704     if (!a || a->hseq < 0)
705         return NULL;
706     hseq = a->hseq;
707     l = buf->firstLine;
708     for (i = 0; i < al->nanchor; i++) {
709         a = &al->anchors[i];
710         if (a->hseq != hseq)
711             continue;
712         for (; l; l = l->next) {
713             if (l->linenumber == a->start.line)
714                 break;
715         }
716         if (!l)
717             break;
718         p = l->lineBuf + a->start.pos;
719         ep = l->lineBuf + a->end.pos;
720         for (; p < ep && IS_SPACE(*p); p++) ;
721         if (p == ep)
722             continue;
723         if (!tmp)
724             tmp = Strnew_size(ep - p);
725         else
726             Strcat_char(tmp, ' ');
727         Strcat_charp_n(tmp, p, ep - p);
728     }
729     return tmp ? tmp->ptr : NULL;
730 }
731
732 Buffer *
733 link_list_panel(Buffer *buf)
734 {
735     LinkList *l;
736     AnchorList *al;
737     Anchor *a;
738     FormItemList *fi;
739     int i;
740     char *t, *u, *p;
741     ParsedURL pu;
742     /* FIXME: gettextize? */
743     Str tmp = Strnew_charp("<title>Link List</title>\
744 <h1 align=center>Link List</h1>\n");
745
746     if (buf->bufferprop & BP_INTERNAL ||
747         (buf->linklist == NULL && buf->href == NULL && buf->img == NULL)) {
748         return NULL;
749     }
750
751     if (buf->linklist) {
752         Strcat_charp(tmp, "<hr><h2>Links</h2>\n<ol>\n");
753         for (l = buf->linklist; l; l = l->next) {
754             if (l->url) {
755                 parseURL2(l->url, &pu, baseURL(buf));
756                 p = parsedURL2Str(&pu)->ptr;
757                 u = html_quote(p);
758                 if (DecodeURL)
759                     p = html_quote(url_unquote_conv(p, buf->document_charset));
760                 else
761                     p = u;
762             }
763             else
764                 u = p = "";
765             if (l->type == LINK_TYPE_REL)
766                 t = " [Rel]";
767             else if (l->type == LINK_TYPE_REV)
768                 t = " [Rev]";
769             else
770                 t = "";
771             t = Sprintf("%s%s\n", l->title ? l->title : "", t)->ptr;
772             t = html_quote(t);
773             Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
774                            "\n", NULL);
775         }
776         Strcat_charp(tmp, "</ol>\n");
777     }
778
779     if (buf->href) {
780         Strcat_charp(tmp, "<hr><h2>Anchors</h2>\n<ol>\n");
781         al = buf->href;
782         for (i = 0; i < al->nanchor; i++) {
783             a = &al->anchors[i];
784             if (a->hseq < 0 || a->slave)
785                 continue;
786             parseURL2(a->url, &pu, baseURL(buf));
787             p = parsedURL2Str(&pu)->ptr;
788             u = html_quote(p);
789             if (DecodeURL)
790                 p = html_quote(url_unquote_conv(p, buf->document_charset));
791             else
792                 p = u;
793             t = getAnchorText(buf, al, a);
794             t = t ? html_quote(t) : "";
795             Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
796                            "\n", NULL);
797         }
798         Strcat_charp(tmp, "</ol>\n");
799     }
800
801     if (buf->img) {
802         Strcat_charp(tmp, "<hr><h2>Images</h2>\n<ol>\n");
803         al = buf->img;
804         for (i = 0; i < al->nanchor; i++) {
805             a = &al->anchors[i];
806             if (a->slave)
807                 continue;
808             parseURL2(a->url, &pu, baseURL(buf));
809             p = parsedURL2Str(&pu)->ptr;
810             u = html_quote(p);
811             if (DecodeURL)
812                 p = html_quote(url_unquote_conv(p, buf->document_charset));
813             else
814                 p = u;
815             if (a->title && *a->title)
816                 t = html_quote(a->title);
817             else if (DecodeURL)
818                 t = html_quote(url_unquote_conv
819                                (a->url, buf->document_charset));
820             else
821                 t = html_quote(a->url);
822             Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
823                            "\n", NULL);
824             a = retrieveAnchor(buf->formitem, a->start.line, a->start.pos);
825             if (!a)
826                 continue;
827             fi = (FormItemList *)a->url;
828             fi = fi->parent->item;
829             if (fi->parent->method == FORM_METHOD_INTERNAL &&
830                 !Strcmp_charp(fi->parent->action, "map") && fi->value) {
831                 MapList *ml = searchMapList(buf, fi->value->ptr);
832                 ListItem *mi;
833                 MapArea *m;
834                 if (!ml)
835                     continue;
836                 Strcat_charp(tmp, "<br>\n<b>Image map</b>\n<ol>\n");
837                 for (mi = ml->area->first; mi != NULL; mi = mi->next) {
838                     m = (MapArea *) mi->ptr;
839                     if (!m)
840                         continue;
841                     parseURL2(m->url, &pu, baseURL(buf));
842                     p = parsedURL2Str(&pu)->ptr;
843                     u = html_quote(p);
844                     if (DecodeURL)
845                         p = html_quote(url_unquote_conv(p,
846                                                         buf->
847                                                         document_charset));
848                     else
849                         p = u;
850                     if (m->alt && *m->alt)
851                         t = html_quote(m->alt);
852                     else if (DecodeURL)
853                         t = html_quote(url_unquote_conv(m->url,
854                                                         buf->
855                                                         document_charset));
856                     else
857                         t = html_quote(m->url);
858                     Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t,
859                                    "</a><br>", p, "\n", NULL);
860                 }
861                 Strcat_charp(tmp, "</ol>\n");
862             }
863         }
864         Strcat_charp(tmp, "</ol>\n");
865     }
866
867     return loadHTMLString(tmp);
868 }