1 /*
2  * Attribute support code for Mini-XML, a small XML file parsing library.
3  *
4  * https://www.msweet.org/mxml
5  *
6  * Copyright © 2003-2019 by Michael R Sweet.
7  *
8  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
9  * information.
10  */
11 
12 /*
13  * Include necessary headers...
14  */
15 
16 #include "config.h"
17 #include "mxml-private.h"
18 
19 
20 /*
21  * Local functions...
22  */
23 
24 static int	mxml_set_attr(mxml_node_t *node, const char *name, char *value);
25 
26 
27 /*
28  * 'mxmlElementDeleteAttr()' - Delete an attribute.
29  *
30  * @since Mini-XML 2.4@
31  */
32 
33 void
mxmlElementDeleteAttr(mxml_node_t * node,const char * name)34 mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */
35                       const char  *name)/* I - Attribute name */
36 {
37   int		i;			/* Looping var */
38   _mxml_attr_t	*attr;			/* Cirrent attribute */
39 
40 
41 #ifdef DEBUG
42   fprintf(stderr, "mxmlElementDeleteAttr(node=%p, name=\"%s\")\n",
43           node, name ? name : "(null)");
44 #endif /* DEBUG */
45 
46  /*
47   * Range check input...
48   */
49 
50   if (!node || node->type != MXML_ELEMENT || !name)
51     return;
52 
53  /*
54   * Look for the attribute...
55   */
56 
57   for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
58        i > 0;
59        i --, attr ++)
60   {
61 #ifdef DEBUG
62     printf("    %s=\"%s\"\n", attr->name, attr->value);
63 #endif /* DEBUG */
64 
65     if (!strcmp(attr->name, name))
66     {
67      /*
68       * Delete this attribute...
69       */
70 
71       free(attr->name);
72       free(attr->value);
73 
74       i --;
75       if (i > 0)
76         memmove(attr, attr + 1, i * sizeof(_mxml_attr_t));
77 
78       node->value.element.num_attrs --;
79 
80       if (node->value.element.num_attrs == 0)
81         free(node->value.element.attrs);
82       return;
83     }
84   }
85 }
86 
87 
88 /*
89  * 'mxmlElementGetAttr()' - Get an attribute.
90  *
91  * This function returns @code NULL@ if the node is not an element or the
92  * named attribute does not exist.
93  */
94 
95 const char *				/* O - Attribute value or @code NULL@ */
mxmlElementGetAttr(mxml_node_t * node,const char * name)96 mxmlElementGetAttr(mxml_node_t *node,	/* I - Element node */
97                    const char  *name)	/* I - Name of attribute */
98 {
99   int		i;			/* Looping var */
100   _mxml_attr_t	*attr;			/* Cirrent attribute */
101 
102 
103 #ifdef DEBUG
104   fprintf(stderr, "mxmlElementGetAttr(node=%p, name=\"%s\")\n",
105           node, name ? name : "(null)");
106 #endif /* DEBUG */
107 
108  /*
109   * Range check input...
110   */
111 
112   if (!node || node->type != MXML_ELEMENT || !name)
113     return (NULL);
114 
115  /*
116   * Look for the attribute...
117   */
118 
119   for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
120        i > 0;
121        i --, attr ++)
122   {
123 #ifdef DEBUG
124     printf("    %s=\"%s\"\n", attr->name, attr->value);
125 #endif /* DEBUG */
126 
127     if (!strcmp(attr->name, name))
128     {
129 #ifdef DEBUG
130       printf("    Returning \"%s\"!\n", attr->value);
131 #endif /* DEBUG */
132       return (attr->value);
133     }
134   }
135 
136  /*
137   * Didn't find attribute, so return NULL...
138   */
139 
140 #ifdef DEBUG
141   puts("    Returning NULL!\n");
142 #endif /* DEBUG */
143 
144   return (NULL);
145 }
146 
147 
148 /*
149  * 'mxmlElementGetAttrByIndex()' - Get an element attribute by index.
150  *
151  * The index ("idx") is 0-based.  @code NULL@ is returned if the specified index
152  * is out of range.
153  *
154  * @since Mini-XML 2.11@
155  */
156 
157 const char *                            /* O - Attribute value */
mxmlElementGetAttrByIndex(mxml_node_t * node,int idx,const char ** name)158 mxmlElementGetAttrByIndex(
159     mxml_node_t *node,                  /* I - Node */
160     int         idx,                    /* I - Attribute index, starting at 0 */
161     const char  **name)                 /* O - Attribute name */
162 {
163   if (!node || node->type != MXML_ELEMENT || idx < 0 || idx >= node->value.element.num_attrs)
164     return (NULL);
165 
166   if (name)
167     *name = node->value.element.attrs[idx].name;
168 
169   return (node->value.element.attrs[idx].value);
170 }
171 
172 
173 /*
174  * 'mxmlElementGetAttrCount()' - Get the number of element attributes.
175  *
176  * @since Mini-XML 2.11@
177  */
178 
179 int                                     /* O - Number of attributes */
mxmlElementGetAttrCount(mxml_node_t * node)180 mxmlElementGetAttrCount(
181     mxml_node_t *node)                  /* I - Node */
182 {
183   if (node && node->type == MXML_ELEMENT)
184     return (node->value.element.num_attrs);
185   else
186     return (0);
187 }
188 
189 
190 /*
191  * 'mxmlElementSetAttr()' - Set an attribute.
192  *
193  * If the named attribute already exists, the value of the attribute
194  * is replaced by the new string value. The string value is copied
195  * into the element node. This function does nothing if the node is
196  * not an element.
197  */
198 
199 void
mxmlElementSetAttr(mxml_node_t * node,const char * name,const char * value)200 mxmlElementSetAttr(mxml_node_t *node,	/* I - Element node */
201                    const char  *name,	/* I - Name of attribute */
202                    const char  *value)	/* I - Attribute value */
203 {
204   char	*valuec;			/* Copy of value */
205 
206 
207 #ifdef DEBUG
208   fprintf(stderr, "mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n",
209           node, name ? name : "(null)", value ? value : "(null)");
210 #endif /* DEBUG */
211 
212  /*
213   * Range check input...
214   */
215 
216   if (!node || node->type != MXML_ELEMENT || !name)
217     return;
218 
219   if (value)
220     valuec = strdup(value);
221   else
222     valuec = NULL;
223 
224   if (mxml_set_attr(node, name, valuec))
225     free(valuec);
226 }
227 
228 
229 /*
230  * 'mxmlElementSetAttrf()' - Set an attribute with a formatted value.
231  *
232  * If the named attribute already exists, the value of the attribute
233  * is replaced by the new formatted string. The formatted string value is
234  * copied into the element node. This function does nothing if the node
235  * is not an element.
236  *
237  * @since Mini-XML 2.3@
238  */
239 
240 void
mxmlElementSetAttrf(mxml_node_t * node,const char * name,const char * format,...)241 mxmlElementSetAttrf(mxml_node_t *node,	/* I - Element node */
242                     const char  *name,	/* I - Name of attribute */
243                     const char  *format,/* I - Printf-style attribute value */
244 		    ...)		/* I - Additional arguments as needed */
245 {
246   va_list	ap;			/* Argument pointer */
247   char		*value;			/* Value */
248 
249 
250 #ifdef DEBUG
251   fprintf(stderr,
252           "mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n",
253           node, name ? name : "(null)", format ? format : "(null)");
254 #endif /* DEBUG */
255 
256  /*
257   * Range check input...
258   */
259 
260   if (!node || node->type != MXML_ELEMENT || !name || !format)
261     return;
262 
263  /*
264   * Format the value...
265   */
266 
267   va_start(ap, format);
268   value = _mxml_vstrdupf(format, ap);
269   va_end(ap);
270 
271   if (!value)
272     mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
273                name, node->value.element.name);
274   else if (mxml_set_attr(node, name, value))
275     free(value);
276 }
277 
278 
279 /*
280  * 'mxml_set_attr()' - Set or add an attribute name/value pair.
281  */
282 
283 static int				/* O - 0 on success, -1 on failure */
mxml_set_attr(mxml_node_t * node,const char * name,char * value)284 mxml_set_attr(mxml_node_t *node,	/* I - Element node */
285               const char  *name,	/* I - Attribute name */
286               char        *value)	/* I - Attribute value */
287 {
288   int		i;			/* Looping var */
289   _mxml_attr_t	*attr;			/* New attribute */
290 
291 
292  /*
293   * Look for the attribute...
294   */
295 
296   for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
297        i > 0;
298        i --, attr ++)
299     if (!strcmp(attr->name, name))
300     {
301      /*
302       * Free the old value as needed...
303       */
304 
305       if (attr->value)
306         free(attr->value);
307 
308       attr->value = value;
309 
310       return (0);
311     }
312 
313  /*
314   * Add a new attribute...
315   */
316 
317   if (node->value.element.num_attrs == 0)
318     attr = malloc(sizeof(_mxml_attr_t));
319   else
320     attr = realloc(node->value.element.attrs,
321                    (node->value.element.num_attrs + 1) * sizeof(_mxml_attr_t));
322 
323   if (!attr)
324   {
325     mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
326                name, node->value.element.name);
327     return (-1);
328   }
329 
330   node->value.element.attrs = attr;
331   attr += node->value.element.num_attrs;
332 
333   if ((attr->name = strdup(name)) == NULL)
334   {
335     mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
336                name, node->value.element.name);
337     return (-1);
338   }
339 
340   attr->value = value;
341 
342   node->value.element.num_attrs ++;
343 
344   return (0);
345 }
346