1 /* $Id: anchor.c,v 1.33 2006/04/08 11:33:16 inu Exp $ */
6 #define FIRST_ANCHOR_SIZE 30
9 putAnchor(AnchorList *al, char *url, char *target, Anchor **anchor_return,
10 char *referer, char *title, unsigned char key, int line, int pos)
18 al->nanchor = al->anchormax = 0;
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;
26 if (al->nanchor == al->anchormax) { /* need realloc */
28 al->anchors = New_Reuse(Anchor, al->anchors, al->anchormax);
33 if (!n || bpcmp(al->anchors[n - 1].start, bp) < 0)
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];
60 registerHref(Buffer *buf, char *url, char *target, char *referer, char *title,
61 unsigned char key, int line, int pos)
64 buf->href = putAnchor(buf->href, url, target, &a, referer, title, key,
70 registerName(Buffer *buf, char *url, int line, int pos)
73 buf->name = putAnchor(buf->name, url, NULL, &a, NULL, NULL, '\0', line,
79 registerImg(Buffer *buf, char *url, char *title, int line, int pos)
82 buf->img = putAnchor(buf->img, url, NULL, &a, NULL, title, '\0', line,
88 registerForm(Buffer *buf, FormList *flist, struct parsed_tag *tag, int line,
94 fi = formList_addInput(flist, tag);
97 buf->formitem = putAnchor(buf->formitem, (char *)fi, flist->target, &a,
98 NULL, NULL, '\0', line, pos);
103 onAnchor(Anchor *a, int line, int pos)
109 if (bpcmp(bp, a->start) < 0)
111 if (bpcmp(a->end, bp) <= 0)
117 retrieveAnchor(AnchorList *al, int line, int pos)
123 if (al == NULL || al->nanchor == 0)
126 if (al->acache < 0 || al->acache >= al->nanchor)
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);
136 else if (al->acache == 0)
145 retrieveCurrentAnchor(Buffer *buf)
147 if (buf->currentLine == NULL)
149 return retrieveAnchor(buf->href, buf->currentLine->linenumber, buf->pos);
153 retrieveCurrentImg(Buffer *buf)
155 if (buf->currentLine == NULL)
157 return retrieveAnchor(buf->img, buf->currentLine->linenumber, buf->pos);
161 retrieveCurrentForm(Buffer *buf)
163 if (buf->currentLine == NULL)
165 return retrieveAnchor(buf->formitem,
166 buf->currentLine->linenumber, buf->pos);
170 searchAnchor(AnchorList *al, char *str)
176 for (i = 0; i < al->nanchor; i++) {
180 if (!strcmp(a->url, str))
187 searchURLLabel(Buffer *buf, char *url)
189 return searchAnchor(buf->name, url);
194 _put_anchor_news(Buffer *buf, char *p1, char *p2, int line, int pos)
200 if (*(p2 - 1) == '>')
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,
209 #endif /* USE_NNTP */
212 _put_anchor_all(Buffer *buf, char *p1, char *p2, int line, int pos)
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,
223 reseq_anchor0(AnchorList *al, short *seqmap)
231 for (i = 0; i < al->nanchor; i++) {
234 a->hseq = seqmap[a->hseq];
239 /* renumber anchor */
241 reseq_anchor(Buffer *buf)
243 int i, j, n, nmark = (buf->hmarklist) ? buf->hmarklist->nmark : 0;
246 HmarkerList *ml = NULL;
252 for (i = 0; i < buf->href->nanchor; i++) {
253 a = &buf->href->anchors[i];
261 seqmap = NewAtom_N(short, n);
263 for (i = 0; i < n; i++)
267 for (i = 0; i < buf->href->nanchor; i++) {
268 a = &buf->href->anchors[i];
271 a1 = closest_next_anchor(buf->href, NULL, a->start.pos,
273 a1 = closest_next_anchor(buf->formitem, a1, a->start.pos,
275 if (a1 && a1->hseq >= 0) {
276 seqmap[n] = seqmap[a1->hseq];
277 for (j = a1->hseq; j < nmark; j++)
280 ml = putHmarker(ml, a->start.line, a->start.pos, seqmap[n]);
285 for (i = 0; i < nmark; i++) {
286 ml = putHmarker(ml, buf->hmarklist->marks[i].line,
287 buf->hmarklist->marks[i].pos, seqmap[i]);
291 reseq_anchor0(buf->href, seqmap);
292 reseq_anchor0(buf->formitem, seqmap);
296 reAnchorPos(Buffer *buf, Line *l, char *p1, char *p2,
297 Anchor *(*anchorproc) (Buffer *, char *, char *, int, int))
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))
309 for (i = spos; i < epos; i++)
310 l->propBuf[i] |= PE_ANCHOR;
311 while (spos > l->len && l->next && l->next->bpos) {
317 a = anchorproc(buf, p1, p2, l->linenumber, spos);
323 a->end.line = l->linenumber;
324 if (epos > l->len && l->next && l->next->bpos) {
339 reAnchorWord(Buffer *buf, Line *l, int spos, int epos)
341 reAnchorPos(buf, l, &l->lineBuf[spos], &l->lineBuf[epos], _put_anchor_all);
344 /* search regexp and register them as anchors */
345 /* returns error message if any */
347 reAnchorAny(Buffer *buf, char *re,
348 Anchor *(*anchorproc) (Buffer *, char *, char *, int, int))
351 char *p = NULL, *p1, *p2;
353 if (re == NULL || *re == '\0') {
356 if ((re = regexCompile(re, 1)) != NULL) {
359 for (l = MarkAllPages ? buf->firstLine : buf->topLine; l != NULL &&
360 (MarkAllPages || l->linenumber < buf->topLine->linenumber + LASTLINE);
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);
374 if (MarkAllPages && l->next == NULL && buf->pagerSource &&
375 !(buf->bufferprop & BP_CLOSE))
376 getNextPage(buf, PagerMax);
382 reAnchor(Buffer *buf, char *re)
384 return reAnchorAny(buf, re, _put_anchor_all);
389 reAnchorNews(Buffer *buf, char *re)
391 return reAnchorAny(buf, re, _put_anchor_news);
395 reAnchorNewsheader(Buffer *buf)
399 static char *header_mid[] = {
400 "Message-Id:", "References:", "In-Reply-To:", NULL
402 static char *header_group[] = {
406 int i, search = FALSE;
408 if (!buf || !buf->firstLine)
410 for (i = 0; i <= 1; i++) {
412 regexCompile("<[!-;=?-~]+@[a-zA-Z0-9\\.\\-_]+>", 1);
416 regexCompile("[a-zA-Z0-9\\.\\-_]+", 1);
417 header = header_group;
419 for (l = buf->firstLine; l != NULL && l->real_linenumber == 0;
426 for (q = header; *q; q++) {
427 if (!strncasecmp(p, *q, strlen(*q))) {
429 p = strchr(p, ':') + 1;
437 if (regexMatch(p, &l->lineBuf[l->size] - p, p == l->lineBuf)
439 matchedPosition(&p1, &p2);
440 p = reAnchorPos(buf, l, p1, p2, _put_anchor_news);
450 #endif /* USE_NNTP */
452 #define FIRST_MARKER_SIZE 30
454 putHmarker(HmarkerList *ml, int line, int pos, int seq)
457 ml = New(HmarkerList);
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);
468 if (seq + 1 > ml->nmark)
470 if (ml->nmark >= ml->markmax) {
471 ml->markmax = ml->nmark * 2;
472 ml->marks = New_Reuse(BufferPoint, ml->marks, ml->markmax);
474 ml->marks[seq].line = line;
475 ml->marks[seq].pos = pos;
476 ml->marks[seq].invalid = 0;
481 closest_next_anchor(AnchorList *a, Anchor *an, int x, int y)
485 if (a == NULL || a->nanchor == 0)
487 for (i = 0; i < a->nanchor; i++) {
488 if (a->anchors[i].hseq < 0)
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))
502 closest_prev_anchor(AnchorList *a, Anchor *an, int x, int y)
506 if (a == NULL || a->nanchor == 0)
508 for (i = 0; i < a->nanchor; i++) {
509 if (a->anchors[i].hseq < 0)
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))
523 shiftAnchorPosition(AnchorList *al, HmarkerList *hl, int line, int pos,
530 if (al == NULL || al->nanchor == 0)
534 for (b = 0, e = al->nanchor - 1; b <= e; s = (b + e + 1) / 2) {
536 cmp = onAnchor(a, line, pos);
546 for (; s < al->nanchor; s++) {
548 if (a->start.line > line)
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;
555 if (a->end.pos >= pos)
562 addMultirowsImg(Buffer *buf, AnchorList *al)
564 int i, j, k, col, ecol, pos;
566 Anchor a_img, a_href, a_form, *a;
569 if (al == NULL || al->nanchor == 0)
571 for (i = 0; i < al->nanchor; i++) {
572 a_img = al->anchors[i];
574 if (a_img.hseq < 0 || !img || img->rows <= 1)
576 for (l = buf->firstLine; l != NULL; l = l->next) {
577 if (l->linenumber == img->y)
582 if (a_img.y == a_img.start.line)
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)
593 a = retrieveAnchor(buf->href, a_img.start.line, a_img.start.pos);
598 a = retrieveAnchor(buf->formitem, a_img.start.line, a_img.start.pos);
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)
608 pos = columnPos(l, col);
609 a = registerImg(buf, a_img.url, a_img.title, l->linenumber, pos);
610 a->hseq = -a_img.hseq;
613 a->end.pos = pos + ecol - col;
614 for (k = pos; k < a->end.pos; k++)
615 l->propBuf[k] |= PE_IMAGE;
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;
622 a->end.pos = pos + ecol - col;
623 for (k = pos; k < a->end.pos; k++)
624 l->propBuf[k] |= PE_ANCHOR;
627 buf->formitem = putAnchor(buf->formitem, a_form.url,
628 a_form.target, &a, NULL, NULL, '\0',
630 a->hseq = a_form.hseq;
631 a->end.pos = pos + ecol - col;
640 addMultirowsForm(Buffer *buf, AnchorList *al)
642 int i, j, k, col, ecol, pos;
647 if (al == NULL || al->nanchor == 0)
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)
654 for (l = buf->firstLine; l != NULL; l = l->next) {
655 if (l->linenumber == a_form.y)
660 if (a_form.y == a_form.start.line)
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)
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);
677 buf->hmarklist->marks[a_form.hseq].line = l->linenumber;
678 buf->hmarklist->marks[a_form.hseq].pos = pos;
680 if (a_form.start.line == l->linenumber)
682 buf->formitem = putAnchor(buf->formitem, a_form.url,
683 a_form.target, &a, NULL, NULL, '\0',
685 a->hseq = a_form.hseq;
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;
697 getAnchorText(Buffer *buf, AnchorList *al, Anchor *a)
704 if (!a || a->hseq < 0)
708 for (i = 0; i < al->nanchor; i++) {
712 for (; l; l = l->next) {
713 if (l->linenumber == a->start.line)
718 p = l->lineBuf + a->start.pos;
719 ep = l->lineBuf + a->end.pos;
720 for (; p < ep && IS_SPACE(*p); p++) ;
724 tmp = Strnew_size(ep - p);
726 Strcat_char(tmp, ' ');
727 Strcat_charp_n(tmp, p, ep - p);
729 return tmp ? tmp->ptr : NULL;
733 link_list_panel(Buffer *buf)
742 /* FIXME: gettextize? */
743 Str tmp = Strnew_charp("<title>Link List</title>\
744 <h1 align=center>Link List</h1>\n");
746 if (buf->bufferprop & BP_INTERNAL ||
747 (buf->linklist == NULL && buf->href == NULL && buf->img == NULL)) {
752 Strcat_charp(tmp, "<hr><h2>Links</h2>\n<ol>\n");
753 for (l = buf->linklist; l; l = l->next) {
755 parseURL2(l->url, &pu, baseURL(buf));
756 p = parsedURL2Str(&pu)->ptr;
759 p = html_quote(url_unquote_conv(p, buf->document_charset));
765 if (l->type == LINK_TYPE_REL)
767 else if (l->type == LINK_TYPE_REV)
771 t = Sprintf("%s%s\n", l->title ? l->title : "", t)->ptr;
773 Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
776 Strcat_charp(tmp, "</ol>\n");
780 Strcat_charp(tmp, "<hr><h2>Anchors</h2>\n<ol>\n");
782 for (i = 0; i < al->nanchor; i++) {
784 if (a->hseq < 0 || a->slave)
786 parseURL2(a->url, &pu, baseURL(buf));
787 p = parsedURL2Str(&pu)->ptr;
790 p = html_quote(url_unquote_conv(p, buf->document_charset));
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,
798 Strcat_charp(tmp, "</ol>\n");
802 Strcat_charp(tmp, "<hr><h2>Images</h2>\n<ol>\n");
804 for (i = 0; i < al->nanchor; i++) {
808 parseURL2(a->url, &pu, baseURL(buf));
809 p = parsedURL2Str(&pu)->ptr;
812 p = html_quote(url_unquote_conv(p, buf->document_charset));
815 if (a->title && *a->title)
816 t = html_quote(a->title);
818 t = html_quote(url_unquote_conv
819 (a->url, buf->document_charset));
821 t = html_quote(a->url);
822 Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
824 a = retrieveAnchor(buf->formitem, a->start.line, a->start.pos);
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);
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;
841 parseURL2(m->url, &pu, baseURL(buf));
842 p = parsedURL2Str(&pu)->ptr;
845 p = html_quote(url_unquote_conv(p,
850 if (m->alt && *m->alt)
851 t = html_quote(m->alt);
853 t = html_quote(url_unquote_conv(m->url,
857 t = html_quote(m->url);
858 Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t,
859 "</a><br>", p, "\n", NULL);
861 Strcat_charp(tmp, "</ol>\n");
864 Strcat_charp(tmp, "</ol>\n");
867 return loadHTMLString(tmp);