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 *
putAnchor(AnchorList * al,char * url,char * target,Anchor ** anchor_return,char * referer,char * title,unsigned char key,int line,int pos)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 = { 0 };
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 *
registerHref(Buffer * buf,char * url,char * target,char * referer,char * title,unsigned char key,int line,int pos)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 *
registerName(Buffer * buf,char * url,int line,int pos)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 *
registerImg(Buffer * buf,char * url,char * title,int line,int pos)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 *
registerForm(Buffer * buf,FormList * flist,struct parsed_tag * tag,int line,int pos)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
onAnchor(Anchor * a,int line,int pos)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 *
retrieveAnchor(AnchorList * al,int line,int pos)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 *
retrieveCurrentAnchor(Buffer * buf)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 *
retrieveCurrentImg(Buffer * buf)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 *
retrieveCurrentForm(Buffer * buf)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 *
searchAnchor(AnchorList * al,char * str)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 *
searchURLLabel(Buffer * buf,char * url)187 searchURLLabel(Buffer *buf, char *url)
188 {
189     return searchAnchor(buf->name, url);
190 }
191 
192 #ifdef USE_NNTP
193 static Anchor *
_put_anchor_news(Buffer * buf,char * p1,char * p2,int line,int pos)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 = Strnew_charp("news:");
204     Strcat_charp_n(tmp, p1, p2 - p1);
205     return registerHref(buf, url_encode(tmp->ptr, baseURL(buf),
206 					buf->document_charset),
207 			NULL, NO_REFERER, NULL, '\0', line,
208 			pos);
209 }
210 #endif				/* USE_NNTP */
211 
212 static Anchor *
_put_anchor_all(Buffer * buf,char * p1,char * p2,int line,int pos)213 _put_anchor_all(Buffer *buf, char *p1, char *p2, int line, int pos)
214 {
215     Str tmp;
216 
217     tmp = Strnew_charp_n(p1, p2 - p1);
218     return registerHref(buf, url_encode(tmp->ptr, baseURL(buf),
219 					buf->document_charset),
220 			NULL, NO_REFERER, NULL,
221 			'\0', line, pos);
222 }
223 
224 static void
reseq_anchor0(AnchorList * al,short * seqmap)225 reseq_anchor0(AnchorList *al, short *seqmap)
226 {
227     int i;
228     Anchor *a;
229 
230     if (!al)
231 	return;
232 
233     for (i = 0; i < al->nanchor; i++) {
234 	a = &al->anchors[i];
235 	if (a->hseq >= 0) {
236 	    a->hseq = seqmap[a->hseq];
237 	}
238     }
239 }
240 
241 /* renumber anchor */
242 static void
reseq_anchor(Buffer * buf)243 reseq_anchor(Buffer *buf)
244 {
245     int i, j, n, nmark = (buf->hmarklist) ? buf->hmarklist->nmark : 0;
246     short *seqmap;
247     Anchor *a, *a1;
248     HmarkerList *ml = NULL;
249 
250     if (!buf->href)
251 	return;
252 
253     n = nmark;
254     for (i = 0; i < buf->href->nanchor; i++) {
255 	a = &buf->href->anchors[i];
256 	if (a->hseq == -2)
257 	    n++;
258     }
259 
260     if (n == nmark)
261 	return;
262 
263     seqmap = NewAtom_N(short, n);
264 
265     for (i = 0; i < n; i++)
266 	seqmap[i] = i;
267 
268     n = nmark;
269     for (i = 0; i < buf->href->nanchor; i++) {
270 	a = &buf->href->anchors[i];
271 	if (a->hseq == -2) {
272 	    a->hseq = n;
273 	    a1 = closest_next_anchor(buf->href, NULL, a->start.pos,
274 				     a->start.line);
275 	    a1 = closest_next_anchor(buf->formitem, a1, a->start.pos,
276 				     a->start.line);
277 	    if (a1 && a1->hseq >= 0) {
278 		seqmap[n] = seqmap[a1->hseq];
279 		for (j = a1->hseq; j < nmark; j++)
280 		    seqmap[j]++;
281 	    }
282 	    ml = putHmarker(ml, a->start.line, a->start.pos, seqmap[n]);
283 	    n++;
284 	}
285     }
286 
287     for (i = 0; i < nmark; i++) {
288 	ml = putHmarker(ml, buf->hmarklist->marks[i].line,
289 			buf->hmarklist->marks[i].pos, seqmap[i]);
290     }
291     buf->hmarklist = ml;
292 
293     reseq_anchor0(buf->href, seqmap);
294     reseq_anchor0(buf->formitem, seqmap);
295 }
296 
297 static char *
reAnchorPos(Buffer * buf,Line * l,char * p1,char * p2,Anchor * (* anchorproc)(Buffer *,char *,char *,int,int))298 reAnchorPos(Buffer *buf, Line *l, char *p1, char *p2,
299 	    Anchor *(*anchorproc) (Buffer *, char *, char *, int, int))
300 {
301     Anchor *a;
302     int spos, epos;
303     int i, hseq = -2;
304 
305     spos = p1 - l->lineBuf;
306     epos = p2 - l->lineBuf;
307     for (i = spos; i < epos; i++) {
308 	if (l->propBuf[i] & (PE_ANCHOR | PE_FORM))
309 	    return p2;
310     }
311     for (i = spos; i < epos; i++)
312 	l->propBuf[i] |= PE_ANCHOR;
313     while (spos > l->len && l->next && l->next->bpos) {
314 	spos -= l->len;
315 	epos -= l->len;
316 	l = l->next;
317     }
318     while (1) {
319 	a = anchorproc(buf, p1, p2, l->linenumber, spos);
320 	a->hseq = hseq;
321 	if (hseq == -2) {
322 	    reseq_anchor(buf);
323 	    hseq = a->hseq;
324 	}
325 	a->end.line = l->linenumber;
326 	if (epos > l->len && l->next && l->next->bpos) {
327 	    a->end.pos = l->len;
328 	    spos = 0;
329 	    epos -= l->len;
330 	    l = l->next;
331 	}
332 	else {
333 	    a->end.pos = epos;
334 	    break;
335 	}
336     }
337     return p2;
338 }
339 
340 void
reAnchorWord(Buffer * buf,Line * l,int spos,int epos)341 reAnchorWord(Buffer *buf, Line *l, int spos, int epos)
342 {
343     reAnchorPos(buf, l, &l->lineBuf[spos], &l->lineBuf[epos], _put_anchor_all);
344 }
345 
346 /* search regexp and register them as anchors */
347 /* returns error message if any               */
348 static char *
reAnchorAny(Buffer * buf,char * re,Anchor * (* anchorproc)(Buffer *,char *,char *,int,int))349 reAnchorAny(Buffer *buf, char *re,
350 	    Anchor *(*anchorproc) (Buffer *, char *, char *, int, int))
351 {
352     Line *l;
353     char *p = NULL, *p1, *p2;
354 
355     if (re == NULL || *re == '\0') {
356 	return NULL;
357     }
358     if ((re = regexCompile(re, 1)) != NULL) {
359 	return re;
360     }
361     for (l = MarkAllPages ? buf->firstLine : buf->topLine; l != NULL &&
362 	 (MarkAllPages || l->linenumber < buf->topLine->linenumber + LASTLINE);
363 	 l = l->next) {
364 	if (p && l->bpos)
365 	    goto next_line;
366 	p = l->lineBuf;
367 	for (;;) {
368 	    if (regexMatch(p, &l->lineBuf[l->size] - p, p == l->lineBuf) == 1) {
369 		matchedPosition(&p1, &p2);
370 		p = reAnchorPos(buf, l, p1, p2, anchorproc);
371 	    }
372 	    else
373 		break;
374 	}
375       next_line:
376 	if (MarkAllPages && l->next == NULL && buf->pagerSource &&
377 	    !(buf->bufferprop & BP_CLOSE))
378 	    getNextPage(buf, PagerMax);
379     }
380     return NULL;
381 }
382 
383 char *
reAnchor(Buffer * buf,char * re)384 reAnchor(Buffer *buf, char *re)
385 {
386     return reAnchorAny(buf, re, _put_anchor_all);
387 }
388 
389 #ifdef USE_NNTP
390 char *
reAnchorNews(Buffer * buf,char * re)391 reAnchorNews(Buffer *buf, char *re)
392 {
393     return reAnchorAny(buf, re, _put_anchor_news);
394 }
395 
396 char *
reAnchorNewsheader(Buffer * buf)397 reAnchorNewsheader(Buffer *buf)
398 {
399     Line *l;
400     char *p, *p1, *p2;
401     static char *header_mid[] = {
402 	"Message-Id:", "References:", "In-Reply-To:", NULL
403     };
404     static char *header_group[] = {
405 	"Newsgroups:", NULL
406     };
407     char **header, **q;
408     int i, search = FALSE;
409 
410     if (!buf || !buf->firstLine)
411 	return NULL;
412     for (i = 0; i <= 1; i++) {
413 	if (i == 0) {
414 	    regexCompile("<[!-;=?-~]+@[a-zA-Z0-9\\.\\-_]+>", 1);
415 	    header = header_mid;
416 	}
417 	else {
418 	    regexCompile("[a-zA-Z0-9\\.\\-_]+", 1);
419 	    header = header_group;
420 	}
421 	for (l = buf->firstLine; l != NULL && l->real_linenumber == 0;
422 	     l = l->next) {
423 	    if (l->bpos)
424 		continue;
425 	    p = l->lineBuf;
426 	    if (!IS_SPACE(*p)) {
427 		search = FALSE;
428 		for (q = header; *q; q++) {
429 		    if (!strncasecmp(p, *q, strlen(*q))) {
430 			search = TRUE;
431 			p = strchr(p, ':') + 1;
432 			break;
433 		    }
434 		}
435 	    }
436 	    if (!search)
437 		continue;
438 	    for (;;) {
439 		if (regexMatch(p, &l->lineBuf[l->size] - p, p == l->lineBuf)
440 		    == 1) {
441 		    matchedPosition(&p1, &p2);
442 		    p = reAnchorPos(buf, l, p1, p2, _put_anchor_news);
443 		}
444 		else
445 		    break;
446 	    }
447 	}
448     }
449     reseq_anchor(buf);
450     return NULL;
451 }
452 #endif				/* USE_NNTP */
453 
454 #define FIRST_MARKER_SIZE 30
455 HmarkerList *
putHmarker(HmarkerList * ml,int line,int pos,int seq)456 putHmarker(HmarkerList *ml, int line, int pos, int seq)
457 {
458     if (ml == NULL) {
459 	ml = New(HmarkerList);
460 	ml->marks = NULL;
461 	ml->nmark = 0;
462 	ml->markmax = 0;
463 	ml->prevhseq = -1;
464     }
465     if (ml->markmax == 0) {
466 	ml->markmax = FIRST_MARKER_SIZE;
467 	ml->marks = NewAtom_N(BufferPoint, ml->markmax);
468 	bzero(ml->marks, sizeof(BufferPoint) * ml->markmax);
469     }
470     if (seq + 1 > ml->nmark)
471 	ml->nmark = seq + 1;
472     if (ml->nmark >= ml->markmax) {
473 	ml->markmax = ml->nmark * 2;
474 	ml->marks = New_Reuse(BufferPoint, ml->marks, ml->markmax);
475     }
476     ml->marks[seq].line = line;
477     ml->marks[seq].pos = pos;
478     ml->marks[seq].invalid = 0;
479     return ml;
480 }
481 
482 Anchor *
closest_next_anchor(AnchorList * a,Anchor * an,int x,int y)483 closest_next_anchor(AnchorList *a, Anchor *an, int x, int y)
484 {
485     int i;
486 
487     if (a == NULL || a->nanchor == 0)
488 	return an;
489     for (i = 0; i < a->nanchor; i++) {
490 	if (a->anchors[i].hseq < 0)
491 	    continue;
492 	if (a->anchors[i].start.line > y ||
493 	    (a->anchors[i].start.line == y && a->anchors[i].start.pos > x)) {
494 	    if (an == NULL || an->start.line > a->anchors[i].start.line ||
495 		(an->start.line == a->anchors[i].start.line &&
496 		 an->start.pos > a->anchors[i].start.pos))
497 		an = &a->anchors[i];
498 	}
499     }
500     return an;
501 }
502 
503 Anchor *
closest_prev_anchor(AnchorList * a,Anchor * an,int x,int y)504 closest_prev_anchor(AnchorList *a, Anchor *an, int x, int y)
505 {
506     int i;
507 
508     if (a == NULL || a->nanchor == 0)
509 	return an;
510     for (i = 0; i < a->nanchor; i++) {
511 	if (a->anchors[i].hseq < 0)
512 	    continue;
513 	if (a->anchors[i].end.line < y ||
514 	    (a->anchors[i].end.line == y && a->anchors[i].end.pos <= x)) {
515 	    if (an == NULL || an->end.line < a->anchors[i].end.line ||
516 		(an->end.line == a->anchors[i].end.line &&
517 		 an->end.pos < a->anchors[i].end.pos))
518 		an = &a->anchors[i];
519 	}
520     }
521     return an;
522 }
523 
524 void
shiftAnchorPosition(AnchorList * al,HmarkerList * hl,int line,int pos,int shift)525 shiftAnchorPosition(AnchorList *al, HmarkerList *hl, int line, int pos,
526 		    int shift)
527 {
528     Anchor *a;
529     size_t b, e, s = 0;
530     int cmp;
531 
532     if (al == NULL || al->nanchor == 0)
533 	return;
534 
535     s = al->nanchor / 2;
536     for (b = 0, e = al->nanchor - 1; b <= e; s = (b + e + 1) / 2) {
537 	a = &al->anchors[s];
538 	cmp = onAnchor(a, line, pos);
539 	if (cmp == 0)
540 	    break;
541 	else if (cmp > 0)
542 	    b = s + 1;
543 	else if (s == 0)
544 	    break;
545 	else
546 	    e = s - 1;
547     }
548     for (; s < al->nanchor; s++) {
549 	a = &al->anchors[s];
550 	if (a->start.line > line)
551 	    break;
552 	if (a->start.pos > pos) {
553 	    a->start.pos += shift;
554 	    if (hl && hl->marks &&
555 		a->hseq >= 0 && hl->marks[a->hseq].line == line)
556 		hl->marks[a->hseq].pos = a->start.pos;
557 	}
558 	if (a->end.pos >= pos)
559 	    a->end.pos += shift;
560     }
561 }
562 
563 #ifdef USE_IMAGE
564 void
addMultirowsImg(Buffer * buf,AnchorList * al)565 addMultirowsImg(Buffer *buf, AnchorList *al)
566 {
567     int i, j, k, col, ecol, pos;
568     Image *img;
569     Anchor a_img, a_href, a_form, *a;
570     Line *l, *ls;
571 
572     if (al == NULL || al->nanchor == 0)
573 	return;
574     for (i = 0; i < al->nanchor; i++) {
575 	a_img = al->anchors[i];
576 	img = a_img.image;
577 	if (a_img.hseq < 0 || !img || img->rows <= 1)
578 	    continue;
579 	for (l = buf->firstLine; l != NULL; l = l->next) {
580 	    if (l->linenumber == img->y)
581 		break;
582 	}
583 	if (!l)
584 	    continue;
585 	if (a_img.y == a_img.start.line)
586 	    ls = l;
587 	else {
588 	    for (ls = l; ls != NULL;
589 		 ls = (a_img.y < a_img.start.line) ? ls->next : ls->prev) {
590 		if (ls->linenumber == a_img.start.line)
591 		    break;
592 	    }
593 	    if (!ls)
594 		continue;
595 	}
596 	a = retrieveAnchor(buf->href, a_img.start.line, a_img.start.pos);
597 	if (a)
598 	    a_href = *a;
599 	else
600 	    a_href.url = NULL;
601 	a = retrieveAnchor(buf->formitem, a_img.start.line, a_img.start.pos);
602 	if (a)
603 	    a_form = *a;
604 	else
605 	    a_form.url = NULL;
606 	col = COLPOS(ls, a_img.start.pos);
607 	ecol = COLPOS(ls, a_img.end.pos);
608 	for (j = 0; l && j < img->rows; l = l->next, j++) {
609 	    if (a_img.start.line == l->linenumber)
610 		continue;
611 	    pos = columnPos(l, col);
612 	    a = registerImg(buf, a_img.url, a_img.title, l->linenumber, pos);
613 	    a->hseq = -a_img.hseq;
614 	    a->slave = TRUE;
615 	    a->image = img;
616 	    a->end.pos = pos + ecol - col;
617 	    for (k = pos; k < a->end.pos; k++)
618 		l->propBuf[k] |= PE_IMAGE;
619 	    if (a_href.url) {
620 		a = registerHref(buf, a_href.url, a_href.target,
621 				 a_href.referer, a_href.title,
622 				 a_href.accesskey, l->linenumber, pos);
623 		a->hseq = a_href.hseq;
624 		a->slave = TRUE;
625 		a->end.pos = pos + ecol - col;
626 		for (k = pos; k < a->end.pos; k++)
627 		    l->propBuf[k] |= PE_ANCHOR;
628 	    }
629 	    if (a_form.url) {
630 		buf->formitem = putAnchor(buf->formitem, a_form.url,
631 					  a_form.target, &a, NULL, NULL, '\0',
632 					  l->linenumber, pos);
633 		a->hseq = a_form.hseq;
634 		a->end.pos = pos + ecol - col;
635 	    }
636 	}
637 	img->rows = 0;
638     }
639 }
640 #endif
641 
642 void
addMultirowsForm(Buffer * buf,AnchorList * al)643 addMultirowsForm(Buffer *buf, AnchorList *al)
644 {
645     int i, j, k, col, ecol, pos;
646     Anchor a_form, *a;
647     Line *l, *ls;
648 
649     if (al == NULL || al->nanchor == 0)
650 	return;
651     for (i = 0; i < al->nanchor; i++) {
652 	a_form = al->anchors[i];
653 	al->anchors[i].rows = 1;
654 	if (a_form.hseq < 0 || a_form.rows <= 1)
655 	    continue;
656 	for (l = buf->firstLine; l != NULL; l = l->next) {
657 	    if (l->linenumber == a_form.y)
658 		break;
659 	}
660 	if (!l)
661 	    continue;
662 	if (a_form.y == a_form.start.line)
663 	    ls = l;
664 	else {
665 	    for (ls = l; ls != NULL;
666 		 ls = (a_form.y < a_form.start.line) ? ls->next : ls->prev) {
667 		if (ls->linenumber == a_form.start.line)
668 		    break;
669 	    }
670 	    if (!ls)
671 		continue;
672 	}
673 	col = COLPOS(ls, a_form.start.pos);
674 	ecol = COLPOS(ls, a_form.end.pos);
675 	for (j = 0; l && j < a_form.rows; l = l->next, j++) {
676 	    pos = columnPos(l, col);
677 	    if (j == 0) {
678 		buf->hmarklist->marks[a_form.hseq].line = l->linenumber;
679 		buf->hmarklist->marks[a_form.hseq].pos = pos;
680 	    }
681 	    if (a_form.start.line == l->linenumber)
682 		continue;
683 	    buf->formitem = putAnchor(buf->formitem, a_form.url,
684 				      a_form.target, &a, NULL, NULL, '\0',
685 				      l->linenumber, pos);
686 	    a->hseq = a_form.hseq;
687 	    a->y = a_form.y;
688 	    a->end.pos = pos + ecol - col;
689 	    if (pos < 1 || a->end.pos >= l->size)
690 		continue;
691 	    l->lineBuf[pos - 1] = '[';
692 	    l->lineBuf[a->end.pos] = ']';
693 	    for (k = pos; k < a->end.pos; k++)
694 		l->propBuf[k] |= PE_FORM;
695 	}
696     }
697 }
698 
699 char *
getAnchorText(Buffer * buf,AnchorList * al,Anchor * a)700 getAnchorText(Buffer *buf, AnchorList *al, Anchor *a)
701 {
702     int hseq, i;
703     Line *l;
704     Str tmp = NULL;
705     char *p, *ep;
706 
707     if (!a || a->hseq < 0)
708 	return NULL;
709     hseq = a->hseq;
710     l = buf->firstLine;
711     for (i = 0; i < al->nanchor; i++) {
712 	a = &al->anchors[i];
713 	if (a->hseq != hseq)
714 	    continue;
715 	for (; l; l = l->next) {
716 	    if (l->linenumber == a->start.line)
717 		break;
718 	}
719 	if (!l)
720 	    break;
721 	p = l->lineBuf + a->start.pos;
722 	ep = l->lineBuf + a->end.pos;
723 	for (; p < ep && IS_SPACE(*p); p++) ;
724 	if (p == ep)
725 	    continue;
726 	if (!tmp)
727 	    tmp = Strnew_size(ep - p);
728 	else
729 	    Strcat_char(tmp, ' ');
730 	Strcat_charp_n(tmp, p, ep - p);
731     }
732     return tmp ? tmp->ptr : NULL;
733 }
734 
735 Buffer *
link_list_panel(Buffer * buf)736 link_list_panel(Buffer *buf)
737 {
738     LinkList *l;
739     AnchorList *al;
740     Anchor *a;
741     FormItemList *fi;
742     int i;
743     char *t, *u, *p;
744     ParsedURL pu;
745     /* FIXME: gettextize? */
746     Str tmp = Strnew_charp("<title>Link List</title>\
747 <h1 align=center>Link List</h1>\n");
748 
749     if (buf->bufferprop & BP_INTERNAL ||
750 	(buf->linklist == NULL && buf->href == NULL && buf->img == NULL)) {
751 	return NULL;
752     }
753 
754     if (buf->linklist) {
755 	Strcat_charp(tmp, "<hr><h2>Links</h2>\n<ol>\n");
756 	for (l = buf->linklist; l; l = l->next) {
757 	    if (l->url) {
758 		parseURL2(l->url, &pu, baseURL(buf));
759 		p = parsedURL2Str(&pu)->ptr;
760 		u = html_quote(p);
761 		if (DecodeURL)
762 		    p = html_quote(url_decode2(p, buf));
763 		else
764 		    p = u;
765 	    }
766 	    else
767 		u = p = "";
768 	    if (l->type == LINK_TYPE_REL)
769 		t = " [Rel]";
770 	    else if (l->type == LINK_TYPE_REV)
771 		t = " [Rev]";
772 	    else
773 		t = "";
774 	    t = Sprintf("%s%s\n", l->title ? l->title : "", t)->ptr;
775 	    t = html_quote(t);
776 	    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
777 			   "\n", NULL);
778 	}
779 	Strcat_charp(tmp, "</ol>\n");
780     }
781 
782     if (buf->href) {
783 	Strcat_charp(tmp, "<hr><h2>Anchors</h2>\n<ol>\n");
784 	al = buf->href;
785 	for (i = 0; i < al->nanchor; i++) {
786 	    a = &al->anchors[i];
787 	    if (a->hseq < 0 || a->slave)
788 		continue;
789 	    parseURL2(a->url, &pu, baseURL(buf));
790 	    p = parsedURL2Str(&pu)->ptr;
791 	    u = html_quote(p);
792 	    if (DecodeURL)
793 		p = html_quote(url_decode2(p, buf));
794 	    else
795 		p = u;
796 	    t = getAnchorText(buf, al, a);
797 	    t = t ? html_quote(t) : "";
798 	    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
799 			   "\n", NULL);
800 	}
801 	Strcat_charp(tmp, "</ol>\n");
802     }
803 
804     if (buf->img) {
805 	Strcat_charp(tmp, "<hr><h2>Images</h2>\n<ol>\n");
806 	al = buf->img;
807 	for (i = 0; i < al->nanchor; i++) {
808 	    a = &al->anchors[i];
809 	    if (a->slave)
810 		continue;
811 	    parseURL2(a->url, &pu, baseURL(buf));
812 	    p = parsedURL2Str(&pu)->ptr;
813 	    u = html_quote(p);
814 	    if (DecodeURL)
815 		p = html_quote(url_decode2(p, buf));
816 	    else
817 		p = u;
818 	    if (a->title && *a->title)
819 		t = html_quote(a->title);
820 	    else
821 		t = html_quote(url_decode2(a->url, buf));
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_decode2(p, buf));
846 		    else
847 			p = u;
848 		    if (m->alt && *m->alt)
849 			t = html_quote(m->alt);
850 		    else
851 			t = html_quote(url_decode2(m->url, buf));
852 		    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t,
853 				   "</a><br>", p, "\n", NULL);
854 		}
855 		Strcat_charp(tmp, "</ol>\n");
856 	    }
857 	}
858 	Strcat_charp(tmp, "</ol>\n");
859     }
860 
861     return loadHTMLString(tmp);
862 }
863