xml.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. /*
  2. neon test suite
  3. Copyright (C) 2002-2007, Joe Orton <[email protected]>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. */
  16. #include "config.h"
  17. #include <sys/types.h>
  18. #ifdef HAVE_STDLIB_H
  19. #include <stdlib.h>
  20. #endif
  21. #ifdef HAVE_UNISTD_H
  22. #include <unistd.h>
  23. #endif
  24. #include "ne_xml.h"
  25. #include "tests.h"
  26. #include "child.h"
  27. #include "utils.h"
  28. #define ABORT (-42) /* magic code for abort handlers */
  29. #define EVAL_DEFAULT "eval-xmlns-default"
  30. #define EVAL_SPECIFIC "eval-xmlns-specific-"
  31. struct context {
  32. ne_buffer *buf;
  33. ne_xml_parser *parser;
  34. };
  35. /* A set of SAX handlers which serialize SAX events back into a
  36. * pseudo-XML-like string. */
  37. static int startelm(void *userdata, int state,
  38. const char *nspace, const char *name,
  39. const char **atts)
  40. {
  41. struct context *ctx = userdata;
  42. ne_buffer *buf = ctx->buf;
  43. int n;
  44. if (strcmp(name, "decline") == 0)
  45. return NE_XML_DECLINE;
  46. if (strcmp(name, EVAL_DEFAULT) == 0) {
  47. const char *val = ne_xml_resolve_nspace(ctx->parser, NULL, 0);
  48. ne_buffer_concat(ctx->buf, EVAL_DEFAULT "=[", val, "]", NULL);
  49. return NE_XML_DECLINE;
  50. }
  51. else if (strncmp(name, EVAL_SPECIFIC, strlen(EVAL_SPECIFIC)) == 0) {
  52. const char *which = name + strlen(EVAL_SPECIFIC);
  53. const char *r = ne_xml_resolve_nspace(ctx->parser, which, strlen(which));
  54. ne_buffer_concat(ctx->buf, name, "=[", r, "]", NULL);
  55. return NE_XML_DECLINE;
  56. }
  57. ne_buffer_concat(buf, "<", "{", nspace, "}", name, NULL);
  58. for (n = 0; atts && atts[n] != NULL; n+=2) {
  59. ne_buffer_concat(buf, " ", atts[n], "='", atts[n+1], "'", NULL);
  60. }
  61. ne_buffer_zappend(buf, ">");
  62. return state + 1;
  63. }
  64. static int chardata(void *userdata, int state, const char *cdata, size_t len)
  65. {
  66. struct context *ctx = userdata;
  67. ne_buffer_append(ctx->buf, cdata, len);
  68. return strncmp(cdata, "!ABORT!", len) == 0 ? ABORT : NE_XML_DECLINE;
  69. }
  70. static int endelm(void *userdata, int state,
  71. const char *nspace, const char *name)
  72. {
  73. struct context *ctx = userdata;
  74. ne_buffer_concat(ctx->buf, "</{", nspace, "}", name, ">", NULL);
  75. return 0;
  76. }
  77. /* A set of SAX handlers which do as above, but change some element
  78. * names; used to check nested SAX handling is working properly. */
  79. static int startelm_xform(void *userdata, int state,
  80. const char *nspace, const char *name,
  81. const char **atts)
  82. {
  83. if (strcmp(nspace, "two") == 0)
  84. return startelm(userdata, state, nspace, "xform", atts);
  85. else
  86. return NE_XML_DECLINE;
  87. }
  88. static int endelm_xform(void *userdata, int state,
  89. const char *nspace, const char *name)
  90. {
  91. if (strcmp(nspace, "two") == 0)
  92. return endelm(userdata, state, nspace, "xform");
  93. else
  94. return NE_XML_DECLINE;
  95. }
  96. /* A set of SAX handlers which verify that state handling is working
  97. * correctly. */
  98. static int startelm_state(void *userdata, int parent,
  99. const char *nspace, const char *name,
  100. const char **atts)
  101. {
  102. struct context *ctx = userdata;
  103. int n;
  104. if (strcmp(nspace, "state") != 0)
  105. return NE_XML_DECLINE;
  106. for (n = 0; atts[n]; n += 2) {
  107. if (strcmp(atts[n], "parent") == 0) {
  108. int expected = atoi(atts[n+1]);
  109. if (expected != parent) {
  110. char err[50];
  111. sprintf(err, "parent state of %s was %d not %d", name, parent,
  112. expected);
  113. ne_buffer_zappend(ctx->buf, err);
  114. }
  115. }
  116. }
  117. return atoi(name+1);
  118. }
  119. static int endelm_state(void *userdata, int state,
  120. const char *nspace, const char *name)
  121. {
  122. int expected = atoi(name + 1);
  123. struct context *ctx = userdata;
  124. if (state != expected)
  125. ne_buffer_concat(ctx->buf, "wrong state in endelm of ", name, NULL);
  126. return 0;
  127. }
  128. /* A set of SAX handlers which verify that abort handling is working
  129. * correctly. */
  130. static int startelm_abort(void *userdata, int parent,
  131. const char *nspace, const char *name,
  132. const char **atts)
  133. {
  134. struct context *ctx = userdata;
  135. if (strcmp(name, "abort-start") == 0) {
  136. ne_buffer_zappend(ctx->buf, "ABORT");
  137. return ABORT;
  138. } else
  139. return startelm(ctx, parent, nspace, name, atts);
  140. }
  141. static int endelm_abort(void *userdata, int state,
  142. const char *nspace, const char *name)
  143. {
  144. struct context *ctx = userdata;
  145. if (strcmp(name, "abort-end") == 0) {
  146. ne_buffer_zappend(ctx->buf, "ABORT");
  147. return ABORT;
  148. } else
  149. return 0;
  150. }
  151. /* Test mode for parse_match: */
  152. enum match_type {
  153. match_valid = 0, /* test that the parse succeeds */
  154. match_invalid, /* test that the parse fails */
  155. match_nohands, /* test with no handlers registered */
  156. match_encoding, /* test whether the encoding is equal to the result string */
  157. match_chunked /* parse the document one byte at a time */
  158. };
  159. static int parse_match(const char *doc, const char *result,
  160. enum match_type t)
  161. {
  162. const char *origdoc = doc;
  163. ne_xml_parser *p = ne_xml_create();
  164. ne_buffer *buf = ne_buffer_create();
  165. int ret;
  166. struct context ctx;
  167. ctx.buf = buf;
  168. ctx.parser = p;
  169. if (t == match_invalid)
  170. ne_xml_push_handler(p, startelm_abort, chardata, endelm_abort, &ctx);
  171. if (t != match_encoding && t != match_nohands) {
  172. ne_xml_push_handler(p, startelm_state, NULL, endelm_state, &ctx);
  173. ne_xml_push_handler(p, startelm, chardata, endelm, &ctx);
  174. ne_xml_push_handler(p, startelm_xform, chardata, endelm_xform, &ctx);
  175. }
  176. if (t == match_chunked) {
  177. do {
  178. ret = ne_xml_parse(p, doc++, 1);
  179. } while (ret == 0 && *doc);
  180. } else {
  181. ret = ne_xml_parse(p, doc, strlen(doc));
  182. }
  183. if (ret == 0) {
  184. ne_xml_parse(p, "", 0);
  185. }
  186. ONV(ret != ne_xml_failed(p),
  187. ("'%s': ne_xml_failed gave %d not %d", origdoc, ne_xml_failed(p), ret));
  188. if (t == match_invalid)
  189. ONV(ret != ABORT,
  190. ("for '%s': parse got %d not abort failure: %s", origdoc, ret,
  191. buf->data));
  192. else
  193. ONV(ret, ("for '%s': parse failed: %s", origdoc, ne_xml_get_error(p)));
  194. if (t == match_encoding) {
  195. const char *enc = ne_xml_doc_encoding(p);
  196. ONV(strcmp(enc, result),
  197. ("for '%s': encoding was `%s' not `%s'", origdoc, enc, result));
  198. }
  199. else if (t == match_valid || t == match_chunked) {
  200. ONV(strcmp(result, buf->data),
  201. ("for '%s': result mismatch: %s not %s", origdoc, buf->data,
  202. result));
  203. }
  204. ne_xml_destroy(p);
  205. ne_buffer_destroy(buf);
  206. return OK;
  207. }
  208. static int matches(void)
  209. {
  210. #define PFX "<?xml version='1.0' encoding='utf-8'?>\r\n"
  211. #define E(ns, n) "<{" ns "}" n "></{" ns "}" n ">"
  212. static const struct {
  213. const char *in, *out;
  214. enum match_type invalid;
  215. } ms[] = {
  216. /*** Simplest tests ***/
  217. { PFX "<hello/>", "<{}hello></{}hello>"},
  218. { PFX "<hello foo='bar'/>",
  219. "<{}hello foo='bar'></{}hello>"},
  220. /*** Tests for character data handling. ***/
  221. { PFX "<hello> world</hello>", "<{}hello> world</{}hello>"},
  222. /* test for cdata between elements. */
  223. { PFX "<hello>\r\n<wide> world</wide></hello>",
  224. "<{}hello>\n<{}wide> world</{}wide></{}hello>"},
  225. /* UTF-8 XML Byte Order Mark */
  226. { "\xEF\xBB\xBF" PFX "<hello/>", "<{}hello></{}hello>" },
  227. /* UTF-8 XML Byte Order Mark */
  228. { "\xEF\xBB\xBF" PFX "<hello/>", "<{}hello></{}hello>", match_chunked },
  229. /* UTF-8 XML Byte Order Mark sans prolog */
  230. { "\xEF\xBB\xBF" "<hello/>", "<{}hello></{}hello>" },
  231. /*** Tests for namespace handling. ***/
  232. #define NSA "xmlns:foo='bar'"
  233. { PFX "<foo:widget " NSA "/>",
  234. "<{bar}widget " NSA ">"
  235. "</{bar}widget>" },
  236. /* inherited namespace expansion. */
  237. { PFX "<widget " NSA "><foo:norman/></widget>",
  238. "<{}widget " NSA ">" E("bar", "norman") "</{}widget>"},
  239. { PFX "<widget " NSA " xmlns:abc='def' xmlns:g='z'>"
  240. "<foo:norman/></widget>",
  241. "<{}widget " NSA " xmlns:abc='def' xmlns:g='z'>"
  242. E("bar", "norman") "</{}widget>"},
  243. /* empty namespace default takes precedence. */
  244. { PFX "<widget xmlns='foo'><smidgen xmlns=''><norman/>"
  245. "</smidgen></widget>",
  246. "<{foo}widget xmlns='foo'><{}smidgen xmlns=''>"
  247. E("", "norman")
  248. "</{}smidgen></{foo}widget>" },
  249. /* inherited empty namespace default */
  250. { PFX "<bar xmlns='foo'><grok xmlns=''><fish/></grok></bar>",
  251. "<{foo}bar xmlns='foo'><{}grok xmlns=''>"
  252. E("", "fish") "</{}grok></{foo}bar>" },
  253. /* regression test for neon <= 0.23.5 with libxml2, where the
  254. * "dereference entities" flag was not set by default. */
  255. { PFX "<widget foo=\"no&amp;body\"/>",
  256. "<{}widget foo='no&body'></{}widget>" },
  257. { PFX "<widget foo=\"no&#x20;body\"/>",
  258. "<{}widget foo='no body'></{}widget>" },
  259. /* tests for declined branches */
  260. { PFX "<hello><decline>fish</decline>"
  261. "<world><decline/>yes</world>goodbye<decline/></hello>",
  262. "<{}hello><{}world>yes</{}world>goodbye</{}hello>" },
  263. { PFX
  264. "<hello><decline><nested>fish</nested>bar</decline><fish/></hello>",
  265. "<{}hello>" E("", "fish") "</{}hello>" },
  266. /* tests for nested SAX handlers */
  267. { PFX "<hello xmlns='two'><decline/></hello>",
  268. "<{two}hello xmlns='two'>" E("two", "xform") "</{two}hello>"},
  269. /* test for nspace resolution. */
  270. { PFX "<hello xmlns='fish'><" EVAL_DEFAULT "/></hello>",
  271. "<{fish}hello xmlns='fish'>" EVAL_DEFAULT "=[fish]" "</{fish}hello>" },
  272. { PFX "<hello><" EVAL_DEFAULT "/></hello>",
  273. "<{}hello>" EVAL_DEFAULT "=[]</{}hello>" },
  274. { PFX "<hello xmlns:foo='bar'><" EVAL_SPECIFIC "foo/></hello>",
  275. "<{}hello xmlns:foo='bar'>" EVAL_SPECIFIC "foo=[bar]</{}hello>" },
  276. /* tests for state handling */
  277. { PFX "<a55 xmlns='state'/>", "" },
  278. { PFX "<a777 xmlns='state' parent='0'/>", "" },
  279. { PFX "<a99 xmlns='state'><f77 parent='99'/>blah</a99>", "" },
  280. /* tests for abort handling */
  281. { PFX "<hello><merry><abort-start/></merry></hello>",
  282. "<{}hello><{}merry>ABORT", match_invalid },
  283. { PFX "<hello><merry><abort-end/>fish</merry></hello>",
  284. "<{}hello><{}merry><{}abort-end>ABORT", match_invalid },
  285. { PFX "<hello>!ABORT!</hello>", "<{}hello>!ABORT!", match_invalid },
  286. { PFX "<hello>!ABORT!<foo/></hello>", "<{}hello>!ABORT!", match_invalid },
  287. { PFX "<hello>!ABORT!</fish>", "<{}hello>!ABORT!", match_invalid },
  288. /* tests for encodings */
  289. { "<?xml version='1.0' encoding='ISO-8859-1'?><hello/>",
  290. "ISO-8859-1", match_encoding },
  291. { "<?xml version='1.0' encoding='UTF-8'?><hello/>",
  292. "UTF-8", match_encoding },
  293. /* test that parse is valid even with no handlers registered. */
  294. { PFX "<hello><old>world</old></hello>", "", match_nohands },
  295. /* regression test for prefix matching bug fixed in 0.18.0 */
  296. #define THENS "xmlns:d='foo' xmlns:dd='bar'"
  297. { PFX "<d:hello " THENS "/>",
  298. "<{foo}hello " THENS "></{foo}hello>" },
  299. /**** end of list ****/ { NULL, NULL }
  300. };
  301. int n;
  302. for (n = 0; ms[n].in != NULL; n++) {
  303. CALL(parse_match(ms[n].in, ms[n].out, ms[n].invalid));
  304. }
  305. return OK;
  306. }
  307. static int mapping(void)
  308. {
  309. static const struct ne_xml_idmap map[] = {
  310. { "fee", "bar", 1 },
  311. { "foo", "bar", 2 },
  312. { "bar", "foo", 3 },
  313. { "", "bob", 4 },
  314. { "balloon", "buffoon", 5},
  315. { NULL, NULL, 0}
  316. };
  317. int n;
  318. for (n = 0; map[n].id; n++) {
  319. int id = ne_xml_mapid(map, NE_XML_MAPLEN(map) - 1,
  320. map[n].nspace, map[n].name);
  321. ONV(id != map[n].id, ("mapped to id %d not %d", id, map[n].id));
  322. }
  323. n = ne_xml_mapid(map, NE_XML_MAPLEN(map) - 1, "no-such", "element");
  324. ONV(n != 0, ("unknown element got id %d not zero", n));
  325. return OK;
  326. }
  327. /* Test for some parse failures */
  328. static int fail_parse(void)
  329. {
  330. static const char *docs[] = {
  331. "foo",
  332. PFX "<bar:foo/>",
  333. /* malformed namespace declarations */
  334. PFX "<foo xmlns:D=''/>",
  335. PFX "<foo xmlns:='fish'/>",
  336. PFX "<foo xmlns:.bar='fish'/>",
  337. PFX "<foo xmlns:-bar='fish'/>",
  338. PFX "<foo xmlns:0bar='fish'/>",
  339. PFX "<fee xmlns:8baz='bar'/>",
  340. /* element names which are not valid QNames. */
  341. PFX "<foo: xmlns:foo='bar'/>",
  342. PFX "<:fee/>",
  343. PFX "<0fish/>",
  344. PFX "<foo:0fish xmlns:foo='bar'/>",
  345. PFX "<foo:9fish xmlns:foo='bar'/>",
  346. PFX "<foo:-fish xmlns:foo='bar'/>",
  347. PFX "<foo:.fish xmlns:foo='bar'/>",
  348. #if 0 /* currently disabled to allow SVN to work */
  349. PFX "<foo:bar:baz xmlns:foo='bar'/>",
  350. PFX "<fee xmlns:baz:bar='bar'/>",
  351. PFX "<fee xmlns::bar='bar'/>",
  352. PFX "<foo::fish xmlns:foo='bar'/>",
  353. #endif
  354. /* These are tests of XML parser itself really... */
  355. /* 2-byte encoding of '.': */
  356. PFX "<foo>" "\x2F\xC0\xAE\x2E\x2F" "</foo>",
  357. /* 3-byte encoding of '.': */
  358. PFX "<foo>" "\x2F\xE0\x80\xAE\x2E\x2F" "</foo>",
  359. /* 4-byte encoding of '.': */
  360. PFX "<foo>" "\x2F\xF0\x80\x80\xAE\x2E\x2F" "</foo>",
  361. /* 5-byte encoding of '.': */
  362. PFX "<foo>" "\x2F\xF8\x80\x80\x80\xAE\x2E\x2F" "</foo>",
  363. /* 6-byte encoding of '.': */
  364. PFX "<foo>" "\x2F\xFC\x80\x80\x80\x80\xAE\x2E\x2F" "</foo>",
  365. /* two-byte encoding of '<' must not be parsed as a '<': */
  366. PFX "\xC0\xBC" "foo></foo>",
  367. /* Invalid UTF-8 XML Byte Order Marks */
  368. "\xEF\xBB" PFX "<hello/>",
  369. "\xEF" PFX "<hello/>",
  370. "<?xml version=\"1.0\"?>\
  371. <!DOCTYPE billion [\
  372. <!ELEMENT billion (#PCDATA)>\
  373. <!ENTITY laugh0 \"ha\">\
  374. <!ENTITY laugh1 \"&laugh0;&laugh0;\">\
  375. <!ENTITY laugh2 \"&laugh1;&laugh1;\">\
  376. <!ENTITY laugh3 \"&laugh2;&laugh2;\">\
  377. <!ENTITY laugh4 \"&laugh3;&laugh3;\">\
  378. <!ENTITY laugh5 \"&laugh4;&laugh4;\">\
  379. <!ENTITY laugh6 \"&laugh5;&laugh5;\">\
  380. <!ENTITY laugh7 \"&laugh6;&laugh6;\">\
  381. <!ENTITY laugh8 \"&laugh7;&laugh7;\">\
  382. <!ENTITY laugh9 \"&laugh8;&laugh8;\">\
  383. <!ENTITY laugh10 \"&laugh9;&laugh9;\">\
  384. <!ENTITY laugh11 \"&laugh10;&laugh10;\">\
  385. <!ENTITY laugh12 \"&laugh11;&laugh11;\">\
  386. <!ENTITY laugh13 \"&laugh12;&laugh12;\">\
  387. <!ENTITY laugh14 \"&laugh13;&laugh13;\">\
  388. <!ENTITY laugh15 \"&laugh14;&laugh14;\">\
  389. <!ENTITY laugh16 \"&laugh15;&laugh15;\">\
  390. <!ENTITY laugh17 \"&laugh16;&laugh16;\">\
  391. <!ENTITY laugh18 \"&laugh17;&laugh17;\">\
  392. <!ENTITY laugh19 \"&laugh18;&laugh18;\">\
  393. <!ENTITY laugh20 \"&laugh19;&laugh19;\">\
  394. <!ENTITY laugh21 \"&laugh20;&laugh20;\">\
  395. <!ENTITY laugh22 \"&laugh21;&laugh21;\">\
  396. <!ENTITY laugh23 \"&laugh22;&laugh22;\">\
  397. <!ENTITY laugh24 \"&laugh23;&laugh23;\">\
  398. <!ENTITY laugh25 \"&laugh24;&laugh24;\">\
  399. <!ENTITY laugh26 \"&laugh25;&laugh25;\">\
  400. <!ENTITY laugh27 \"&laugh26;&laugh26;\">\
  401. <!ENTITY laugh28 \"&laugh27;&laugh27;\">\
  402. <!ENTITY laugh29 \"&laugh28;&laugh28;\">\
  403. <!ENTITY laugh30 \"&laugh29;&laugh29;\">\
  404. ]>\
  405. <billion>&laugh30;</billion>\
  406. ",
  407. NULL
  408. };
  409. int n;
  410. for (n = 0; docs[n]; n++) {
  411. ne_xml_parser *p = ne_xml_create();
  412. const char *err;
  413. ne_xml_parse(p, docs[n], strlen(docs[n]));
  414. ne_xml_parse(p, "", 0);
  415. ONV(ne_xml_failed(p) <= 0,
  416. ("`%s' did not get positive parse error", docs[n]));
  417. err = ne_xml_get_error(p);
  418. NE_DEBUG(NE_DBG_HTTP, "Parse error for '%s': %s\n", docs[n], err);
  419. ONV(strstr(err, "parse error") == NULL
  420. && strstr(err, "Invalid Byte Order Mark") == NULL,
  421. ("bad error %s", err));
  422. ne_xml_destroy(p);
  423. }
  424. return OK;
  425. }
  426. static int check_attrib(ne_xml_parser *p, const char **atts,
  427. const char *nspace, const char *name,
  428. const char *value)
  429. {
  430. const char *act = ne_xml_get_attr(p, atts, nspace, name);
  431. char err[50];
  432. int ret = 0;
  433. if (value == NULL) {
  434. if (act != NULL) {
  435. sprintf(err, "attribute %s was set to %s", name, act);
  436. ret = NE_XML_ABORT;
  437. }
  438. } else {
  439. if (act == NULL) {
  440. sprintf(err, "attribute %s not found", name);
  441. ret = NE_XML_ABORT;
  442. } else if (strcmp(act, value) != 0) {
  443. sprintf(err, "attribute %s was %s not %s", name, act, value);
  444. ret = NE_XML_ABORT;
  445. }
  446. }
  447. if (ret == NE_XML_ABORT) ne_xml_set_error(p, err);
  448. return ret;
  449. }
  450. static int startelm_attrib(void *userdata, int state,
  451. const char *nspace, const char *name,
  452. const char **atts)
  453. {
  454. ne_xml_parser *p = userdata;
  455. if (strcmp(name, "hello") == 0) {
  456. CALL(check_attrib(p, atts, NULL, "first", "second"));
  457. CALL(check_attrib(p, atts, NULL, "third", ""));
  458. CALL(check_attrib(p, atts, "garth", "bar", "asda"));
  459. CALL(check_attrib(p, atts, "giraffe", "bar", NULL));
  460. CALL(check_attrib(p, atts, "hot", "dog", NULL));
  461. CALL(check_attrib(p, atts, NULL, "nonesuch", NULL));
  462. } else if (strcmp(name, "goodbye") == 0) {
  463. if (atts[0] != NULL) {
  464. ne_xml_set_error(p, "non-empty attrib array");
  465. return 1;
  466. }
  467. }
  468. return 1;
  469. }
  470. static int attributes(void)
  471. {
  472. ne_xml_parser *p = ne_xml_create();
  473. static const char doc[] = PFX
  474. "<hello xmlns:foo='garth' first='second' third='' "
  475. "foo:bar='asda' goof:bar='jar'><goodbye/></hello>";
  476. ne_xml_push_handler(p, startelm_attrib, NULL, NULL, p);
  477. ne_xml_parse_v(p, doc, strlen(doc));
  478. ONV(ne_xml_failed(p), ("parse error: %s", ne_xml_get_error(p)));
  479. ne_xml_destroy(p);
  480. return OK;
  481. }
  482. /* Test for the get/set error interface */
  483. static int errors(void)
  484. {
  485. ne_xml_parser *p = ne_xml_create();
  486. const char *err;
  487. ONV(strcmp(ne_xml_get_error(p), "Unknown error") != 0,
  488. ("initial error string unspecified"));
  489. ne_xml_set_error(p, "Fish food");
  490. err = ne_xml_get_error(p);
  491. ONV(strcmp(err, "Fish food"), ("wrong error %s!", err));
  492. ne_xml_destroy(p);
  493. return 0;
  494. }
  495. ne_test tests[] = {
  496. T(matches),
  497. T(mapping),
  498. T(fail_parse),
  499. T(attributes),
  500. T(errors),
  501. T(NULL)
  502. };