w3m

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/w3m.git/
Log | Files | Refs | README

anchor.c (19858B)


      1 /* $Id$ */
      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 }