00001 /* SVN FILE INFO 00002 * $Revision: 174 $ : Last Committed Revision 00003 * $Date: 2008年06月24日 10:50:29 -0700 (2008年6月24日) $ : Last Committed Date */ 00004 /* 00005 * "$Id: mxml-node.c,v 1.1 2007年05月23日 20:43:27 david_ko Exp $" 00006 * 00007 * Node support code for Mini-XML, a small XML-like file parsing library. 00008 * 00009 * Copyright 2003-2005 by Michael Sweet. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU Library General Public 00013 * License as published by the Free Software Foundation; either 00014 * version 2, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * Contents: 00022 * 00023 * mxmlAdd() - Add a node to a tree. 00024 * mxmlDelete() - Delete a node and all of its children. 00025 * mxmlNewElement() - Create a new element node. 00026 * mxmlNewInteger() - Create a new integer node. 00027 * mxmlNewOpaque() - Create a new opaque string. 00028 * mxmlNewReal() - Create a new real number node. 00029 * mxmlNewText() - Create a new text fragment node. 00030 * mxmlNewTextf() - Create a new formatted text fragment node. 00031 * mxmlRemove() - Remove a node from its parent. 00032 * mxml_new() - Create a new node. 00033 */ 00034 00035 /* 00036 * Include necessary headers... 00037 */ 00038 00039 #include "config.h" 00040 #include "mxml.h" 00041 00042 00043 /* 00044 * Local functions... 00045 */ 00046 00047 static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type); 00048 00049 00050 /* 00051 * 'mxmlAdd()' - Add a node to a tree. 00052 * 00053 * Adds the specified node to the parent. If the child argument is not 00054 * NULL, puts the new node before or after the specified child depending 00055 * on the value of the where argument. If the child argument is NULL, 00056 * puts the new node at the beginning of the child list (MXML_ADD_BEFORE) 00057 * or at the end of the child list (MXML_ADD_AFTER). The constant 00058 * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer. 00059 */ 00060 00061 void 00062 mxmlAdd(mxml_node_t *parent, /* I - Parent node */ 00063 int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */ 00064 mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */ 00065 mxml_node_t *node) /* I - Node to add */ 00066 { 00067 #ifdef DEBUG 00068 fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent, 00069 where, child, node); 00070 #endif /* DEBUG */ 00071 00072 /* 00073 * Range check input... 00074 */ 00075 00076 if (!parent || !node) 00077 return; 00078 00079 #if DEBUG > 1 00080 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); 00081 if (parent) 00082 { 00083 fprintf(stderr, " BEFORE: parent->child=%p\n", parent->child); 00084 fprintf(stderr, " BEFORE: parent->last_child=%p\n", parent->last_child); 00085 fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev); 00086 fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next); 00087 } 00088 #endif /* DEBUG > 1 */ 00089 00090 /* 00091 * Remove the node from any existing parent... 00092 */ 00093 00094 if (node->parent) 00095 mxmlRemove(node); 00096 00097 /* 00098 * Reset pointers... 00099 */ 00100 00101 node->parent = parent; 00102 00103 switch (where) 00104 { 00105 case MXML_ADD_BEFORE : 00106 if (!child || child == parent->child || child->parent != parent) 00107 { 00108 /* 00109 * Insert as first node under parent... 00110 */ 00111 00112 node->next = parent->child; 00113 00114 if (parent->child) 00115 parent->child->prev = node; 00116 else 00117 parent->last_child = node; 00118 00119 parent->child = node; 00120 } 00121 else 00122 { 00123 /* 00124 * Insert node before this child... 00125 */ 00126 00127 node->next = child; 00128 node->prev = child->prev; 00129 00130 if (child->prev) 00131 child->prev->next = node; 00132 else 00133 parent->child = node; 00134 00135 child->prev = node; 00136 } 00137 break; 00138 00139 case MXML_ADD_AFTER : 00140 if (!child || child == parent->last_child || child->parent != parent) 00141 { 00142 /* 00143 * Insert as last node under parent... 00144 */ 00145 00146 node->parent = parent; 00147 node->prev = parent->last_child; 00148 00149 if (parent->last_child) 00150 parent->last_child->next = node; 00151 else 00152 parent->child = node; 00153 00154 parent->last_child = node; 00155 } 00156 else 00157 { 00158 /* 00159 * Insert node after this child... 00160 */ 00161 00162 node->prev = child; 00163 node->next = child->next; 00164 00165 if (child->next) 00166 child->next->prev = node; 00167 else 00168 parent->last_child = node; 00169 00170 child->next = node; 00171 } 00172 break; 00173 } 00174 00175 #if DEBUG > 1 00176 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); 00177 if (parent) 00178 { 00179 fprintf(stderr, " AFTER: parent->child=%p\n", parent->child); 00180 fprintf(stderr, " AFTER: parent->last_child=%p\n", parent->last_child); 00181 fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev); 00182 fprintf(stderr, " AFTER: parent->next=%p\n", parent->next); 00183 } 00184 #endif /* DEBUG > 1 */ 00185 } 00186 00187 00188 /* 00189 * 'mxmlDelete()' - Delete a node and all of its children. 00190 * 00191 * If the specified node has a parent, this function first removes the 00192 * node from its parent using the mxmlRemove() function. 00193 */ 00194 00195 void 00196 mxmlDelete(mxml_node_t *node) /* I - Node to delete */ 00197 { 00198 int i; /* Looping var */ 00199 00200 00201 #ifdef DEBUG 00202 fprintf(stderr, "mxmlDelete(node=%p)\n", node); 00203 #endif /* DEBUG */ 00204 00205 /* 00206 * Range check input... 00207 */ 00208 00209 if (!node) 00210 return; 00211 00212 /* 00213 * Remove the node from its parent, if any... 00214 */ 00215 00216 mxmlRemove(node); 00217 00218 /* 00219 * Delete children... 00220 */ 00221 00222 while (node->child) 00223 mxmlDelete(node->child); 00224 00225 /* 00226 * Now delete any node data... 00227 */ 00228 00229 switch (node->type) 00230 { 00231 case MXML_ELEMENT : 00232 if (node->value.element.name) 00233 free(node->value.element.name); 00234 00235 if (node->value.element.num_attrs) 00236 { 00237 for (i = 0; i < node->value.element.num_attrs; i ++) 00238 { 00239 if (node->value.element.attrs[i].name) 00240 free(node->value.element.attrs[i].name); 00241 if (node->value.element.attrs[i].value) 00242 free(node->value.element.attrs[i].value); 00243 } 00244 00245 free(node->value.element.attrs); 00246 } 00247 break; 00248 case MXML_INTEGER : 00249 /* Nothing to do */ 00250 break; 00251 case MXML_OPAQUE : 00252 if (node->value.opaque) 00253 free(node->value.opaque); 00254 break; 00255 case MXML_REAL : 00256 /* Nothing to do */ 00257 break; 00258 case MXML_TEXT : 00259 if (node->value.text.string) 00260 free(node->value.text.string); 00261 break; 00262 case MXML_CUSTOM : 00263 if (node->value.custom.data && 00264 node->value.custom.destroy) 00265 (*(node->value.custom.destroy))(node->value.custom.data); 00266 break; 00267 } 00268 00269 /* 00270 * Free this node... 00271 */ 00272 00273 free(node); 00274 } 00275 00276 00277 /* 00278 * 'mxmlNewCustom()' - Create a new custom data node. 00279 * 00280 * The new custom node is added to the end of the specified parent's child 00281 * list. The constant MXML_NO_PARENT can be used to specify that the new 00282 * element node has no parent. NULL can be passed when the data in the 00283 * node is not dynamically allocated or is separately managed. 00284 */ 00285 00286 mxml_node_t * /* O - New node */ 00287 mxmlNewCustom(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ 00288 void *data, /* I - Pointer to data */ 00289 void (*destroy)(void *)) 00290 /* I - Function to destroy data */ 00291 { 00292 mxml_node_t *node; /* New node */ 00293 00294 00295 #ifdef DEBUG 00296 fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent, 00297 data, destroy); 00298 #endif /* DEBUG */ 00299 00300 /* 00301 * Create the node and set the value... 00302 */ 00303 00304 if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL) 00305 { 00306 node->value.custom.data = data; 00307 node->value.custom.destroy = destroy; 00308 } 00309 00310 return (node); 00311 } 00312 00313 00314 /* 00315 * 'mxmlNewElement()' - Create a new element node. 00316 * 00317 * The new element node is added to the end of the specified parent's child 00318 * list. The constant MXML_NO_PARENT can be used to specify that the new 00319 * element node has no parent. 00320 */ 00321 00322 mxml_node_t * /* O - New node */ 00323 mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ 00324 const char *name) /* I - Name of element */ 00325 { 00326 mxml_node_t *node; /* New node */ 00327 00328 00329 #ifdef DEBUG 00330 fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent, 00331 name ? name : "(null)"); 00332 #endif /* DEBUG */ 00333 00334 /* 00335 * Range check input... 00336 */ 00337 00338 if (!name) 00339 return (NULL); 00340 00341 /* 00342 * Create the node and set the element name... 00343 */ 00344 00345 if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) 00346 node->value.element.name = strdup(name); 00347 00348 return (node); 00349 } 00350 00351 00352 /* 00353 * 'mxmlNewInteger()' - Create a new integer node. 00354 * 00355 * The new integer node is added to the end of the specified parent's child 00356 * list. The constant MXML_NO_PARENT can be used to specify that the new 00357 * integer node has no parent. 00358 */ 00359 00360 mxml_node_t * /* O - New node */ 00361 mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ 00362 int integer) /* I - Integer value */ 00363 { 00364 mxml_node_t *node; /* New node */ 00365 00366 00367 #ifdef DEBUG 00368 fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer); 00369 #endif /* DEBUG */ 00370 00371 /* 00372 * Create the node and set the element name... 00373 */ 00374 00375 if ((node = mxml_new(parent, MXML_INTEGER)) != NULL) 00376 node->value.integer = integer; 00377 00378 return (node); 00379 } 00380 00381 00382 /* 00383 * 'mxmlNewOpaque()' - Create a new opaque string. 00384 * 00385 * The new opaque node is added to the end of the specified parent's child 00386 * list. The constant MXML_NO_PARENT can be used to specify that the new 00387 * opaque node has no parent. The opaque string must be nul-terminated and 00388 * is copied into the new node. 00389 */ 00390 00391 mxml_node_t * /* O - New node */ 00392 mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ 00393 const char *opaque) /* I - Opaque string */ 00394 { 00395 mxml_node_t *node; /* New node */ 00396 00397 00398 #ifdef DEBUG 00399 fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent, 00400 opaque ? opaque : "(null)"); 00401 #endif /* DEBUG */ 00402 00403 /* 00404 * Range check input... 00405 */ 00406 00407 if (!opaque) 00408 return (NULL); 00409 00410 /* 00411 * Create the node and set the element name... 00412 */ 00413 00414 if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL) 00415 node->value.opaque = strdup(opaque); 00416 00417 return (node); 00418 } 00419 00420 00421 /* 00422 * 'mxmlNewReal()' - Create a new real number node. 00423 * 00424 * The new real number node is added to the end of the specified parent's 00425 * child list. The constant MXML_NO_PARENT can be used to specify that 00426 * the new real number node has no parent. 00427 */ 00428 00429 mxml_node_t * /* O - New node */ 00430 mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ 00431 double real) /* I - Real number value */ 00432 { 00433 mxml_node_t *node; /* New node */ 00434 00435 00436 #ifdef DEBUG 00437 fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real); 00438 #endif /* DEBUG */ 00439 00440 /* 00441 * Create the node and set the element name... 00442 */ 00443 00444 if ((node = mxml_new(parent, MXML_REAL)) != NULL) 00445 node->value.real = real; 00446 00447 return (node); 00448 } 00449 00450 00451 /* 00452 * 'mxmlNewText()' - Create a new text fragment node. 00453 * 00454 * The new text node is added to the end of the specified parent's child 00455 * list. The constant MXML_NO_PARENT can be used to specify that the new 00456 * text node has no parent. The whitespace parameter is used to specify 00457 * whether leading whitespace is present before the node. The text 00458 * string must be nul-terminated and is copied into the new node. 00459 */ 00460 00461 mxml_node_t * /* O - New node */ 00462 mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ 00463 int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ 00464 const char *string) /* I - String */ 00465 { 00466 mxml_node_t *node; /* New node */ 00467 00468 00469 #ifdef DEBUG 00470 fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n", 00471 parent, whitespace, string ? string : "(null)"); 00472 #endif /* DEBUG */ 00473 00474 /* 00475 * Range check input... 00476 */ 00477 00478 if (!string) 00479 return (NULL); 00480 00481 /* 00482 * Create the node and set the text value... 00483 */ 00484 00485 if ((node = mxml_new(parent, MXML_TEXT)) != NULL) 00486 { 00487 node->value.text.whitespace = whitespace; 00488 node->value.text.string = strdup(string); 00489 } 00490 00491 return (node); 00492 } 00493 00494 00495 /* 00496 * 'mxmlNewTextf()' - Create a new formatted text fragment node. 00497 * 00498 * The new text node is added to the end of the specified parent's child 00499 * list. The constant MXML_NO_PARENT can be used to specify that the new 00500 * text node has no parent. The whitespace parameter is used to specify 00501 * whether leading whitespace is present before the node. The format 00502 * string must be nul-terminated and is formatted into the new node. 00503 */ 00504 00505 mxml_node_t * /* O - New node */ 00506 mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ 00507 int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ 00508 const char *format, /* I - Printf-style frmat string */ 00509 ...) /* I - Additional args as needed */ 00510 { 00511 mxml_node_t *node; /* New node */ 00512 va_list ap; /* Pointer to arguments */ 00513 00514 00515 #ifdef DEBUG 00516 fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n", 00517 parent, whitespace, format ? format : "(null)"); 00518 #endif /* DEBUG */ 00519 00520 /* 00521 * Range check input... 00522 */ 00523 00524 if (!format) 00525 return (NULL); 00526 00527 /* 00528 * Create the node and set the text value... 00529 */ 00530 00531 if ((node = mxml_new(parent, MXML_TEXT)) != NULL) 00532 { 00533 va_start(ap, format); 00534 00535 node->value.text.whitespace = whitespace; 00536 node->value.text.string = mxml_strdupf(format, ap); 00537 00538 va_end(ap); 00539 } 00540 00541 return (node); 00542 } 00543 00544 00545 /* 00546 * 'mxmlRemove()' - Remove a node from its parent. 00547 * 00548 * Does not free memory used by the node - use mxmlDelete() for that. 00549 * This function does nothing if the node has no parent. 00550 */ 00551 00552 void 00553 mxmlRemove(mxml_node_t *node) /* I - Node to remove */ 00554 { 00555 #ifdef DEBUG 00556 fprintf(stderr, "mxmlRemove(node=%p)\n", node); 00557 #endif /* DEBUG */ 00558 00559 /* 00560 * Range check input... 00561 */ 00562 00563 if (!node || !node->parent) 00564 return; 00565 00566 /* 00567 * Remove from parent... 00568 */ 00569 00570 #if DEBUG > 1 00571 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); 00572 if (node->parent) 00573 { 00574 fprintf(stderr, " BEFORE: node->parent->child=%p\n", node->parent->child); 00575 fprintf(stderr, " BEFORE: node->parent->last_child=%p\n", node->parent->last_child); 00576 } 00577 fprintf(stderr, " BEFORE: node->child=%p\n", node->child); 00578 fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child); 00579 fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev); 00580 fprintf(stderr, " BEFORE: node->next=%p\n", node->next); 00581 #endif /* DEBUG > 1 */ 00582 00583 if (node->prev) 00584 node->prev->next = node->next; 00585 else 00586 node->parent->child = node->next; 00587 00588 if (node->next) 00589 node->next->prev = node->prev; 00590 else 00591 node->parent->last_child = node->prev; 00592 00593 node->parent = NULL; 00594 node->prev = NULL; 00595 node->next = NULL; 00596 00597 #if DEBUG > 1 00598 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); 00599 if (node->parent) 00600 { 00601 fprintf(stderr, " AFTER: node->parent->child=%p\n", node->parent->child); 00602 fprintf(stderr, " AFTER: node->parent->last_child=%p\n", node->parent->last_child); 00603 } 00604 fprintf(stderr, " AFTER: node->child=%p\n", node->child); 00605 fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child); 00606 fprintf(stderr, " AFTER: node->prev=%p\n", node->prev); 00607 fprintf(stderr, " AFTER: node->next=%p\n", node->next); 00608 #endif /* DEBUG > 1 */ 00609 } 00610 00611 00612 /* 00613 * 'mxml_new()' - Create a new node. 00614 */ 00615 00616 static mxml_node_t * /* O - New node */ 00617 mxml_new(mxml_node_t *parent, /* I - Parent node */ 00618 mxml_type_t type) /* I - Node type */ 00619 { 00620 mxml_node_t *node; /* New node */ 00621 00622 00623 #if DEBUG > 1 00624 fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type); 00625 #endif /* DEBUG > 1 */ 00626 00627 /* 00628 * Allocate memory for the node... 00629 */ 00630 00631 if ((node = calloc(1, sizeof(mxml_node_t))) == NULL) 00632 { 00633 #if DEBUG > 1 00634 fputs(" returning NULL\n", stderr); 00635 #endif /* DEBUG > 1 */ 00636 00637 return (NULL); 00638 } 00639 00640 #if DEBUG > 1 00641 fprintf(stderr, " returning %p\n", node); 00642 #endif /* DEBUG > 1 */ 00643 00644 /* 00645 * Set the node type... 00646 */ 00647 00648 node->type = type; 00649 00650 /* 00651 * Add to the parent if present... 00652 */ 00653 00654 if (parent) 00655 mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); 00656 00657 /* 00658 * Return the new node... 00659 */ 00660 00661 return (node); 00662 } 00663 00664 00665 /* 00666 * End of "$Id: mxml-node.c,v 1.1 2007年05月23日 20:43:27 david_ko Exp $". 00667 */