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: testmxml.c,v 1.1 2007年05月23日 20:43:28 david_ko Exp $" 00006 * 00007 * Test program 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 * main() - Main entry for test program. 00024 * type_cb() - XML data type callback for mxmlLoadFile()... 00025 * whitespace_cb() - Let the mxmlSaveFile() function know when to insert 00026 * newlines and tabs... 00027 */ 00028 00029 /* 00030 * Include necessary headers... 00031 */ 00032 00033 #include "config.h" 00034 #include "mxml.h" 00035 #ifdef WIN32 00036 # include <io.h> 00037 #else 00038 # include <unistd.h> 00039 #endif /* WIN32 */ 00040 #include <fcntl.h> 00041 #ifndef O_BINARY 00042 # define O_BINARY 0 00043 #endif /* !O_BINARY */ 00044 00045 00046 /* 00047 * Local functions... 00048 */ 00049 00050 mxml_type_t type_cb(mxml_node_t *node); 00051 const char *whitespace_cb(mxml_node_t *node, int where); 00052 00053 00054 /* 00055 * 'main()' - Main entry for test program. 00056 */ 00057 00058 int /* O - Exit status */ 00059 main(int argc, /* I - Number of command-line args */ 00060 char *argv[]) /* I - Command-line args */ 00061 { 00062 int i; /* Looping var */ 00063 FILE *fp; /* File to read */ 00064 int fd; /* File descriptor */ 00065 mxml_node_t *tree, /* XML tree */ 00066 *node; /* Node which should be in test.xml */ 00067 mxml_index_t *ind; /* XML index */ 00068 char buffer[16384]; /* Save string */ 00069 static const char *types[] = /* Strings for node types */ 00070 { 00071 "MXML_ELEMENT", 00072 "MXML_INTEGER", 00073 "MXML_OPAQUE", 00074 "MXML_REAL", 00075 "MXML_TEXT" 00076 }; 00077 00078 00079 /* 00080 * Check arguments... 00081 */ 00082 00083 if (argc != 2) 00084 { 00085 fputs("Usage: testmxml filename.xml\n", stderr); 00086 return (1); 00087 } 00088 00089 /* 00090 * Test the basic functionality... 00091 */ 00092 00093 tree = mxmlNewElement(MXML_NO_PARENT, "element"); 00094 00095 if (!tree) 00096 { 00097 fputs("ERROR: No parent node in basic test!\n", stderr); 00098 return (1); 00099 } 00100 00101 if (tree->type != MXML_ELEMENT) 00102 { 00103 fprintf(stderr, "ERROR: Parent has type %s (%d), expected MXML_ELEMENT!\n", 00104 tree->type < MXML_ELEMENT || tree->type > MXML_TEXT ? 00105 "UNKNOWN" : types[tree->type], tree->type); 00106 mxmlDelete(tree); 00107 return (1); 00108 } 00109 00110 if (strcmp(tree->value.element.name, "element")) 00111 { 00112 fprintf(stderr, "ERROR: Parent value is \"%s\", expected \"element\"!\n", 00113 tree->value.element.name); 00114 mxmlDelete(tree); 00115 return (1); 00116 } 00117 00118 mxmlNewInteger(tree, 123); 00119 mxmlNewOpaque(tree, "opaque"); 00120 mxmlNewReal(tree, 123.4f); 00121 mxmlNewText(tree, 1, "text"); 00122 00123 mxmlLoadString(tree, "<group type='string'>string string string</group>", 00124 MXML_NO_CALLBACK); 00125 mxmlLoadString(tree, "<group type='integer'>1 2 3</group>", 00126 MXML_INTEGER_CALLBACK); 00127 mxmlLoadString(tree, "<group type='real'>1.0 2.0 3.0</group>", 00128 MXML_REAL_CALLBACK); 00129 mxmlLoadString(tree, "<group>opaque opaque opaque</group>", 00130 MXML_OPAQUE_CALLBACK); 00131 00132 node = tree->child; 00133 00134 if (!node) 00135 { 00136 fputs("ERROR: No first child node in basic test!\n", stderr); 00137 mxmlDelete(tree); 00138 return (1); 00139 } 00140 00141 if (node->type != MXML_INTEGER) 00142 { 00143 fprintf(stderr, "ERROR: First child has type %s (%d), expected MXML_INTEGER!\n", 00144 node->type < MXML_ELEMENT || node->type > MXML_TEXT ? 00145 "UNKNOWN" : types[node->type], node->type); 00146 mxmlDelete(tree); 00147 return (1); 00148 } 00149 00150 if (node->value.integer != 123) 00151 { 00152 fprintf(stderr, "ERROR: First child value is %d, expected 123!\n", 00153 node->value.integer); 00154 mxmlDelete(tree); 00155 return (1); 00156 } 00157 00158 node = node->next; 00159 00160 if (!node) 00161 { 00162 fputs("ERROR: No second child node in basic test!\n", stderr); 00163 mxmlDelete(tree); 00164 return (1); 00165 } 00166 00167 if (node->type != MXML_OPAQUE) 00168 { 00169 fprintf(stderr, "ERROR: Second child has type %s (%d), expected MXML_OPAQUE!\n", 00170 node->type < MXML_ELEMENT || node->type > MXML_TEXT ? 00171 "UNKNOWN" : types[node->type], node->type); 00172 mxmlDelete(tree); 00173 return (1); 00174 } 00175 00176 if (!node->value.opaque || strcmp(node->value.opaque, "opaque")) 00177 { 00178 fprintf(stderr, "ERROR: Second child value is \"%s\", expected \"opaque\"!\n", 00179 node->value.opaque ? node->value.opaque : "(null)"); 00180 mxmlDelete(tree); 00181 return (1); 00182 } 00183 00184 node = node->next; 00185 00186 if (!node) 00187 { 00188 fputs("ERROR: No third child node in basic test!\n", stderr); 00189 mxmlDelete(tree); 00190 return (1); 00191 } 00192 00193 if (node->type != MXML_REAL) 00194 { 00195 fprintf(stderr, "ERROR: Third child has type %s (%d), expected MXML_REAL!\n", 00196 node->type < MXML_ELEMENT || node->type > MXML_TEXT ? 00197 "UNKNOWN" : types[node->type], node->type); 00198 mxmlDelete(tree); 00199 return (1); 00200 } 00201 00202 if (node->value.real != 123.4f) 00203 { 00204 fprintf(stderr, "ERROR: Third child value is %f, expected 123.4!\n", 00205 node->value.real); 00206 mxmlDelete(tree); 00207 return (1); 00208 } 00209 00210 node = node->next; 00211 00212 if (!node) 00213 { 00214 fputs("ERROR: No fourth child node in basic test!\n", stderr); 00215 mxmlDelete(tree); 00216 return (1); 00217 } 00218 00219 if (node->type != MXML_TEXT) 00220 { 00221 fprintf(stderr, "ERROR: Fourth child has type %s (%d), expected MXML_TEXT!\n", 00222 node->type < MXML_ELEMENT || node->type > MXML_TEXT ? 00223 "UNKNOWN" : types[node->type], node->type); 00224 mxmlDelete(tree); 00225 return (1); 00226 } 00227 00228 if (!node->value.text.whitespace || 00229 !node->value.text.string || strcmp(node->value.text.string, "text")) 00230 { 00231 fprintf(stderr, "ERROR: Fourth child value is %d,\"%s\", expected 1,\"text\"!\n", 00232 node->value.text.whitespace, 00233 node->value.text.string ? node->value.text.string : "(null)"); 00234 mxmlDelete(tree); 00235 return (1); 00236 } 00237 00238 for (i = 0; i < 4; i ++) 00239 { 00240 node = node->next; 00241 00242 if (!node) 00243 { 00244 fprintf(stderr, "ERROR: No group #%d child node in basic test!\n", i + 1); 00245 mxmlDelete(tree); 00246 return (1); 00247 } 00248 00249 if (node->type != MXML_ELEMENT) 00250 { 00251 fprintf(stderr, "ERROR: Group child #%d has type %s (%d), expected MXML_ELEMENT!\n", 00252 i + 1, node->type < MXML_ELEMENT || node->type > MXML_TEXT ? 00253 "UNKNOWN" : types[node->type], node->type); 00254 mxmlDelete(tree); 00255 return (1); 00256 } 00257 } 00258 00259 /* 00260 * Test indices... 00261 */ 00262 00263 ind = mxmlIndexNew(tree, NULL, NULL); 00264 if (!ind) 00265 { 00266 fputs("ERROR: Unable to create index of all nodes!\n", stderr); 00267 mxmlDelete(tree); 00268 return (1); 00269 } 00270 00271 if (ind->num_nodes != 5) 00272 { 00273 fprintf(stderr, "ERROR: Index of all nodes contains %d " 00274 "nodes; expected 5!\n", ind->num_nodes); 00275 mxmlIndexDelete(ind); 00276 mxmlDelete(tree); 00277 return (1); 00278 } 00279 00280 mxmlIndexReset(ind); 00281 if (!mxmlIndexFind(ind, "group", NULL)) 00282 { 00283 fputs("ERROR: mxmlIndexFind for \"group\" failed!\n", stderr); 00284 mxmlIndexDelete(ind); 00285 mxmlDelete(tree); 00286 return (1); 00287 } 00288 00289 mxmlIndexDelete(ind); 00290 00291 ind = mxmlIndexNew(tree, "group", NULL); 00292 if (!ind) 00293 { 00294 fputs("ERROR: Unable to create index of groups!\n", stderr); 00295 mxmlDelete(tree); 00296 return (1); 00297 } 00298 00299 if (ind->num_nodes != 4) 00300 { 00301 fprintf(stderr, "ERROR: Index of groups contains %d " 00302 "nodes; expected 4!\n", ind->num_nodes); 00303 mxmlIndexDelete(ind); 00304 mxmlDelete(tree); 00305 return (1); 00306 } 00307 00308 mxmlIndexReset(ind); 00309 if (!mxmlIndexEnum(ind)) 00310 { 00311 fputs("ERROR: mxmlIndexEnum failed!\n", stderr); 00312 mxmlIndexDelete(ind); 00313 mxmlDelete(tree); 00314 return (1); 00315 } 00316 00317 mxmlIndexDelete(ind); 00318 00319 ind = mxmlIndexNew(tree, NULL, "type"); 00320 if (!ind) 00321 { 00322 fputs("ERROR: Unable to create index of type attributes!\n", stderr); 00323 mxmlDelete(tree); 00324 return (1); 00325 } 00326 00327 if (ind->num_nodes != 3) 00328 { 00329 fprintf(stderr, "ERROR: Index of type attributes contains %d " 00330 "nodes; expected 3!\n", ind->num_nodes); 00331 mxmlIndexDelete(ind); 00332 mxmlDelete(tree); 00333 return (1); 00334 } 00335 00336 mxmlIndexReset(ind); 00337 if (!mxmlIndexFind(ind, NULL, "string")) 00338 { 00339 fputs("ERROR: mxmlIndexFind for \"string\" failed!\n", stderr); 00340 mxmlIndexDelete(ind); 00341 mxmlDelete(tree); 00342 return (1); 00343 } 00344 00345 mxmlIndexDelete(ind); 00346 00347 ind = mxmlIndexNew(tree, "group", "type"); 00348 if (!ind) 00349 { 00350 fputs("ERROR: Unable to create index of elements and attributes!\n", stderr); 00351 mxmlDelete(tree); 00352 return (1); 00353 } 00354 00355 if (ind->num_nodes != 3) 00356 { 00357 fprintf(stderr, "ERROR: Index of elements and attributes contains %d " 00358 "nodes; expected 3!\n", ind->num_nodes); 00359 mxmlIndexDelete(ind); 00360 mxmlDelete(tree); 00361 return (1); 00362 } 00363 00364 mxmlIndexReset(ind); 00365 if (!mxmlIndexFind(ind, "group", "string")) 00366 { 00367 fputs("ERROR: mxmlIndexFind for \"string\" failed!\n", stderr); 00368 mxmlIndexDelete(ind); 00369 mxmlDelete(tree); 00370 return (1); 00371 } 00372 00373 mxmlIndexDelete(ind); 00374 00375 /* 00376 * Check the mxmlDelete() works properly... 00377 */ 00378 00379 for (i = 0; i < 8; i ++) 00380 { 00381 if (tree->child) 00382 mxmlDelete(tree->child); 00383 else 00384 { 00385 fprintf(stderr, "ERROR: Child pointer prematurely NULL on child #%d\n", 00386 i + 1); 00387 mxmlDelete(tree); 00388 return (1); 00389 } 00390 } 00391 00392 if (tree->child) 00393 { 00394 fputs("ERROR: Child pointer not NULL after deleting all children!\n", stderr); 00395 return (1); 00396 } 00397 00398 if (tree->last_child) 00399 { 00400 fputs("ERROR: Last child pointer not NULL after deleting all children!\n", stderr); 00401 return (1); 00402 } 00403 00404 mxmlDelete(tree); 00405 00406 /* 00407 * Open the file... 00408 */ 00409 00410 if (argv[1][0] == '<') 00411 tree = mxmlLoadString(NULL, argv[1], type_cb); 00412 else if ((fp = fopen(argv[1], "rb")) == NULL) 00413 { 00414 perror(argv[1]); 00415 return (1); 00416 } 00417 else 00418 { 00419 /* 00420 * Read the file... 00421 */ 00422 00423 tree = mxmlLoadFile(NULL, fp, type_cb); 00424 00425 fclose(fp); 00426 } 00427 00428 if (!tree) 00429 { 00430 fputs("Unable to read XML file!\n", stderr); 00431 return (1); 00432 } 00433 00434 if (!strcmp(argv[1], "test.xml")) 00435 { 00436 /* 00437 * Verify that mxmlFindElement() and indirectly mxmlWalkNext() work 00438 * properly... 00439 */ 00440 00441 if ((node = mxmlFindElement(tree, tree, "choice", NULL, NULL, 00442 MXML_DESCEND)) == NULL) 00443 { 00444 fputs("Unable to find first <choice> element in XML tree!\n", stderr); 00445 mxmlDelete(tree); 00446 return (1); 00447 } 00448 00449 if ((node = mxmlFindElement(node, tree, "choice", NULL, NULL, 00450 MXML_NO_DESCEND)) == NULL) 00451 { 00452 fputs("Unable to find second <choice> element in XML tree!\n", stderr); 00453 mxmlDelete(tree); 00454 return (1); 00455 } 00456 } 00457 00458 /* 00459 * Print the XML tree... 00460 */ 00461 00462 mxmlSaveFile(tree, stdout, whitespace_cb); 00463 00464 /* 00465 * Save the XML tree to a string and print it... 00466 */ 00467 00468 if (mxmlSaveString(tree, buffer, sizeof(buffer), whitespace_cb) > 0) 00469 fputs(buffer, stderr); 00470 00471 /* 00472 * Delete the tree... 00473 */ 00474 00475 mxmlDelete(tree); 00476 00477 /* 00478 * Read from/write to file descriptors... 00479 */ 00480 00481 if (argv[1][0] != '<') 00482 { 00483 /* 00484 * Open the file again... 00485 */ 00486 00487 if ((fd = open(argv[1], O_RDONLY | O_BINARY)) < 0) 00488 { 00489 perror(argv[1]); 00490 return (1); 00491 } 00492 00493 /* 00494 * Read the file... 00495 */ 00496 00497 tree = mxmlLoadFd(NULL, fd, type_cb); 00498 00499 close(fd); 00500 00501 /* 00502 * Create filename.xmlfd... 00503 */ 00504 00505 snprintf(buffer, sizeof(buffer), "%sfd", argv[1]); 00506 00507 if ((fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) < 0) 00508 { 00509 perror(buffer); 00510 mxmlDelete(tree); 00511 return (1); 00512 } 00513 00514 /* 00515 * Write the file... 00516 */ 00517 00518 mxmlSaveFd(tree, fd, whitespace_cb); 00519 00520 close(fd); 00521 00522 /* 00523 * Delete the tree... 00524 */ 00525 00526 mxmlDelete(tree); 00527 } 00528 00529 /* 00530 * Return... 00531 */ 00532 00533 return (0); 00534 } 00535 00536 00537 /* 00538 * 'type_cb()' - XML data type callback for mxmlLoadFile()... 00539 */ 00540 00541 mxml_type_t /* O - Data type */ 00542 type_cb(mxml_node_t *node) /* I - Element node */ 00543 { 00544 const char *type; /* Type string */ 00545 00546 00547 /* 00548 * You can lookup attributes and/or use the element name, hierarchy, etc... 00549 */ 00550 00551 if ((type = mxmlElementGetAttr(node, "type")) == NULL) 00552 type = node->value.element.name; 00553 00554 if (!strcmp(type, "integer")) 00555 return (MXML_INTEGER); 00556 else if (!strcmp(type, "opaque") || !strcmp(type, "pre")) 00557 return (MXML_OPAQUE); 00558 else if (!strcmp(type, "real")) 00559 return (MXML_REAL); 00560 else 00561 return (MXML_TEXT); 00562 } 00563 00564 00565 /* 00566 * 'whitespace_cb()' - Let the mxmlSaveFile() function know when to insert 00567 * newlines and tabs... 00568 */ 00569 00570 const char * /* O - Whitespace string or NULL */ 00571 whitespace_cb(mxml_node_t *node, /* I - Element node */ 00572 int where) /* I - Open or close tag? */ 00573 { 00574 mxml_node_t *parent; /* Parent node */ 00575 int level; /* Indentation level */ 00576 const char *name; /* Name of element */ 00577 static const char *tabs = "\t\t\t\t\t\t\t\t"; 00578 /* Tabs for indentation */ 00579 00580 00581 /* 00582 * We can conditionally break to a new line before or after any element. 00583 * These are just common HTML elements... 00584 */ 00585 00586 name = node->value.element.name; 00587 00588 if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") || 00589 !strcmp(name, "pre") || !strcmp(name, "p") || 00590 !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") || 00591 !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6")) 00592 { 00593 /* 00594 * Newlines before open and after close... 00595 */ 00596 00597 if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE) 00598 return ("\n"); 00599 } 00600 else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul")) 00601 { 00602 /* 00603 * Put a newline before and after list elements... 00604 */ 00605 00606 return ("\n"); 00607 } 00608 else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li")) 00609 { 00610 /* 00611 * Put a tab before <li>'s, <dd>'s, and <dt>'s, and a newline after them... 00612 */ 00613 00614 if (where == MXML_WS_BEFORE_OPEN) 00615 return ("\t"); 00616 else if (where == MXML_WS_AFTER_CLOSE) 00617 return ("\n"); 00618 } 00619 else if (!strcmp(name, "?xml")) 00620 { 00621 return (NULL); 00622 } 00623 else if (where == MXML_WS_BEFORE_OPEN || 00624 ((!strcmp(name, "choice") || !strcmp(name, "option")) && 00625 where == MXML_WS_BEFORE_CLOSE)) 00626 { 00627 for (level = -1, parent = node->parent; 00628 parent; 00629 level ++, parent = parent->parent); 00630 00631 if (level > 8) 00632 level = 8; 00633 else if (level < 0) 00634 level = 0; 00635 00636 return (tabs + 8 - level); 00637 } 00638 else if (where == MXML_WS_AFTER_CLOSE || 00639 ((!strcmp(name, "group") || !strcmp(name, "option") || 00640 !strcmp(name, "choice")) && 00641 where == MXML_WS_AFTER_OPEN)) 00642 return ("\n"); 00643 else if (where == MXML_WS_AFTER_OPEN && !node->child) 00644 return ("\n"); 00645 00646 /* 00647 * Return NULL for no added whitespace... 00648 */ 00649 00650 return (NULL); 00651 } 00652 00653 00654 /* 00655 * End of "$Id: testmxml.c,v 1.1 2007年05月23日 20:43:28 david_ko Exp $". 00656 */