1/*
2 * "$Id: mxml-node.c,v 1.7 2004/09/17 18:38:21 rleigh Exp $"
3 *
4 * Node support code for mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003 by Michael Sweet.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * Contents:
19 *
20 *   stp_mxmlAdd()        - Add a node to a tree.
21 *   stp_mxmlDelete()     - Delete a node and all of its children.
22 *   stp_mxmlNewElement() - Create a new element node.
23 *   stp_mxmlNewInteger() - Create a new integer node.
24 *   stp_mxmlNewOpaque()  - Create a new opaque string.
25 *   stp_mxmlNewReal()    - Create a new real number node.
26 *   stp_mxmlNewText()    - Create a new text fragment node.
27 *   stp_mxmlRemove()     - Remove a node from its parent.
28 *   mxml_new()       - Create a new node.
29 */
30
31/*
32 * Include necessary headers...
33 */
34
35#include <gutenprint/mxml.h>
36#include "config.h"
37
38
39/*
40 * Local functions...
41 */
42
43static stp_mxml_node_t	*mxml_new(stp_mxml_node_t *parent, stp_mxml_type_t type);
44
45
46/*
47 * 'stp_mxmlAdd()' - Add a node to a tree.
48 *
49 * Adds the specified node to the parent. If the child argument is not
50 * NULL, puts the new node before or after the specified child depending
51 * on the value of the where argument. If the child argument is NULL,
52 * puts the new node at the beginning of the child list (STP_MXML_ADD_BEFORE)
53 * or at the end of the child list (STP_MXML_ADD_AFTER). The constant
54 * STP_MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.
55 */
56
57void
58stp_mxmlAdd(stp_mxml_node_t *parent,		/* I - Parent node */
59        int         where,		/* I - Where to add, STP_MXML_ADD_BEFORE or STP_MXML_ADD_AFTER */
60        stp_mxml_node_t *child,		/* I - Child node for where or STP_MXML_ADD_TO_PARENT */
61	stp_mxml_node_t *node)		/* I - Node to add */
62{
63/*  fprintf(stderr, "stp_mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent,
64         where, child, node);*/
65
66 /*
67  * Range check input...
68  */
69
70  if (!parent || !node)
71    return;
72
73 /*
74  * Remove the node from any existing parent...
75  */
76
77  if (node->parent)
78    stp_mxmlRemove(node);
79
80 /*
81  * Reset pointers...
82  */
83
84  node->parent = parent;
85
86  switch (where)
87  {
88    case STP_MXML_ADD_BEFORE :
89        if (!child || child == parent->child || child->parent != parent)
90	{
91	 /*
92	  * Insert as first node under parent...
93	  */
94
95	  node->next = parent->child;
96
97	  if (parent->child)
98	    parent->child->prev = node;
99	  else
100	    parent->last_child = node;
101
102	  parent->child = node;
103	}
104	else
105	{
106	 /*
107	  * Insert node before this child...
108	  */
109
110	  node->next = child;
111	  node->prev = child->prev;
112
113	  if (child->prev)
114	    child->prev->next = node;
115	  else
116	    parent->child = node;
117
118	  child->prev = node;
119	}
120        break;
121
122    case STP_MXML_ADD_AFTER :
123        if (!child || child == parent->last_child || child->parent != parent)
124	{
125	 /*
126	  * Insert as last node under parent...
127	  */
128
129	  node->parent = parent;
130	  node->prev   = parent->last_child;
131
132	  if (parent->last_child)
133	    parent->last_child->next = node;
134	  else
135	    parent->child = node;
136
137	  parent->last_child = node;
138        }
139	else
140	{
141	 /*
142	  * Insert node after this child...
143	  */
144
145	  node->prev = child;
146	  node->next = child->next;
147
148	  if (child->next)
149	    child->next->prev = node;
150	  else
151	    parent->last_child = node;
152
153	  child->next = node;
154	}
155        break;
156  }
157}
158
159
160/*
161 * 'stp_mxmlDelete()' - Delete a node and all of its children.
162 *
163 * If the specified node has a parent, this function first removes the
164 * node from its parent using the stp_mxmlRemove() function.
165 */
166
167void
168stp_mxmlDelete(stp_mxml_node_t *node)		/* I - Node to delete */
169{
170  int	i;				/* Looping var */
171
172
173/*  fprintf(stderr, "stp_mxmlDelete(node=%p)\n", node);*/
174
175 /*
176  * Range check input...
177  */
178
179  if (!node)
180    return;
181
182 /*
183  * Remove the node from its parent, if any...
184  */
185
186  stp_mxmlRemove(node);
187
188 /*
189  * Delete children...
190  */
191
192  while (node->child)
193    stp_mxmlDelete(node->child);
194
195 /*
196  * Now delete any node data...
197  */
198
199  switch (node->type)
200  {
201    case STP_MXML_ELEMENT :
202        if (node->value.element.name)
203	  free(node->value.element.name);
204
205	if (node->value.element.num_attrs)
206	{
207	  for (i = 0; i < node->value.element.num_attrs; i ++)
208	  {
209	    if (node->value.element.attrs[i].name)
210	      free(node->value.element.attrs[i].name);
211	    if (node->value.element.attrs[i].value)
212	      free(node->value.element.attrs[i].value);
213	  }
214
215          free(node->value.element.attrs);
216	}
217        break;
218    case STP_MXML_INTEGER :
219       /* Nothing to do */
220        break;
221    case STP_MXML_OPAQUE :
222        if (node->value.opaque)
223	  free(node->value.opaque);
224        break;
225    case STP_MXML_REAL :
226       /* Nothing to do */
227        break;
228    case STP_MXML_TEXT :
229        if (node->value.text.string)
230	  free(node->value.text.string);
231        break;
232  }
233
234 /*
235  * Free this node...
236  */
237
238  free(node);
239}
240
241
242/*
243 * 'stp_mxmlNewElement()' - Create a new element node.
244 *
245 * The new element node is added to the end of the specified parent's child
246 * list. The constant STP_MXML_NO_PARENT can be used to specify that the new
247 * element node has no parent.
248 */
249
250stp_mxml_node_t *				/* O - New node */
251stp_mxmlNewElement(stp_mxml_node_t *parent,	/* I - Parent node or STP_MXML_NO_PARENT */
252               const char  *name)	/* I - Name of element */
253{
254  stp_mxml_node_t	*node;			/* New node */
255
256
257 /*
258  * Range check input...
259  */
260
261  if (!name)
262    return (NULL);
263
264 /*
265  * Create the node and set the element name...
266  */
267
268  if ((node = mxml_new(parent, STP_MXML_ELEMENT)) != NULL)
269    node->value.element.name = strdup(name);
270
271  return (node);
272}
273
274
275/*
276 * 'stp_mxmlNewInteger()' - Create a new integer node.
277 *
278 * The new integer node is added to the end of the specified parent's child
279 * list. The constant STP_MXML_NO_PARENT can be used to specify that the new
280 * integer node has no parent.
281 */
282
283stp_mxml_node_t *				/* O - New node */
284stp_mxmlNewInteger(stp_mxml_node_t *parent,	/* I - Parent node or STP_MXML_NO_PARENT */
285               int         integer)	/* I - Integer value */
286{
287  stp_mxml_node_t	*node;			/* New node */
288
289
290 /*
291  * Range check input...
292  */
293
294  if (!parent)
295    return (NULL);
296
297 /*
298  * Create the node and set the element name...
299  */
300
301  if ((node = mxml_new(parent, STP_MXML_INTEGER)) != NULL)
302    node->value.integer = integer;
303
304  return (node);
305}
306
307
308/*
309 * 'stp_mxmlNewOpaque()' - Create a new opaque string.
310 *
311 * The new opaque node is added to the end of the specified parent's child
312 * list. The constant STP_MXML_NO_PARENT can be used to specify that the new
313 * opaque node has no parent. The opaque string must be nul-terminated and
314 * is copied into the new node.
315 */
316
317stp_mxml_node_t *				/* O - New node */
318stp_mxmlNewOpaque(stp_mxml_node_t *parent,	/* I - Parent node or STP_MXML_NO_PARENT */
319              const char  *opaque)	/* I - Opaque string */
320{
321  stp_mxml_node_t	*node;			/* New node */
322
323
324 /*
325  * Range check input...
326  */
327
328  if (!parent || !opaque)
329    return (NULL);
330
331 /*
332  * Create the node and set the element name...
333  */
334
335  if ((node = mxml_new(parent, STP_MXML_OPAQUE)) != NULL)
336    node->value.opaque = strdup(opaque);
337
338  return (node);
339}
340
341
342/*
343 * 'stp_mxmlNewReal()' - Create a new real number node.
344 *
345 * The new real number node is added to the end of the specified parent's
346 * child list. The constant STP_MXML_NO_PARENT can be used to specify that
347 * the new real number node has no parent.
348 */
349
350stp_mxml_node_t *				/* O - New node */
351stp_mxmlNewReal(stp_mxml_node_t *parent,	/* I - Parent node or STP_MXML_NO_PARENT */
352            double      real)		/* I - Real number value */
353{
354  stp_mxml_node_t	*node;			/* New node */
355
356
357 /*
358  * Range check input...
359  */
360
361  if (!parent)
362    return (NULL);
363
364 /*
365  * Create the node and set the element name...
366  */
367
368  if ((node = mxml_new(parent, STP_MXML_REAL)) != NULL)
369    node->value.real = real;
370
371  return (node);
372}
373
374
375/*
376 * 'stp_mxmlNewText()' - Create a new text fragment node.
377 *
378 * The new text node is added to the end of the specified parent's child
379 * list. The constant STP_MXML_NO_PARENT can be used to specify that the new
380 * text node has no parent. The whitespace parameter is used to specify
381 * whether leading whitespace is present before the node. The text
382 * string must be nul-terminated and is copied into the new node.
383 */
384
385stp_mxml_node_t *				/* O - New node */
386stp_mxmlNewText(stp_mxml_node_t *parent,	/* I - Parent node or STP_MXML_NO_PARENT */
387            int         whitespace,	/* I - 1 = leading whitespace, 0 = no whitespace */
388	    const char  *string)	/* I - String */
389{
390  stp_mxml_node_t	*node;			/* New node */
391
392
393 /*
394  * Range check input...
395  */
396
397  if (!parent || !string)
398    return (NULL);
399
400 /*
401  * Create the node and set the text value...
402  */
403
404  if ((node = mxml_new(parent, STP_MXML_TEXT)) != NULL)
405  {
406    node->value.text.whitespace = whitespace;
407    node->value.text.string     = strdup(string);
408  }
409
410  return (node);
411}
412
413
414/*
415 * 'stp_mxmlRemove()' - Remove a node from its parent.
416 *
417 * Does not free memory used by the node - use stp_mxmlDelete() for that.
418 * This function does nothing if the node has no parent.
419 */
420
421void
422stp_mxmlRemove(stp_mxml_node_t *node)		/* I - Node to remove */
423{
424 /*
425  * Range check input...
426  */
427
428/*  fprintf(stderr, "stp_mxmlRemove(node=%p)\n", node);*/
429
430  if (!node || !node->parent)
431    return;
432
433 /*
434  * Remove from parent...
435  */
436
437  if (node->prev)
438    node->prev->next = node->next;
439  else
440    node->parent->child = node->next;
441
442  if (node->next)
443    node->next->prev = node->prev;
444  else
445    node->parent->last_child = node->prev;
446
447  node->parent = NULL;
448  node->prev   = NULL;
449  node->next   = NULL;
450}
451
452
453/*
454 * 'mxml_new()' - Create a new node.
455 */
456
457static stp_mxml_node_t *			/* O - New node */
458mxml_new(stp_mxml_node_t *parent,		/* I - Parent node */
459         stp_mxml_type_t type)		/* I - Node type */
460{
461  stp_mxml_node_t	*node;			/* New node */
462
463
464 /*
465  * Allocate memory for the node...
466  */
467
468  if ((node = calloc(1, sizeof(stp_mxml_node_t))) == NULL)
469    return (NULL);
470
471 /*
472  * Set the node type...
473  */
474
475  node->type = type;
476
477 /*
478  * Add to the parent if present...
479  */
480
481  if (parent)
482    stp_mxmlAdd(parent, STP_MXML_ADD_AFTER, STP_MXML_ADD_TO_PARENT, node);
483
484 /*
485  * Return the new node...
486  */
487
488  return (node);
489}
490
491
492/*
493 * End of "$Id: mxml-node.c,v 1.7 2004/09/17 18:38:21 rleigh Exp $".
494 */
495