00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <getopt.h>
00029 #include <errno.h>
00030 #include <string.h>
00031
00032 #include <libxml/xmlreader.h>
00033 #include <libxml/xmlschemas.h>
00034
00035 #include "testrunnerlite.h"
00036 #include "testdefinitiondatatypes.h"
00037 #include "testdefinitionparser.h"
00038 #include "log.h"
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065 LOCAL td_parser_callbacks *cbs;
00066 LOCAL xmlTextReaderPtr reader;
00067 LOCAL xmlSchemaParserCtxtPtr schema_context = NULL;
00068 LOCAL xmlSchemaPtr schema = NULL;
00069 LOCAL td_suite *current_suite;
00070 LOCAL td_set *current_set;
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083 LOCAL int td_parse_gen_attribs (td_gen_attribs *,td_gen_attribs *);
00084
00085 LOCAL int td_parse_suite (void);
00086
00087 LOCAL int td_parse_steps (xmlListPtr, const char *);
00088
00089 LOCAL td_step *td_parse_step (int manual_default);
00090
00091 LOCAL int td_parse_case (td_set *s);
00092
00093 LOCAL int td_parse_environments(xmlListPtr);
00094
00095 LOCAL int td_parse_gets(xmlListPtr);
00096
00097 LOCAL int td_parse_set ();
00098
00099
00100
00101
00102
00103
00104
00105 LOCAL int td_parse_gen_attribs (td_gen_attribs *attr,
00106 td_gen_attribs *defaults)
00107 {
00108 const xmlChar *name;
00109
00110 if (defaults) {
00111 attr->timeout = defaults->timeout;
00112 attr->manual = defaults->manual;
00113 attr->insignificant = defaults->insignificant;
00114 if (defaults->requirement)
00115 attr->requirement = xmlStrdup(defaults->requirement);
00116 if (defaults->level)
00117 attr->level = xmlStrdup(defaults->level);
00118 if (defaults->type)
00119 attr->type = xmlStrdup(defaults->type);
00120 }
00121
00122 while (xmlTextReaderMoveToNextAttribute(reader)) {
00123 name = xmlTextReaderConstName(reader);
00124 if (!xmlStrcmp (name, BAD_CAST "name")) {
00125 attr->name = xmlTextReaderValue(reader);
00126 continue;
00127 }
00128 if (!xmlStrcmp (name, BAD_CAST "timeout")) {
00129 attr->timeout = strtoul((char *)
00130 xmlTextReaderConstValue(reader),
00131 NULL, 10);
00132 continue;
00133 }
00134 if (!xmlStrcmp (name, BAD_CAST "description")) {
00135 attr->description = xmlTextReaderValue(reader);
00136 continue;
00137 }
00138 if (!xmlStrcmp (name, BAD_CAST "requirement")) {
00139 if (attr->requirement) free (attr->requirement);
00140 attr->requirement = xmlTextReaderValue(reader);
00141 continue;
00142 }
00143 if (!xmlStrcmp (name, BAD_CAST "type")) {
00144 if (attr->type) free (attr->type);
00145 attr->type = xmlTextReaderValue(reader);
00146 continue;
00147 }
00148 if (!xmlStrcmp (name, BAD_CAST "level")) {
00149 if (attr->level) free (attr->level);
00150 attr->level = xmlTextReaderValue(reader);
00151 continue;
00152 }
00153 if (!xmlStrcmp (name, BAD_CAST "domain")) {
00154 if (attr->domain) free (attr->domain);
00155 attr->domain = xmlTextReaderValue(reader);
00156 continue;
00157 }
00158 if (!xmlStrcmp (name, BAD_CAST "feature")) {
00159 if (attr->feature) free (attr->feature);
00160 attr->feature = xmlTextReaderValue(reader);
00161 continue;
00162 }
00163 if (!xmlStrcmp (name, BAD_CAST "component")) {
00164 if (attr->component) free (attr->component);
00165 attr->component = xmlTextReaderValue(reader);
00166 continue;
00167 }
00168 if (!xmlStrcmp (name, BAD_CAST "manual")) {
00169 attr->manual = !xmlStrcmp (xmlTextReaderConstValue
00170 (reader), BAD_CAST "true");
00171 continue;
00172 }
00173 if (!xmlStrcmp (name, BAD_CAST "insignificant")) {
00174 attr->insignificant =
00175 !xmlStrcmp (xmlTextReaderConstValue (reader),
00176 BAD_CAST "true");
00177 continue;
00178 }
00179 }
00180 return 0;
00181 }
00182
00186 LOCAL td_step *td_parse_step(int manual_default)
00187 {
00188 const xmlChar *name;
00189 td_step *step = NULL;
00190 xmlNodePtr node;
00191 int ret;
00192
00193 step = td_step_create();
00194 step->manual = manual_default;
00195
00196 while (xmlTextReaderMoveToNextAttribute(reader)) {
00197 name = xmlTextReaderConstName(reader);
00198 if (!xmlStrcmp (name, BAD_CAST "expected_result")) {
00199 step->expected_result =
00200 strtoul((char *)xmlTextReaderConstValue(reader),
00201 NULL, 10);
00202 step->has_expected_result = 1;
00203
00204 continue;
00205 }
00206 if (!xmlStrcmp (name, BAD_CAST "manual")) {
00207 step->manual = !xmlStrcmp (xmlTextReaderConstValue
00208 (reader), BAD_CAST "true");
00209 continue;
00210 }
00211 }
00212
00213 do {
00214 ret = xmlTextReaderRead(reader);
00215 if (!ret) {
00216 LOG_MSG (LOG_ERR, "%s:%s: ReaderRead() fail\n",
00217 PROGNAME, __FUNCTION__);
00218
00219 goto ERROUT;
00220 }
00221 name = xmlTextReaderConstName(reader);
00222 if (!name) {
00223 LOG_MSG (LOG_ERR, "%s: ReaderName() fail\n",
00224 PROGNAME);
00225 goto ERROUT;
00226 }
00227
00228 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
00229 if (step->step)
00230 step->step = xmlStrcat
00231 (step->step,
00232 xmlTextReaderReadString (reader));
00233 else
00234 step->step = xmlTextReaderReadString (reader);
00235 }
00236
00237 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_CDATA) {
00238 node = xmlTextReaderCurrentNode (reader);
00239 if (step->step)
00240 step->step = xmlStrcat
00241 (step->step,
00242 node->content);
00243 else
00244 step->step = xmlStrdup(node->content);
00245
00246 }
00247
00248 } while (!(xmlTextReaderNodeType(reader) ==
00249 XML_READER_TYPE_END_ELEMENT &&
00250 !xmlStrcmp (name, BAD_CAST "step")));
00251
00252 return step;
00253 ERROUT:
00254 LOG_MSG (LOG_ERR, "%s:%s: Exiting with error\n",
00255 PROGNAME, __FUNCTION__);
00256 free (step);
00257
00258 return NULL;
00259 }
00260
00266 LOCAL int td_parse_steps(xmlListPtr list, const char *tag)
00267 {
00268 const xmlChar *name;
00269 td_steps *steps = NULL;
00270 td_step *step = NULL;
00271 int ret;
00272
00273 steps = td_steps_create();
00274
00275 if (!steps) {
00276 goto ERROUT;
00277 }
00278
00279 if (xmlTextReaderMoveToAttribute (reader, BAD_CAST "timeout") == 1) {
00280 steps->timeout = strtoul((char *)
00281 xmlTextReaderConstValue(reader),
00282 NULL, 10);
00283 }
00284
00285 do {
00286 ret = xmlTextReaderRead(reader);
00287 if (!ret) {
00288 LOG_MSG (LOG_ERR, "%s: ReaderRead() fail\n",
00289 PROGNAME);
00290 goto ERROUT;
00291 }
00292
00293 name = xmlTextReaderConstName(reader);
00294 if (!name) {
00295 LOG_MSG (LOG_ERR, "%s: ReaderName() fail\n",
00296 PROGNAME);
00297 goto ERROUT;
00298 }
00299
00300 if (xmlTextReaderNodeType(reader) ==
00301 XML_READER_TYPE_ELEMENT &&
00302 !xmlStrcmp (name, BAD_CAST "step")) {
00303 step = td_parse_step (current_set->gen.manual);
00304 if (!step)
00305 goto ERROUT;
00306 if (xmlListAppend (steps->steps, step)) {
00307 LOG_MSG (LOG_ERR, "%s: list insert failed\n",
00308 PROGNAME);
00309 goto ERROUT;
00310 }
00311 }
00312 } while (!(xmlTextReaderNodeType(reader) ==
00313 XML_READER_TYPE_END_ELEMENT &&
00314 !xmlStrcmp (name, BAD_CAST tag)));
00315
00316 xmlListAppend (list, steps);
00317
00318 return 0;
00319 ERROUT:
00320 LOG_MSG (LOG_ERR, "%s:%s: Exiting with error\n",
00321 PROGNAME, __FUNCTION__);
00322 xmlListDelete (steps->steps);
00323 free(steps);
00324 return 1;
00325 }
00326
00331 LOCAL int td_parse_case(td_set *s)
00332 {
00333 const xmlChar *name;
00334 td_step *step = NULL;
00335 td_case *c = NULL;
00336 int ret;
00337
00338 c = td_case_create();
00339 if (!c)
00340 goto ERROUT;
00341
00342 if (td_parse_gen_attribs (&c->gen, &s->gen))
00343 goto ERROUT;
00344
00345 if (xmlTextReaderMoveToAttribute (reader,
00346 BAD_CAST "subfeature") == 1) {
00347 c->subfeature = xmlTextReaderValue(reader);
00348 }
00349
00350 do {
00351 ret = xmlTextReaderRead(reader);
00352 if (!ret) {
00353 LOG_MSG (LOG_ERR, "%s: ReaderRead() fail\n",
00354 PROGNAME);
00355
00356 goto ERROUT;
00357 }
00358 name = xmlTextReaderConstName(reader);
00359 if (!name) {
00360 LOG_MSG (LOG_ERR, "%s: ReaderName() fail\n",
00361 PROGNAME);
00362 goto ERROUT;
00363 }
00364 if (xmlTextReaderNodeType(reader) ==
00365 XML_READER_TYPE_ELEMENT &&
00366 !xmlStrcmp (name, BAD_CAST "step")) {
00367 step = td_parse_step (c->gen.manual);
00368 if (!step)
00369 goto ERROUT;
00370 if (xmlListAppend (c->steps, step)) {
00371 LOG_MSG (LOG_ERR, "%s: list insert failed\n",
00372 PROGNAME);
00373 goto ERROUT;
00374 }
00375 }
00376 if (xmlTextReaderNodeType(reader) ==
00377 XML_READER_TYPE_ELEMENT &&
00378 !xmlStrcmp (name, BAD_CAST "TC_Title")) {
00379 c->tc_title = xmlTextReaderReadString (reader);
00380
00381 }
00382 if (xmlTextReaderNodeType(reader) ==
00383 XML_READER_TYPE_ELEMENT &&
00384 !xmlStrcmp (name, BAD_CAST "state")) {
00385 c->state = xmlTextReaderReadString (reader);
00386
00387 }
00388 if (xmlTextReaderNodeType(reader) ==
00389 XML_READER_TYPE_ELEMENT &&
00390 !xmlStrcmp (name, BAD_CAST "description")) {
00391 if (c->gen.description) {
00392 c->gen.description = xmlStrcat
00393 (c->gen.description,
00394 BAD_CAST " - ");
00395 c->gen.description = xmlStrcat
00396 (c->gen.description,
00397 BAD_CAST
00398 xmlTextReaderReadString (reader));
00399 }
00400 else
00401 c->gen.description = xmlTextReaderReadString(reader);
00402 }
00403
00404
00405 } while (!(xmlTextReaderNodeType(reader) ==
00406 XML_READER_TYPE_END_ELEMENT &&
00407 !xmlStrcmp (name, BAD_CAST "case")));
00408
00409 xmlListAppend (s->cases, c);
00410
00411 return 0;
00412 ERROUT:
00413 LOG_MSG (LOG_ERR, "%s:%s: Exiting with error\n",
00414 PROGNAME, __FUNCTION__);
00415 xmlListDelete (c->steps);
00416 if (c->gen.name) free (c->gen.name);
00417 if (c->gen.description) free (c->gen.description);
00418 free (c);
00419 return 1;
00420 }
00421
00426 LOCAL int td_parse_environments(xmlListPtr list)
00427 {
00428 int ret;
00429 const xmlChar *name;
00430 xmlChar *value;
00431
00432 do {
00433 ret = xmlTextReaderRead(reader);
00434 if (!ret) {
00435 LOG_MSG (LOG_ERR, "%s:%s: ReaderRead() fail\n",
00436 PROGNAME, __FUNCTION__);
00437
00438 goto ERROUT;
00439 }
00440
00441 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
00442 name = xmlTextReaderConstName(reader);
00443 if (!name) {
00444 LOG_MSG (LOG_ERR, "%s:%s: ReaderName() "
00445 "fail\n",
00446 PROGNAME, __FUNCTION__);
00447 goto ERROUT;
00448 }
00449 }
00450
00451 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
00452 value = xmlTextReaderReadString (reader);
00453 if (!xmlStrcmp (value, BAD_CAST "false")) {
00454 xmlListRemoveAll (list, (void *)name);
00455 }
00456 free (value);
00457 }
00458 } while (!(xmlTextReaderNodeType(reader) ==
00459 XML_READER_TYPE_END_ELEMENT &&
00460 !xmlStrcmp (xmlTextReaderConstName(reader),
00461 BAD_CAST "environments")));
00462
00463 return 0;
00464 ERROUT:
00465 LOG_MSG (LOG_ERR, "%s:%s: Exiting with error\n",
00466 PROGNAME, __FUNCTION__);
00467 return 1;
00468 }
00469
00474 LOCAL int td_parse_gets(xmlListPtr list)
00475 {
00476 int ret;
00477 int delete_after = 0;
00478 td_file *file;
00479
00480 do {
00481
00482 ret = xmlTextReaderRead(reader);
00483 if (!ret) {
00484 LOG_MSG (LOG_ERR, "%s:%s: ReaderRead() fail\n",
00485 PROGNAME, __FUNCTION__);
00486
00487 goto ERROUT;
00488 }
00489
00490 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT &&
00491 xmlTextReaderMoveToNextAttribute(reader) == 1) {
00492 delete_after = !xmlStrcmp (xmlTextReaderConstValue
00493 (reader),
00494 BAD_CAST "true");
00495 }
00496
00497
00498 if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
00499 file = (td_file *)malloc (sizeof (td_file));
00500 file->filename = xmlTextReaderReadString (reader);
00501 file->delete_after = delete_after;
00502 delete_after = 0;
00503 if (xmlListAppend (list, file)) {
00504 LOG_MSG (LOG_ERR,
00505 "%s:%s list insert failed\n",
00506 PROGNAME, __FUNCTION__);
00507 goto ERROUT;
00508 }
00509
00510 }
00511 } while (!(xmlTextReaderNodeType(reader) ==
00512 XML_READER_TYPE_END_ELEMENT &&
00513 !xmlStrcmp (xmlTextReaderConstName(reader),
00514 BAD_CAST "get")));
00515
00516 return 0;
00517 ERROUT:
00518 LOG_MSG (LOG_ERR, "%s:%s: Exiting with error\n",
00519 PROGNAME, __FUNCTION__);
00520 return 1;
00521 }
00522
00526 LOCAL int td_parse_suite ()
00527 {
00528 td_suite *s;
00529
00530 if (!cbs->test_suite)
00531 return 1;
00532
00533 s = td_suite_create();
00534 if (!s) return 1;
00535
00536 current_suite = s;
00537
00538 td_parse_gen_attribs (&s->gen, NULL);
00539 cbs->test_suite(s);
00540
00541 return 0;
00542 }
00543
00547 LOCAL int td_parse_set ()
00548 {
00549 int ret = 0;
00550 td_set *s;
00551 const xmlChar *name;
00552
00553 if (!cbs->test_set)
00554 return 1;
00555 s = td_set_create ();
00556 current_set = s;
00557
00558 if (td_parse_gen_attribs(&s->gen, ¤t_suite->gen))
00559 goto ERROUT;
00560
00561 do {
00562 ret = xmlTextReaderRead(reader);
00563 if (!ret) {
00564 LOG_MSG (LOG_ERR, "%s:%s: ReaderRead() fail\n",
00565 PROGNAME, __FUNCTION__);
00566
00567 goto ERROUT;
00568 }
00569 name = xmlTextReaderConstName(reader);
00570 if (!name) {
00571 LOG_MSG (LOG_ERR, "%s: ReaderName() fail\n",
00572 PROGNAME);
00573 goto ERROUT;
00574 }
00575 if (!xmlStrcmp (name, BAD_CAST "pre_steps"))
00576 ret = !td_parse_steps(s->pre_steps, "pre_steps");
00577 if (!xmlStrcmp (name, BAD_CAST "post_steps"))
00578 ret = !td_parse_steps(s->post_steps, "post_steps");
00579 if (!xmlStrcmp (name, BAD_CAST "case"))
00580 ret = !td_parse_case(s);
00581 if (!xmlStrcmp (name, BAD_CAST "environments"))
00582 ret = !td_parse_environments(s->environments);
00583 if (!xmlStrcmp (name, BAD_CAST "get"))
00584 ret = !td_parse_gets(s->gets);
00585
00586 if (!ret)
00587 goto ERROUT;
00588 } while (!(xmlTextReaderNodeType(reader) ==
00589 XML_READER_TYPE_END_ELEMENT &&
00590 !xmlStrcmp (name, BAD_CAST "set")));
00591 cbs->test_set(s);
00592
00593 return 0;
00594 ERROUT:
00595 LOG_MSG (LOG_ERR, "%s:%s: Exiting with error\n",
00596 PROGNAME, __FUNCTION__);
00597
00598 return 1;
00599 }
00600
00601
00602
00603
00608 int parse_test_definition (testrunner_lite_options *opts)
00609 {
00610 int ret = TESTRUNNER_LITE_XML_PARSE_FAIL;
00611 xmlDocPtr doc = NULL;
00612 xmlParserCtxtPtr ctxt = NULL;
00613 xmlSchemaPtr sch = NULL;
00614 xmlSchemaValidCtxtPtr valid_ctxt = NULL;
00615 xmlSchemaParserCtxtPtr schema_ctxt = NULL;
00616
00617 xmlSubstituteEntitiesDefault(1);
00618
00619
00620
00621
00622 ctxt = xmlNewParserCtxt();
00623 if (ctxt == NULL) {
00624 LOG_MSG (LOG_ERR, "%s: Failed to allocate parser context\n",
00625 PROGNAME);
00626 goto out;
00627 }
00628
00629 doc = xmlCtxtReadFile(ctxt, opts->input_filename, NULL, XML_PARSE_NOENT);
00630 if (doc == NULL) {
00631 LOG_MSG (LOG_ERR, "%s: Failed to parse %s\n", PROGNAME,
00632 opts->input_filename);
00633 goto out;
00634 } else if (!ctxt->valid) {
00635 LOG_MSG (LOG_ERR, "%s: Failed to validate %s\n", PROGNAME,
00636 opts->input_filename);
00637 xmlFreeDoc(doc);
00638 doc = NULL;
00639 goto out;
00640 }
00641
00642 if (opts->disable_schema) {
00643 ret = 0;
00644 goto out;
00645 }
00646
00647
00648
00649 if (opts->semantic_schema)
00650 schema_ctxt = xmlSchemaNewParserCtxt("/usr/share/test-definition/"
00651 "testdefinition-tm_terms.xsd");
00652 else
00653 schema_ctxt = xmlSchemaNewParserCtxt("/usr/share/test-definition/"
00654 "testdefinition-syntax.xsd");
00655
00656 if (schema_ctxt == NULL) {
00657 LOG_MSG (LOG_ERR, "%s: Failed to allocate schema context\n",
00658 PROGNAME);
00659 goto out;
00660 }
00661
00662 sch = xmlSchemaParse(schema_ctxt);
00663 if (sch == NULL) {
00664 LOG_MSG (LOG_ERR, "%s: Failed to parse schema\n",
00665 PROGNAME);
00666 goto out;
00667 }
00668
00669 valid_ctxt = xmlSchemaNewValidCtxt(sch);
00670 if (valid_ctxt == NULL) {
00671 LOG_MSG (LOG_ERR, "%s: Failed to create schema validation "
00672 "context\n", PROGNAME);
00673 goto out;
00674
00675 }
00676
00677 ret = xmlSchemaValidateDoc(valid_ctxt, doc);
00678 if (ret)
00679 ret = TESTRUNNER_LITE_XML_VALIDATION_FAIL;
00680 out:
00681
00682
00683
00684 if (doc) xmlFreeDoc(doc);
00685 if (ctxt) xmlFreeParserCtxt(ctxt);
00686 if (sch) xmlSchemaFree(sch);
00687 if (valid_ctxt) xmlSchemaFreeValidCtxt(valid_ctxt);
00688 if (schema_ctxt) xmlSchemaFreeParserCtxt(schema_ctxt);
00689
00690 return ret;
00691 }
00692
00697 int td_reader_init (testrunner_lite_options *opts)
00698 {
00699
00700 reader = xmlNewTextReaderFilename(opts->input_filename);
00701 if (!reader) {
00702 LOG_MSG (LOG_ERR, "%s: failed to create xml reader for %s\n",
00703 PROGNAME, opts->input_filename);
00704
00705 }
00706
00707 if (opts->disable_schema)
00708 return 0;
00709
00710 if (opts->semantic_schema)
00711 schema_context = xmlSchemaNewParserCtxt
00712 ("/usr/share/test-definition/"
00713 "testdefinition-tm_terms.xsd");
00714 else
00715 schema_context = xmlSchemaNewParserCtxt
00716 ("/usr/share/test-definition/"
00717 "testdefinition-syntax.xsd");
00718 if (schema_context == NULL) {
00719 LOG_MSG (LOG_ERR, "%s: Failed to allocate schema context\n",
00720 PROGNAME);
00721 goto err_out;
00722 }
00723
00724 schema = xmlSchemaParse(schema_context);
00725 if (schema == NULL) {
00726 LOG_MSG (LOG_ERR, "%s: Failed to parse schema\n",
00727 PROGNAME);
00728 goto err_out;
00729 }
00730
00731 if (xmlTextReaderSetSchema (reader, schema)) {
00732 LOG_MSG (LOG_ERR, "%s: Failed to set schema for xml reader\n",
00733 PROGNAME);
00734 goto err_out;
00735 }
00736
00737
00738 return 0;
00739 err_out:
00740 LOG_MSG (LOG_ERR, "%s:%s: Exiting with error\n",
00741 PROGNAME, __FUNCTION__);
00742 td_reader_close ();
00743 return 1;
00744
00745 }
00746
00749 void td_reader_close ()
00750 {
00751 if (reader) xmlFreeTextReader (reader);
00752 if (schema) xmlSchemaFree(schema);
00753 if (schema_context) xmlSchemaFreeParserCtxt(schema_context);
00754 }
00755
00759 int td_next_node (void) {
00760 int ret;
00761 const xmlChar *name;
00762 xmlReaderTypes type;
00763
00764 ret = xmlTextReaderRead(reader);
00765
00766 if (!ret)
00767 return !ret;
00768
00769 name = xmlTextReaderConstName(reader);
00770 type = xmlTextReaderNodeType(reader);
00771
00772 if (!name)
00773 return 1;
00774
00775 if (!xmlStrcmp (name, BAD_CAST "suite")) {
00776 if (type == XML_READER_TYPE_ELEMENT)
00777 return td_parse_suite();
00778 else if (type == XML_READER_TYPE_END_ELEMENT) {
00779 if (cbs->test_suite_end) cbs->test_suite_end();
00780 return 0;
00781 }
00782 }
00783
00784 if (!xmlStrcmp (name, BAD_CAST "set") &&
00785 type == XML_READER_TYPE_ELEMENT)
00786 return td_parse_set();
00787
00788
00789
00790 return !ret;
00791 }
00792
00796 int td_register_callbacks(td_parser_callbacks *pcbs)
00797 {
00798 cbs = pcbs;
00799
00800 return 0;
00801 }
00802
00803
00804
00805
00806
00807
00808