1 /* tag.c -- Functions to handle Info tags (that is, the special
2 construct for images, not the "tag table" of starting position.)
3
4 Copyright 2012-2019 Free Software Foundation, Inc.
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18
19 #include "info.h"
20 #include "tag.h"
21 #include "info-utils.h"
22
23 struct tag_handler
24 {
25 const char *name;
26 size_t len;
27 int (*handler) (char *, struct text_buffer *);
28 };
29
30 struct info_tag
31 {
32 struct info_tag *next;
33 char *kw;
34 char *val;
35 };
36
37 static void
info_tag_free(struct info_tag * tag)38 info_tag_free (struct info_tag *tag)
39 {
40 while (tag)
41 {
42 struct info_tag *next = tag->next;
43 free (tag->kw);
44 free (tag->val);
45 free (tag);
46 tag = next;
47 }
48 }
49
50
51 /* See if KW is one of the tags in the list starting at TAG. */
52
53 static struct info_tag *
info_tag_find(struct info_tag * tag,const char * kw)54 info_tag_find (struct info_tag *tag, const char *kw)
55 {
56 for (; tag; tag = tag->next)
57 if (strcmp (tag->kw, kw) == 0)
58 return tag;
59 return NULL;
60 }
61
62
63 /* Found a keyword when parsing the full tag string: alt, text, etc.
64 Return the new tag, update *TMPBUF_PTR and set *KW. */
65
66 static struct info_tag *
tag_found_keyword(struct text_buffer * tmpbuf_ptr,char ** kw)67 tag_found_keyword (struct text_buffer *tmpbuf_ptr, char **kw)
68 {
69 struct info_tag *tag = xmalloc (sizeof (*tag));
70 tag->next = NULL; /* have to update in caller */
71
72 text_buffer_add_char (tmpbuf_ptr, 0);
73 if (*kw != tmpbuf_ptr->base) { /* in case tmpbuf got realloc-ed */
74 *kw = tmpbuf_ptr->base; /* ick */
75 }
76 tag->kw = xstrdup (*kw);
77 tag->val = xstrdup (*kw + strlen(*kw) + 1);
78 text_buffer_reset (tmpbuf_ptr);
79
80 return tag;
81 }
82
83 /* Handle the image tag. */
84
85 static int
tag_image(char * text,struct text_buffer * outbuf)86 tag_image (char *text, struct text_buffer *outbuf)
87 {
88 mbi_iterator_t iter;
89 enum { state_kw, state_val, state_qstr, state_delim } state = state_kw;
90 struct text_buffer tmpbuf;
91 char *kw;
92 struct info_tag *tag_head = NULL, *tag;
93 int escaped = 0;
94
95 text_buffer_init (&tmpbuf);
96 for (mbi_init (iter, text, strlen (text)); mbi_avail (iter);
97 mbi_advance (iter))
98 {
99 const char *cur_ptr;
100 size_t cur_len;
101
102 if (mb_isspace (mbi_cur (iter)))
103 {
104 if (state == state_val)
105 {
106 struct info_tag *new_kw = tag_found_keyword (&tmpbuf, &kw);
107 new_kw->next = tag_head;
108 tag_head = new_kw;
109 state = state_delim;
110 continue;
111 }
112 if (state == state_delim)
113 continue;
114 }
115 else if (state == state_delim)
116 state = state_kw;
117 cur_len = mb_len (mbi_cur (iter));
118 cur_ptr = mbi_cur_ptr (iter);
119
120 if (state == state_qstr && escaped)
121 {
122 escaped = 0;
123 }
124 else if (cur_len == 1)
125 {
126 switch (*cur_ptr)
127 {
128 case '=':
129 if (state != state_kw)
130 break;
131 text_buffer_add_char (&tmpbuf, 0);
132 kw = tmpbuf.base;
133 if (!mbi_avail (iter))
134 break;
135 mbi_advance (iter);
136 state = state_val;
137 cur_len = mb_len (mbi_cur (iter));
138 cur_ptr = mbi_cur_ptr (iter);
139 if (!(cur_len == 1 && *cur_ptr == '"'))
140 break;
141 /* fall through */
142
143 case '"':
144 if (state == state_val)
145 {
146 state = state_qstr;
147 continue;
148 }
149 if (state == state_qstr)
150 {
151 struct info_tag *new_kw = tag_found_keyword (&tmpbuf, &kw);
152 new_kw->next = tag_head;
153 tag_head = new_kw;
154 state = state_delim;
155 continue;
156 }
157 break;
158
159 case '\\':
160 if (state == state_qstr)
161 {
162 escaped = 1;
163 continue;
164 }
165 }
166 }
167 text_buffer_add_string (&tmpbuf, cur_ptr, cur_len);
168 }
169
170 tag = info_tag_find (tag_head, "text");
171 if (!tag)
172 tag = info_tag_find (tag_head, "alt");
173
174 if (tag)
175 {
176 text_buffer_add_string (outbuf, tag->val, strlen (tag->val));
177 }
178
179 text_buffer_free (&tmpbuf);
180 info_tag_free (tag_head);
181 return 0;
182 }
183
184
185 /* We don't do anything with the index tag; it'll just be ignored. */
186
187 static struct tag_handler tagtab[] = {
188 { "image", 5, tag_image },
189 { "index", 5, NULL },
190 { NULL }
191 };
192
193 static struct tag_handler *
find_tag_handler(char * tag,size_t taglen)194 find_tag_handler (char *tag, size_t taglen)
195 {
196 struct tag_handler *tp;
197
198 for (tp = tagtab; tp->name; tp++)
199 if (taglen >= tp->len && strncmp (tp->name, tag, tp->len) == 0)
200 return tp;
201 return NULL;
202 }
203
204 /* Expand \b[...\b] construct at *INPUT. If encountered, append the
205 expanded text to OUTBUF, advance *INPUT past the tag, and return 1.
206 Otherwise, return 0. If it is an index tag, set IS_INDEX to 1.
207 *INPUT points into a null-terminated area which may however contain other
208 null characters. INPUT_END points to the end of this area. */
209 int
tag_expand(char ** input,char * input_end,struct text_buffer * outbuf,int * is_index)210 tag_expand (char **input, char *input_end,
211 struct text_buffer *outbuf, int *is_index)
212 {
213 char *p = *input;
214 char *q;
215 size_t len;
216 struct tag_handler *tp;
217
218 if (p >= input_end - 3
219 || memcmp(p, "\0\b[", 3) != 0) /* opening magic? */
220 return 0;
221
222 p += 3;
223 q = p + strlen (p);
224 if (q >= input_end - 3
225 || memcmp (q + 1, "\b]", 2)) /* closing magic? */
226 return 0; /* Not a proper tag. */
227
228 /* Output is different for index nodes */
229 if (!strncmp ("index", p, strlen ("index")))
230 *is_index = 1;
231
232 len = strcspn (p, " \t"); /* tag name */
233 tp = find_tag_handler (p, len);
234 if (tp && tp->handler)
235 {
236 while (p[len] == ' ' || p[len] == '\t')
237 ++len; /* move past whitespace */
238
239 tp->handler (p + len, outbuf);
240 }
241 *input = q + 3;
242 return 1;
243 }
244