[wip] Initial stab on inline images support, with many problems
[elinks/images.git] / src / document / html / parser / link.c
blob91733e2669a71ee0b07d2be74fd63b6a81c70134
1 /* HTML parser */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #ifndef _GNU_SOURCE
8 #define _GNU_SOURCE /* strcasestr() */
9 #endif
11 #include <libgen.h> /* basename() */
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
18 #include "elinks.h"
20 #include "bfu/listmenu.h"
21 #include "bfu/menu.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"
40 /* Unsafe macros */
41 #include "document/html/internal.h"
44 void
45 html_a(struct html_context *html_context, unsigned char *a,
46 unsigned char *xxx3, unsigned char *xxx4, unsigned char **xxx5)
48 unsigned char *href;
50 href = get_url_val(a, "href", html_context->doc_cp);
51 if (href) {
52 unsigned char *target;
54 mem_free_set(&format.link,
55 join_urls(html_context->base_href,
56 trim_chars(href, ' ', 0)));
58 mem_free(href);
60 target = get_target(html_context->options, a);
61 if (target) {
62 mem_free_set(&format.target, target);
63 } else {
64 mem_free_set(&format.target, stracpy(html_context->base_target));
67 if (0) {
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;
74 #endif
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;
81 #endif
82 } else {
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);
93 } else {
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
106 * of @label string.
107 * In either case, it may return NULL if a memory
108 * allocation failure occurs.
109 * Example:
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);
116 int left_part_len;
117 int right_part_len;
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)
126 right_part_len--;
128 new_label = mem_alloc(max_len + 1);
129 if (!new_label) return NULL;
131 if (left_part_len)
132 memcpy(new_label, label, left_part_len);
134 new_label[left_part_len] = '*';
136 if (right_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';
142 return new_label;
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;
151 int len;
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])) {
160 break;
163 len -= start - src;
165 filename = memacpy(start, len);
166 if (filename) {
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);
171 mem_free(filename);
174 return text;
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);
187 mem_free(label);
189 return formatted_label;
192 static void
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;
213 static void
214 inline_image(struct html_context *html_context, unsigned char *a, unsigned char *src)
216 #ifdef CONFIG_IMAGES
217 int width = -1, height = -1;
218 unsigned char *dimstr;
219 unsigned char *url;
220 struct uri *uri;
222 url = join_urls(html_context->base_href, src);
223 if (!url) return;
224 uri = get_uri(url, URI_BASE);
225 mem_free(url);
226 if (!uri) return;
228 dimstr = get_attr_val(a, "width", html_context->doc_cp);
229 if (dimstr) {
230 width = atoi(dimstr);
231 mem_free(dimstr);
233 dimstr = get_attr_val(a, "height", html_context->doc_cp);
234 if (dimstr) {
235 height = atoi(dimstr);
236 mem_free(dimstr);
239 html_context->special_f(html_context, SP_IMAGE, uri, width, height);
241 done_uri(uri);
242 #endif
245 static void
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);
264 if (usemap_attr) {
265 unsigned char *joined_urls = join_urls(html_context->base_href,
266 usemap_attr);
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);
278 format.form = NULL;
279 format.style.attr |= AT_BOLD;
280 usemap = 1;
283 ismap = format.link
284 && has_attr(a, "ismap", html_context->doc_cp)
285 && !usemap;
287 if (display_style == 2 || display_style == 3) {
288 label = get_attr_val(a, "alt", html_context->doc_cp);
289 if (!label)
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);
302 if (src)
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
310 * link on them ?
311 * If not, just exit now. */
312 if (!options->images && !format.link) {
313 mem_free_if(src);
314 if (usemap) pop_html_element(html_context);
315 return;
318 add_brackets = 1;
320 if (usemap) {
321 label = stracpy("USEMAP");
322 } else if (ismap) {
323 label = stracpy("ISMAP");
324 } else {
325 if (display_style == 3)
326 label = get_image_filename_from_src(options->image_link.filename_maxlen, src);
329 } else {
330 label = get_image_label(options->image_link.label_maxlen, label);
333 if (!label || !*label) {
334 mem_free_set(&label, NULL);
335 add_brackets = 1;
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);
345 if (label) {
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);
359 } else {
360 if (src) {
361 format.image = join_urls(html_context->base_href, src);
364 format.title = get_attr_val(a, "title", html_context->doc_cp);
366 if (ismap) {
367 unsigned char *new_link;
369 html_stack_dup(html_context, ELEMENT_KILLABLE);
370 new_link = straconcat(format.link, "?0,0", (unsigned char *) NULL);
371 if (new_link)
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);
382 mem_free(label);
385 mem_free_if(src);
386 if (usemap) pop_html_element(html_context);
389 void
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. */
397 void
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);
408 format.form = 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);
423 void
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);
430 if (!code) return;
432 alt = get_attr_val(a, "alt", html_context->doc_cp);
434 html_focusable(html_context, a);
436 if (alt && *alt) {
437 put_link_line("Applet: ", alt, code,
438 html_context->options->framename, html_context);
439 } else {
440 put_link_line("", "Applet", code,
441 html_context->options->framename, html_context);
444 mem_free_if(alt);
445 mem_free(code);
448 void
449 html_audio(struct html_context *html_context, unsigned char *a,
450 unsigned char *xxx3, unsigned char *xxx4, unsigned char **xxx5)
452 unsigned char *url;
454 /* This just places a link where a audio element would be. */
456 url = get_url_val(a, "src", html_context->doc_cp);
457 if (!url) return;
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);
466 mem_free(url);
469 static void
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);
477 if (!url) return;
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("");
482 if (!name) {
483 mem_free(url);
484 return;
487 html_focusable(html_context, a);
489 if (*name) {
490 put_link_line("IFrame: ", name, url,
491 html_context->options->framename, html_context);
492 } else {
493 put_link_line("", "IFrame", url,
494 html_context->options->framename, html_context);
497 mem_free(name);
498 mem_free(url);
501 void
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);
508 void
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);
520 if (!url) return;
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);
534 } else {
535 unsigned char *name;
537 name = get_attr_val(a, "standby", html_context->doc_cp);
539 html_focusable(html_context, a);
541 if (name && *name) {
542 put_link_line("Object: ", name, url,
543 html_context->options->framename,
544 html_context);
545 } else {
546 put_link_line("Object: ", type, url,
547 html_context->options->framename,
548 html_context);
551 mem_free_if(name);
554 mem_free(type);
555 mem_free(url);
558 void
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);
572 return;
575 /* If there is no extension we want to get the default mime/type
576 * anyway? */
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);
583 } else {
584 /* We will just emulate <iframe>. */
585 html_iframe_do(a, object_src, html_context);
588 mem_free_if(type);
589 mem_free_set(&object_src, NULL);
592 void
593 html_video(struct html_context *html_context, unsigned char *a,
594 unsigned char *xxx3, unsigned char *xxx4, unsigned char **xxx5)
596 unsigned char *url;
598 /* This just places a link where a video element would be. */
600 url = get_url_val(a, "src", html_context->doc_cp);
601 if (!url) return;
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);
610 mem_free(url);
613 /* Link types:
615 Alternate
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
620 medium (or media).
622 Stylesheet
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.
627 Start
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.
632 Next
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
635 perceived load time.
637 Prev
638 Refers to the previous document in an ordered series of documents.
639 Some user agents also support the synonym "Previous".
641 Contents
642 Refers to a document serving as a table of contents.
643 Some user agents also support the synonym ToC (from "Table of Contents").
645 Index
646 Refers to a document providing an index for the current document.
648 Glossary
649 Refers to a document providing a glossary of terms that pertain to the
650 current document.
652 Copyright
653 Refers to a copyright statement for the current document.
655 Chapter
656 Refers to a document serving as a chapter in a collection of documents.
658 Section
659 Refers to a document serving as a section in a collection of documents.
661 Subsection
662 Refers to a document serving as a subsection in a collection of
663 documents.
665 Appendix
666 Refers to a document serving as an appendix in a collection of
667 documents.
669 Help
670 Refers to a document offering help (more information, links to other
671 sources information, etc.)
673 Bookmark
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 */
681 enum hlink_type {
682 LT_UNKNOWN = 0,
683 LT_START,
684 LT_PARENT,
685 LT_NEXT,
686 LT_PREV,
687 LT_CONTENTS,
688 LT_INDEX,
689 LT_GLOSSARY,
690 LT_CHAPTER,
691 LT_SECTION,
692 LT_SUBSECTION,
693 LT_APPENDIX,
694 LT_HELP,
695 LT_SEARCH,
696 LT_BOOKMARK,
697 LT_COPYRIGHT,
698 LT_AUTHOR,
699 LT_ICON,
700 LT_ALTERNATE,
701 LT_ALTERNATE_LANG,
702 LT_ALTERNATE_MEDIA,
703 LT_ALTERNATE_STYLESHEET,
704 LT_STYLESHEET,
707 enum hlink_direction {
708 LD_UNKNOWN = 0,
709 LD_REV,
710 LD_REL,
713 struct hlink {
714 enum hlink_type type;
715 enum hlink_direction direction;
716 unsigned char *content_type;
717 unsigned char *media;
718 unsigned char *href;
719 unsigned char *hreflang;
720 unsigned char *title;
721 unsigned char *lang;
722 unsigned char *name;
723 /* Not implemented yet.
724 unsigned char *charset;
725 unsigned char *target;
726 unsigned char *id;
727 unsigned char *class;
728 unsigned char *dir;
732 struct lt_default_name {
733 enum hlink_type type;
734 unsigned char *str;
737 /* TODO: i18n */
738 /* XXX: Keep the (really really ;) default name first */
739 static struct lt_default_name lt_names[] = {
740 { LT_START, "start" },
741 { LT_START, "top" },
742 { LT_START, "home" },
743 { LT_PARENT, "parent" },
744 { LT_PARENT, "up" },
745 { LT_NEXT, "next" },
746 { LT_PREV, "previous" },
747 { LT_PREV, "prev" },
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" },
758 { LT_HELP, "help" },
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" },
770 { LT_ICON, "icon" },
771 { LT_UNKNOWN, NULL }
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;
780 assert(link);
782 while (entry && entry->str) {
783 if (entry->type == link->type) return entry->str;
784 entry++;
787 return "unknown";
790 static void
791 html_link_clear(struct hlink *link)
793 assert(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. */
808 static int
809 html_link_parse(struct html_context *html_context, unsigned char *a,
810 struct hlink *link)
812 int i;
814 assert(a && link);
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);
827 if (link->name) {
828 link->direction = LD_REL;
829 } else {
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;
836 /* TODO: fastfind */
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;
840 return 1;
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;
849 if (link->lang)
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;
861 return 1;
864 void
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;
869 unsigned char *name;
870 struct hlink link;
871 struct string text;
872 int name_neq_title = 0;
873 int first = 1;
875 #ifndef CONFIG_CSS
876 if (!link_display) return;
877 #endif
878 if (!html_link_parse(html_context, a, &link)) return;
879 if (!link.href) goto free_and_return;
881 #ifdef CONFIG_CSS
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;
891 #endif
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);
903 else
904 name = link.name;
906 if (!name) goto free_and_return;
907 if (!init_string(&text)) goto free_and_return;
909 html_focusable(html_context, a);
911 if (link.title) {
912 add_to_string(&text, link.title);
913 name_neq_title = strcmp(link.title, name);
914 } else
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)); \
922 first = 0; \
923 } while (0)
925 if (name_neq_title) {
926 APPEND(name);
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)))) {
940 APPEND(link.lang);
943 if (link.media) {
944 APPEND(link.media);
947 #undef APPEND
949 if (!first) add_char_to_string(&text, ')');
951 put_link_line:
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);
964 free_and_return:
965 html_link_clear(&link);