| /* tag.c -- Functions to handle Info tags (that is, the special |
| construct for images, not the "tag table" of starting position.) |
| |
| Copyright 2012-2025 Free Software Foundation, Inc. |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #include "info.h" |
| #include "tag.h" |
| #include "scan.h" |
| #include "util.h" |
| |
| struct tag_handler |
| { |
| const char *name; |
| size_t len; |
| int (*handler) (char *, struct text_buffer *); |
| }; |
| |
| struct info_tag |
| { |
| struct info_tag *next; |
| char *kw; |
| char *val; |
| }; |
| |
| static void |
| info_tag_free (struct info_tag *tag) |
| { |
| while (tag) |
| { |
| struct info_tag *next = tag->next; |
| free (tag->kw); |
| free (tag->val); |
| free (tag); |
| tag = next; |
| } |
| } |
| |
| |
| /* See if KW is one of the tags in the list starting at TAG. */ |
| |
| static struct info_tag * |
| info_tag_find (struct info_tag *tag, const char *kw) |
| { |
| for (; tag; tag = tag->next) |
| if (strcmp (tag->kw, kw) == 0) |
| return tag; |
| return NULL; |
| } |
| |
| |
| /* Found a keyword when parsing the full tag string: alt, text, etc. |
| Return the new tag, update *TMPBUF_PTR and set *KW. */ |
| |
| static struct info_tag * |
| tag_found_keyword (struct text_buffer *tmpbuf_ptr, char **kw) |
| { |
| struct info_tag *tag = xmalloc (sizeof (*tag)); |
| tag->next = NULL; /* have to update in caller */ |
| |
| text_buffer_add_char (tmpbuf_ptr, 0); |
| if (*kw != tmpbuf_ptr->base) { /* in case tmpbuf got realloc-ed */ |
| *kw = tmpbuf_ptr->base; /* ick */ |
| } |
| tag->kw = xstrdup (*kw); |
| tag->val = xstrdup (*kw + strlen(*kw) + 1); |
| text_buffer_reset (tmpbuf_ptr); |
| |
| return tag; |
| } |
| |
| /* Handle the image tag. */ |
| |
| static int |
| tag_image (char *text, struct text_buffer *outbuf) |
| { |
| mbi_iterator_t iter; |
| enum { state_kw, state_val, state_qstr, state_delim } state = state_kw; |
| struct text_buffer tmpbuf; |
| char *kw; |
| struct info_tag *tag_head = NULL, *tag; |
| int escaped = 0; |
| |
| text_buffer_init (&tmpbuf); |
| for (mbi_init (iter, text, strlen (text)); mbi_avail (iter); |
| mbi_advance (iter)) |
| { |
| const char *cur_ptr; |
| size_t cur_len; |
| |
| if (mb_isspace (mbi_cur (iter))) |
| { |
| if (state == state_val) |
| { |
| struct info_tag *new_kw = tag_found_keyword (&tmpbuf, &kw); |
| new_kw->next = tag_head; |
| tag_head = new_kw; |
| state = state_delim; |
| continue; |
| } |
| if (state == state_delim) |
| continue; |
| } |
| else if (state == state_delim) |
| state = state_kw; |
| cur_len = mb_len (mbi_cur (iter)); |
| cur_ptr = mbi_cur_ptr (iter); |
| |
| if (state == state_qstr && escaped) |
| { |
| escaped = 0; |
| } |
| else if (cur_len == 1) |
| { |
| switch (*cur_ptr) |
| { |
| case '=': |
| if (state != state_kw) |
| break; |
| text_buffer_add_char (&tmpbuf, 0); |
| kw = tmpbuf.base; |
| if (!mbi_avail (iter)) |
| break; |
| mbi_advance (iter); |
| state = state_val; |
| cur_len = mb_len (mbi_cur (iter)); |
| cur_ptr = mbi_cur_ptr (iter); |
| if (!(cur_len == 1 && *cur_ptr == '"')) |
| break; |
| /* fall through */ |
| |
| case '"': |
| if (state == state_val) |
| { |
| state = state_qstr; |
| continue; |
| } |
| if (state == state_qstr) |
| { |
| struct info_tag *new_kw = tag_found_keyword (&tmpbuf, &kw); |
| new_kw->next = tag_head; |
| tag_head = new_kw; |
| state = state_delim; |
| continue; |
| } |
| break; |
| |
| case '\\': |
| if (state == state_qstr) |
| { |
| escaped = 1; |
| continue; |
| } |
| } |
| } |
| text_buffer_add_string (&tmpbuf, cur_ptr, cur_len); |
| } |
| |
| tag = info_tag_find (tag_head, "text"); |
| if (!tag) |
| tag = info_tag_find (tag_head, "alt"); |
| |
| if (tag) |
| { |
| text_buffer_add_string (outbuf, tag->val, strlen (tag->val)); |
| } |
| |
| text_buffer_free (&tmpbuf); |
| info_tag_free (tag_head); |
| return 0; |
| } |
| |
| |
| /* We don't do anything with the index tag; it'll just be ignored. */ |
| |
| static struct tag_handler tagtab[] = { |
| { "image", 5, tag_image }, |
| { "index", 5, NULL }, |
| { NULL } |
| }; |
| |
| static struct tag_handler * |
| find_tag_handler (char *tag, size_t taglen) |
| { |
| struct tag_handler *tp; |
| |
| for (tp = tagtab; tp->name; tp++) |
| if (taglen >= tp->len && strncmp (tp->name, tag, tp->len) == 0) |
| return tp; |
| return NULL; |
| } |
| |
| /* Expand \b[...\b] construct at *INPUT. If encountered, append the |
| expanded text to OUTBUF, advance *INPUT past the tag, and return 1. |
| Otherwise, return 0. If it is an index tag, set IS_INDEX to 1. |
| *INPUT points into a null-terminated area which may however contain other |
| null characters. INPUT_END points to the end of this area. */ |
| int |
| tag_expand (char **input, char *input_end, |
| struct text_buffer *outbuf, int *is_index) |
| { |
| char *p = *input; |
| char *q; |
| size_t len; |
| struct tag_handler *tp; |
| |
| if (p >= input_end - 3 |
| || memcmp(p, "\0\b[", 3) != 0) /* opening magic? */ |
| return 0; |
| |
| p += 3; |
| q = p + strlen (p); |
| if (q >= input_end - 3 |
| || memcmp (q + 1, "\b]", 2)) /* closing magic? */ |
| return 0; /* Not a proper tag. */ |
| |
| /* Output is different for index nodes */ |
| if (!strncmp ("index", p, strlen ("index"))) |
| *is_index = 1; |
| |
| len = strcspn (p, " \t"); /* tag name */ |
| tp = find_tag_handler (p, len); |
| if (tp && tp->handler) |
| { |
| while (p[len] == ' ' || p[len] == '\t') |
| ++len; /* move past whitespace */ |
| |
| tp->handler (p + len, outbuf); |
| } |
| *input = q + 3; |
| return 1; |
| } |