8 #define _GNU_SOURCE /* strcasestr() */
11 #include <libgen.h> /* basename() */
20 #include "bfu/listmenu.h"
22 #include "bookmarks/bookmarks.h"
23 #include "config/options.h"
24 #include "config/kbdbind.h"
25 #include "document/html/frames.h"
26 #include "document/html/parser/link.h"
27 #include "document/html/parser/parse.h"
28 #include "document/html/parser/stack.h"
29 #include "document/html/parser.h"
30 #include "document/html/renderer.h"
31 #include "globhist/globhist.h"
32 #include "mime/mime.h"
33 #include "protocol/uri.h"
34 #include "util/conv.h"
35 #include "util/error.h"
36 #include "util/memdebug.h"
37 #include "util/memory.h"
38 #include "util/string.h"
41 #include "document/html/internal.h"
45 html_a(struct html_context
*html_context
, unsigned char *a
,
46 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
50 href
= get_url_val(a
, "href", html_context
->doc_cp
);
52 unsigned char *target
;
54 mem_free_set(&format
.link
,
55 join_urls(html_context
->base_href
,
56 trim_chars(href
, ' ', 0)));
60 target
= get_target(html_context
->options
, a
);
62 mem_free_set(&format
.target
, target
);
64 mem_free_set(&format
.target
, stracpy(html_context
->base_target
));
68 ; /* Shut up compiler */
69 #ifdef CONFIG_GLOBHIST
70 } else if (get_global_history_item(format
.link
)) {
71 format
.style
.color
.foreground
= format
.color
.vlink
;
72 html_top
->pseudo_class
&= ~ELEMENT_LINK
;
73 html_top
->pseudo_class
|= ELEMENT_VISITED
;
75 #ifdef CONFIG_BOOKMARKS
76 } else if (get_bookmark(format
.link
)) {
77 format
.style
.color
.foreground
= format
.color
.bookmark_link
;
78 html_top
->pseudo_class
&= ~ELEMENT_VISITED
;
79 /* XXX: Really set ELEMENT_LINK? --pasky */
80 html_top
->pseudo_class
|= ELEMENT_LINK
;
83 format
.style
.color
.foreground
= format
.color
.clink
;
84 html_top
->pseudo_class
&= ~ELEMENT_VISITED
;
85 html_top
->pseudo_class
|= ELEMENT_LINK
;
88 mem_free_set(&format
.title
,
89 get_attr_val(a
, "title", html_context
->doc_cp
));
91 html_focusable(html_context
, a
);
94 pop_html_element(html_context
);
97 set_fragment_identifier(html_context
, a
, "name");
100 /* Returns an allocated string made after @label
101 * but limited to @max_len length, by truncating
102 * the middle of @label string, which is replaced
103 * by an asterisk ('*').
104 * If @max_len < 0 it returns NULL.
105 * If @max_len == 0 it returns an unmodified copy
107 * In either case, it may return NULL if a memory
108 * allocation failure occurs.
110 * truncate_label("some_string", 5) => "so*ng" */
111 static unsigned char *
112 truncate_label(unsigned char *label
, int max_len
)
114 unsigned char *new_label
;
115 int len
= strlen(label
);
119 if (max_len
< 0) return NULL
;
120 if (max_len
== 0 || len
<= max_len
)
121 return stracpy(label
);
123 right_part_len
= left_part_len
= max_len
/ 2;
125 if (left_part_len
+ right_part_len
+ 1 > max_len
)
128 new_label
= mem_alloc(max_len
+ 1);
129 if (!new_label
) return NULL
;
132 memcpy(new_label
, label
, left_part_len
);
134 new_label
[left_part_len
] = '*';
137 memcpy(new_label
+ left_part_len
+ 1,
138 label
+ len
- right_part_len
, right_part_len
);
140 new_label
[max_len
] = '\0';
145 /* Get image filename from its src attribute. */
146 static unsigned char *
147 get_image_filename_from_src(int max_len
, unsigned char *src
)
149 unsigned char *text
= NULL
;
150 unsigned char *start
, *filename
;
153 if (!src
) return NULL
;
154 /* We can display image as [foo.gif]. */
156 len
= strcspn(src
, "?");
158 for (start
= src
+ len
; start
> src
; start
--)
159 if (dir_sep(start
[-1])) {
165 filename
= memacpy(start
, len
);
167 /* XXX: Due to a compatibility alias (added: 2004-12-15 in
168 * 0.10pre3.CVS for document.browse.images.file_tags) this can
169 * return a negative @max_len. */
170 text
= truncate_label(filename
, max_len
);
178 /* Returns an allocated string containing formatted @label. */
179 static unsigned char *
180 get_image_label(int max_len
, unsigned char *label
)
182 unsigned char *formatted_label
;
184 if (!label
) return NULL
;
186 formatted_label
= truncate_label(label
, max_len
);
189 return formatted_label
;
193 put_image_label(unsigned char *a
, unsigned char *label
,
194 struct html_context
*html_context
)
196 color_T saved_foreground
;
197 enum text_style_format saved_attr
;
199 /* This is not 100% appropriate for <img>, but well, accepting
200 * accesskey and tabindex near <img> is just our little
201 * extension to the standard. After all, it makes sense. */
202 html_focusable(html_context
, a
);
204 saved_foreground
= format
.style
.color
.foreground
;
205 saved_attr
= format
.style
.attr
;
206 format
.style
.color
.foreground
= format
.color
.image_link
;
207 format
.style
.attr
|= AT_NO_ENTITIES
;
208 put_chrs(html_context
, label
, strlen(label
));
209 format
.style
.color
.foreground
= saved_foreground
;
210 format
.style
.attr
= saved_attr
;
214 inline_image(struct html_context
*html_context
, unsigned char *a
, unsigned char *src
)
217 int width
= -1, height
= -1;
218 unsigned char *dimstr
;
222 url
= join_urls(html_context
->base_href
, src
);
224 uri
= get_uri(url
, URI_BASE
);
228 dimstr
= get_attr_val(a
, "width", html_context
->doc_cp
);
230 width
= atoi(dimstr
);
233 dimstr
= get_attr_val(a
, "height", html_context
->doc_cp
);
235 height
= atoi(dimstr
);
239 html_context
->special_f(html_context
, SP_IMAGE
, uri
, width
, height
);
246 html_img_do(unsigned char *a
, unsigned char *object_src
,
247 struct html_context
*html_context
)
249 int ismap
, usemap
= 0;
250 int add_brackets
= 0;
251 unsigned char *src
= NULL
;
252 unsigned char *label
= NULL
;
253 unsigned char *usemap_attr
;
254 struct document_options
*options
= html_context
->options
;
255 int display_style
= options
->image_link
.display_style
;
257 /* Note about display_style:
258 * 0 means always display IMG
259 * 1 means always display filename
260 * 2 means display alt/title attribute if possible, IMG if not
261 * 3 means display alt/title attribute if possible, filename if not */
263 usemap_attr
= get_attr_val(a
, "usemap", html_context
->doc_cp
);
265 unsigned char *joined_urls
= join_urls(html_context
->base_href
,
267 unsigned char *map_url
;
269 mem_free(usemap_attr
);
270 if (!joined_urls
) return;
271 map_url
= straconcat("MAP@", joined_urls
,
272 (unsigned char *) NULL
);
273 mem_free(joined_urls
);
274 if (!map_url
) return;
276 html_stack_dup(html_context
, ELEMENT_KILLABLE
);
277 mem_free_set(&format
.link
, map_url
);
279 format
.style
.attr
|= AT_BOLD
;
284 && has_attr(a
, "ismap", html_context
->doc_cp
)
287 if (display_style
== 2 || display_style
== 3) {
288 label
= get_attr_val(a
, "alt", html_context
->doc_cp
);
290 label
= get_attr_val(a
, "title", html_context
->doc_cp
);
292 /* Little hack to preserve rendering of [ ], in directory listings,
293 * but we still want to drop extra spaces in alt or title attribute
294 * to limit display width on certain websites. --Zas */
295 if (label
&& strlen(label
) > 5) clr_spaces(label
);
298 src
= null_or_stracpy(object_src
);
299 if (!src
) src
= get_url_val(a
, "src", html_context
->doc_cp
);
300 if (!src
) src
= get_url_val(a
, "dynsrc", html_context
->doc_cp
);
303 inline_image(html_context
, a
, src
);
305 /* If we have no label yet (no title or alt), so
306 * just use default ones, or image filename. */
307 if (!label
|| !*label
) {
308 mem_free_set(&label
, NULL
);
309 /* Do we want to display images with no alt/title and with no
311 * If not, just exit now. */
312 if (!options
->images
&& !format
.link
) {
314 if (usemap
) pop_html_element(html_context
);
321 label
= stracpy("USEMAP");
323 label
= stracpy("ISMAP");
325 if (display_style
== 3)
326 label
= get_image_filename_from_src(options
->image_link
.filename_maxlen
, src
);
330 label
= get_image_label(options
->image_link
.label_maxlen
, label
);
333 if (!label
|| !*label
) {
334 mem_free_set(&label
, NULL
);
336 if (display_style
== 1)
337 label
= get_image_filename_from_src(options
->image_link
.filename_maxlen
, src
);
338 if (!label
|| !*label
)
339 mem_free_set(&label
, stracpy("IMG"));
342 mem_free_set(&format
.image
, NULL
);
343 mem_free_set(&format
.title
, NULL
);
346 int img_link_tag
= options
->image_link
.tagging
;
348 if (img_link_tag
&& (img_link_tag
== 2 || add_brackets
)) {
349 unsigned char *img_link_prefix
= options
->image_link
.prefix
;
350 unsigned char *img_link_suffix
= options
->image_link
.suffix
;
351 unsigned char *new_label
= straconcat(img_link_prefix
, label
, img_link_suffix
, (unsigned char *) NULL
);
353 if (new_label
) mem_free_set(&label
, new_label
);
356 if (!options
->image_link
.show_any_as_links
) {
357 put_image_label(a
, label
, html_context
);
361 format
.image
= join_urls(html_context
->base_href
, src
);
364 format
.title
= get_attr_val(a
, "title", html_context
->doc_cp
);
367 unsigned char *new_link
;
369 html_stack_dup(html_context
, ELEMENT_KILLABLE
);
370 new_link
= straconcat(format
.link
, "?0,0", (unsigned char *) NULL
);
372 mem_free_set(&format
.link
, new_link
);
375 put_image_label(a
, label
, html_context
);
377 if (ismap
) pop_html_element(html_context
);
378 mem_free_set(&format
.image
, NULL
);
379 mem_free_set(&format
.title
, NULL
);
386 if (usemap
) pop_html_element(html_context
);
390 html_img(struct html_context
*html_context
, unsigned char *a
,
391 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
393 html_img_do(a
, NULL
, html_context
);
396 /* prefix can have entities in it, but linkname cannot. */
398 put_link_line(unsigned char *prefix
, unsigned char *linkname
,
399 unsigned char *link
, unsigned char *target
,
400 struct html_context
*html_context
)
402 html_context
->has_link_lines
= 1;
403 html_stack_dup(html_context
, ELEMENT_KILLABLE
);
404 ln_break(html_context
, 1);
405 mem_free_set(&format
.link
, NULL
);
406 mem_free_set(&format
.target
, NULL
);
407 mem_free_set(&format
.title
, NULL
);
409 put_chrs(html_context
, prefix
, strlen(prefix
));
410 format
.link
= join_urls(html_context
->base_href
, link
);
411 format
.target
= stracpy(target
);
412 format
.style
.color
.foreground
= format
.color
.clink
;
413 /* linkname typically comes from get_attr_val, which
414 * has already expanded character entity references.
415 * Tell put_chrs not to expand them again. */
416 format
.style
.attr
|= AT_NO_ENTITIES
;
417 put_chrs(html_context
, linkname
, strlen(linkname
));
418 ln_break(html_context
, 1);
419 pop_html_element(html_context
);
424 html_applet(struct html_context
*html_context
, unsigned char *a
,
425 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
427 unsigned char *code
, *alt
;
429 code
= get_url_val(a
, "code", html_context
->doc_cp
);
432 alt
= get_attr_val(a
, "alt", html_context
->doc_cp
);
434 html_focusable(html_context
, a
);
437 put_link_line("Applet: ", alt
, code
,
438 html_context
->options
->framename
, html_context
);
440 put_link_line("", "Applet", code
,
441 html_context
->options
->framename
, html_context
);
449 html_audio(struct html_context
*html_context
, unsigned char *a
,
450 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
454 /* This just places a link where a audio element would be. */
456 url
= get_url_val(a
, "src", html_context
->doc_cp
);
459 html_focusable(html_context
, a
);
461 put_link_line("Audio: ", basename(url
), url
,
462 html_context
->options
->framename
, html_context
);
464 html_skip(html_context
, a
);
470 html_iframe_do(unsigned char *a
, unsigned char *object_src
,
471 struct html_context
*html_context
)
473 unsigned char *name
, *url
= NULL
;
475 url
= null_or_stracpy(object_src
);
476 if (!url
) url
= get_url_val(a
, "src", html_context
->doc_cp
);
479 name
= get_attr_val(a
, "name", html_context
->doc_cp
);
480 if (!name
) name
= get_attr_val(a
, "id", html_context
->doc_cp
);
481 if (!name
) name
= stracpy("");
487 html_focusable(html_context
, a
);
490 put_link_line("IFrame: ", name
, url
,
491 html_context
->options
->framename
, html_context
);
493 put_link_line("", "IFrame", url
,
494 html_context
->options
->framename
, html_context
);
502 html_iframe(struct html_context
*html_context
, unsigned char *a
,
503 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
505 html_iframe_do(a
, NULL
, html_context
);
509 html_object(struct html_context
*html_context
, unsigned char *a
,
510 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
512 unsigned char *type
, *url
;
514 /* This is just some dirty wrapper. We emulate various things through
515 * this, which is anyway in the spirit of <object> element, unifying
516 * <img> and <iframe> etc. */
518 url
= get_url_val(a
, "data", html_context
->doc_cp
);
519 if (!url
) url
= get_url_val(a
, "codebase", html_context
->doc_cp
);
522 type
= get_attr_val(a
, "type", html_context
->doc_cp
);
523 if (!type
) { mem_free(url
); return; }
525 if (!c_strncasecmp(type
, "text/", 5)) {
526 /* We will just emulate <iframe>. */
527 html_iframe_do(a
, url
, html_context
);
528 html_skip(html_context
, a
);
530 } else if (!c_strncasecmp(type
, "image/", 6)) {
531 /* <img> emulation. */
532 /* TODO: Use the enclosed text as 'alt' attribute. */
533 html_img_do(a
, url
, html_context
);
537 name
= get_attr_val(a
, "standby", html_context
->doc_cp
);
539 html_focusable(html_context
, a
);
542 put_link_line("Object: ", name
, url
,
543 html_context
->options
->framename
,
546 put_link_line("Object: ", type
, url
,
547 html_context
->options
->framename
,
559 html_embed(struct html_context
*html_context
, unsigned char *a
,
560 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
562 unsigned char *type
, *extension
;
563 unsigned char *object_src
;
565 /* This is just some dirty wrapper. We emulate various things through
566 * this, which is anyway in the spirit of <object> element, unifying
567 * <img> and <iframe> etc. */
569 object_src
= get_url_val(a
, "src", html_context
->doc_cp
);
570 if (!object_src
|| !*object_src
) {
571 mem_free_set(&object_src
, NULL
);
575 /* If there is no extension we want to get the default mime/type
577 extension
= strrchr(object_src
, '.');
578 if (!extension
) extension
= object_src
;
580 type
= get_extension_content_type(extension
);
581 if (type
&& !c_strncasecmp(type
, "image/", 6)) {
582 html_img_do(a
, object_src
, html_context
);
584 /* We will just emulate <iframe>. */
585 html_iframe_do(a
, object_src
, html_context
);
589 mem_free_set(&object_src
, NULL
);
593 html_video(struct html_context
*html_context
, unsigned char *a
,
594 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
598 /* This just places a link where a video element would be. */
600 url
= get_url_val(a
, "src", html_context
->doc_cp
);
603 html_focusable(html_context
, a
);
605 put_link_line("Video: ", basename(url
), url
,
606 html_context
->options
->framename
, html_context
);
608 html_skip(html_context
, a
);
616 Designates substitute versions for the document in which the link
617 occurs. When used together with the lang attribute, it implies a
618 translated version of the document. When used together with the
619 media attribute, it implies a version designed for a different
623 Refers to an external style sheet. See the section on external style
624 sheets for details. This is used together with the link type
625 "Alternate" for user-selectable alternate style sheets.
628 Refers to the first document in a collection of documents. This link
629 type tells search engines which document is considered by the author
630 to be the starting point of the collection.
633 Refers to the next document in a linear sequence of documents. User
634 agents may choose to preload the "next" document, to reduce the
638 Refers to the previous document in an ordered series of documents.
639 Some user agents also support the synonym "Previous".
642 Refers to a document serving as a table of contents.
643 Some user agents also support the synonym ToC (from "Table of Contents").
646 Refers to a document providing an index for the current document.
649 Refers to a document providing a glossary of terms that pertain to the
653 Refers to a copyright statement for the current document.
656 Refers to a document serving as a chapter in a collection of documents.
659 Refers to a document serving as a section in a collection of documents.
662 Refers to a document serving as a subsection in a collection of
666 Refers to a document serving as an appendix in a collection of
670 Refers to a document offering help (more information, links to other
671 sources information, etc.)
674 Refers to a bookmark. A bookmark is a link to a key entry point
675 within an extended document. The title attribute may be used, for
676 example, to label the bookmark. Note that several bookmarks may be
677 defined in each document.
679 Some more were added, like top. --Zas */
703 LT_ALTERNATE_STYLESHEET
,
707 enum hlink_direction
{
714 enum hlink_type type
;
715 enum hlink_direction direction
;
716 unsigned char *content_type
;
717 unsigned char *media
;
719 unsigned char *hreflang
;
720 unsigned char *title
;
723 /* Not implemented yet.
724 unsigned char *charset;
725 unsigned char *target;
727 unsigned char *class;
732 struct lt_default_name
{
733 enum hlink_type type
;
738 /* XXX: Keep the (really really ;) default name first */
739 static struct lt_default_name lt_names
[] = {
740 { LT_START
, "start" },
742 { LT_START
, "home" },
743 { LT_PARENT
, "parent" },
746 { LT_PREV
, "previous" },
748 { LT_CONTENTS
, "contents" },
749 { LT_CONTENTS
, "toc" },
750 { LT_INDEX
, "index" },
751 { LT_GLOSSARY
, "glossary" },
752 { LT_CHAPTER
, "chapter" },
753 { LT_SECTION
, "section" },
754 { LT_SUBSECTION
, "subsection" },
755 { LT_SUBSECTION
, "child" },
756 { LT_SUBSECTION
, "sibling" },
757 { LT_APPENDIX
, "appendix" },
759 { LT_SEARCH
, "search" },
760 { LT_BOOKMARK
, "bookmark" },
761 { LT_ALTERNATE_LANG
, "alt. language" },
762 { LT_ALTERNATE_MEDIA
, "alt. media" },
763 { LT_ALTERNATE_STYLESHEET
, "alt. stylesheet" },
764 { LT_STYLESHEET
, "stylesheet" },
765 { LT_ALTERNATE
, "alternate" },
766 { LT_COPYRIGHT
, "copyright" },
767 { LT_AUTHOR
, "author" },
768 { LT_AUTHOR
, "made" },
769 { LT_AUTHOR
, "owner" },
774 /* Search for default name for this link according to its type. */
775 static unsigned char *
776 get_lt_default_name(struct hlink
*link
)
778 struct lt_default_name
*entry
= lt_names
;
782 while (entry
&& entry
->str
) {
783 if (entry
->type
== link
->type
) return entry
->str
;
791 html_link_clear(struct hlink
*link
)
795 mem_free_if(link
->content_type
);
796 mem_free_if(link
->media
);
797 mem_free_if(link
->href
);
798 mem_free_if(link
->hreflang
);
799 mem_free_if(link
->title
);
800 mem_free_if(link
->lang
);
801 mem_free_if(link
->name
);
803 memset(link
, 0, sizeof(*link
));
806 /* Parse a link and return results in @link.
807 * It tries to identify known types. */
809 html_link_parse(struct html_context
*html_context
, unsigned char *a
,
815 memset(link
, 0, sizeof(*link
));
817 link
->href
= get_url_val(a
, "href", html_context
->doc_cp
);
818 if (!link
->href
) return 0;
820 link
->lang
= get_attr_val(a
, "lang", html_context
->doc_cp
);
821 link
->hreflang
= get_attr_val(a
, "hreflang", html_context
->doc_cp
);
822 link
->title
= get_attr_val(a
, "title", html_context
->doc_cp
);
823 link
->content_type
= get_attr_val(a
, "type", html_context
->doc_cp
);
824 link
->media
= get_attr_val(a
, "media", html_context
->doc_cp
);
826 link
->name
= get_attr_val(a
, "rel", html_context
->doc_cp
);
828 link
->direction
= LD_REL
;
830 link
->name
= get_attr_val(a
, "rev", html_context
->doc_cp
);
831 if (link
->name
) link
->direction
= LD_REV
;
834 if (!link
->name
) return 1;
837 for (i
= 0; lt_names
[i
].str
; i
++)
838 if (!c_strcasecmp(link
->name
, lt_names
[i
].str
)) {
839 link
->type
= lt_names
[i
].type
;
843 if (c_strcasestr(link
->name
, "icon") ||
844 (link
->content_type
&& c_strcasestr(link
->content_type
, "icon"))) {
845 link
->type
= LT_ICON
;
847 } else if (c_strcasestr(link
->name
, "alternate")) {
848 link
->type
= LT_ALTERNATE
;
850 link
->type
= LT_ALTERNATE_LANG
;
851 else if (c_strcasestr(link
->name
, "stylesheet") ||
852 (link
->content_type
&& c_strcasestr(link
->content_type
, "css")))
853 link
->type
= LT_ALTERNATE_STYLESHEET
;
854 else if (link
->media
)
855 link
->type
= LT_ALTERNATE_MEDIA
;
857 } else if (link
->content_type
&& c_strcasestr(link
->content_type
, "css")) {
858 link
->type
= LT_STYLESHEET
;
865 html_link(struct html_context
*html_context
, unsigned char *a
,
866 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
868 int link_display
= html_context
->options
->meta_link_display
;
872 int name_neq_title
= 0;
876 if (!link_display
) return;
878 if (!html_link_parse(html_context
, a
, &link
)) return;
879 if (!link
.href
) goto free_and_return
;
882 if (link
.type
== LT_STYLESHEET
883 && supports_html_media_attr(link
.media
)) {
884 int len
= strlen(link
.href
);
886 import_css_stylesheet(&html_context
->css_styles
,
887 html_context
->base_href
, link
.href
, len
);
890 if (!link_display
) goto free_and_return
;
893 /* Ignore few annoying links.. */
894 if (link_display
< 5 &&
895 (link
.type
== LT_ICON
||
896 link
.type
== LT_AUTHOR
||
897 link
.type
== LT_STYLESHEET
||
898 link
.type
== LT_ALTERNATE_STYLESHEET
)) goto free_and_return
;
900 if (!link
.name
|| link
.type
!= LT_UNKNOWN
)
901 /* Give preference to our default names for known types. */
902 name
= get_lt_default_name(&link
);
906 if (!name
) goto free_and_return
;
907 if (!init_string(&text
)) goto free_and_return
;
909 html_focusable(html_context
, a
);
912 add_to_string(&text
, link
.title
);
913 name_neq_title
= strcmp(link
.title
, name
);
915 add_to_string(&text
, name
);
917 if (link_display
== 1) goto put_link_line
; /* Only title */
919 #define APPEND(what) do { \
920 add_to_string(&text, first ? " (" : ", "); \
921 add_to_string(&text, (what)); \
925 if (name_neq_title
) {
929 if (link_display
>= 3 && link
.hreflang
) {
930 APPEND(link
.hreflang
);
933 if (link_display
>= 4 && link
.content_type
) {
934 APPEND(link
.content_type
);
937 if (link
.lang
&& link
.type
== LT_ALTERNATE_LANG
&&
938 (link_display
< 3 || (link
.hreflang
&&
939 c_strcasecmp(link
.hreflang
, link
.lang
)))) {
949 if (!first
) add_char_to_string(&text
, ')');
953 unsigned char *prefix
= (link
.direction
== LD_REL
)
954 ? "Link: " : "Reverse link: ";
955 unsigned char *link_name
= (text
.length
)
956 ? text
.source
: name
;
958 put_link_line(prefix
, link_name
, link
.href
,
959 html_context
->base_target
, html_context
);
961 if (text
.source
) done_string(&text
);
965 html_link_clear(&link
);