Remove no-longer-used svn_*_get_mergeinfo_for_tree APIs.
[svn.git] / subversion / libsvn_subr / xml.c
blobbb58dc43728a9aa4e8d1cc20016b51183acc8f8d
1 /*
2 * xml.c: xml helper code shared among the Subversion libraries.
4 * ====================================================================
5 * Copyright (c) 2000-2006 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #include <string.h>
22 #include <assert.h>
24 #include "svn_private_config.h" /* for SVN_HAVE_OLD_EXPAT */
25 #include "svn_pools.h"
26 #include "svn_xml.h"
27 #include "svn_error.h"
28 #include "svn_ctype.h"
29 #include "utf_impl.h"
31 #ifdef SVN_HAVE_OLD_EXPAT
32 #include <xmlparse.h>
33 #else
34 #include <expat.h>
35 #endif
37 #ifdef XML_UNICODE
38 #error Expat is unusable -- it has been compiled for wide characters
39 #endif
41 /* The private internals for a parser object. */
42 struct svn_xml_parser_t
44 /** the expat parser */
45 XML_Parser parser;
47 /** the SVN callbacks to call from the Expat callbacks */
48 svn_xml_start_elem start_handler;
49 svn_xml_end_elem end_handler;
50 svn_xml_char_data data_handler;
52 /** the user's baton for private data */
53 void *baton;
55 /** if non-@c NULL, an error happened while parsing */
56 svn_error_t *error;
58 /** where this object is allocated, so we can free it easily */
59 apr_pool_t *pool;
64 /*** XML character validation ***/
66 svn_boolean_t
67 svn_xml_is_xml_safe(const char *data, apr_size_t len)
69 const char *end = data + len;
70 const char *p;
72 if (! svn_utf__is_valid(data, len))
73 return FALSE;
75 for (p = data; p < end; p++)
77 unsigned char c = *p;
79 if (svn_ctype_iscntrl(c))
81 if ((c != SVN_CTYPE_ASCII_TAB)
82 && (c != SVN_CTYPE_ASCII_LINEFEED)
83 && (c != SVN_CTYPE_ASCII_CARRIAGERETURN)
84 && (c != SVN_CTYPE_ASCII_DELETE))
85 return FALSE;
88 return TRUE;
95 /*** XML escaping. ***/
97 static void
98 xml_escape_cdata(svn_stringbuf_t **outstr,
99 const char *data,
100 apr_size_t len,
101 apr_pool_t *pool)
103 const char *end = data + len;
104 const char *p = data, *q;
106 if (*outstr == NULL)
107 *outstr = svn_stringbuf_create("", pool);
109 while (1)
111 /* Find a character which needs to be quoted and append bytes up
112 to that point. Strictly speaking, '>' only needs to be
113 quoted if it follows "]]", but it's easier to quote it all
114 the time.
116 So, why are we escaping '\r' here? Well, according to the
117 XML spec, '\r\n' gets converted to '\n' during XML parsing.
118 Also, any '\r' not followed by '\n' is converted to '\n'. By
119 golly, if we say we want to escape a '\r', we want to make
120 sure it remains a '\r'! */
121 q = p;
122 while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '\r')
123 q++;
124 svn_stringbuf_appendbytes(*outstr, p, q - p);
126 /* We may already be a winner. */
127 if (q == end)
128 break;
130 /* Append the entity reference for the character. */
131 if (*q == '&')
132 svn_stringbuf_appendcstr(*outstr, "&amp;");
133 else if (*q == '<')
134 svn_stringbuf_appendcstr(*outstr, "&lt;");
135 else if (*q == '>')
136 svn_stringbuf_appendcstr(*outstr, "&gt;");
137 else if (*q == '\r')
138 svn_stringbuf_appendcstr(*outstr, "&#13;");
140 p = q + 1;
144 /* Essentially the same as xml_escape_cdata, with the addition of
145 whitespace and quote characters. */
146 static void
147 xml_escape_attr(svn_stringbuf_t **outstr,
148 const char *data,
149 apr_size_t len,
150 apr_pool_t *pool)
152 const char *end = data + len;
153 const char *p = data, *q;
155 if (*outstr == NULL)
156 *outstr = svn_stringbuf_create("", pool);
158 while (1)
160 /* Find a character which needs to be quoted and append bytes up
161 to that point. */
162 q = p;
163 while (q < end && *q != '&' && *q != '<' && *q != '>'
164 && *q != '"' && *q != '\'' && *q != '\r'
165 && *q != '\n' && *q != '\t')
166 q++;
167 svn_stringbuf_appendbytes(*outstr, p, q - p);
169 /* We may already be a winner. */
170 if (q == end)
171 break;
173 /* Append the entity reference for the character. */
174 if (*q == '&')
175 svn_stringbuf_appendcstr(*outstr, "&amp;");
176 else if (*q == '<')
177 svn_stringbuf_appendcstr(*outstr, "&lt;");
178 else if (*q == '>')
179 svn_stringbuf_appendcstr(*outstr, "&gt;");
180 else if (*q == '"')
181 svn_stringbuf_appendcstr(*outstr, "&quot;");
182 else if (*q == '\'')
183 svn_stringbuf_appendcstr(*outstr, "&apos;");
184 else if (*q == '\r')
185 svn_stringbuf_appendcstr(*outstr, "&#13;");
186 else if (*q == '\n')
187 svn_stringbuf_appendcstr(*outstr, "&#10;");
188 else if (*q == '\t')
189 svn_stringbuf_appendcstr(*outstr, "&#9;");
191 p = q + 1;
196 void
197 svn_xml_escape_cdata_stringbuf(svn_stringbuf_t **outstr,
198 const svn_stringbuf_t *string,
199 apr_pool_t *pool)
201 xml_escape_cdata(outstr, string->data, string->len, pool);
205 void
206 svn_xml_escape_cdata_string(svn_stringbuf_t **outstr,
207 const svn_string_t *string,
208 apr_pool_t *pool)
210 xml_escape_cdata(outstr, string->data, string->len, pool);
214 void
215 svn_xml_escape_cdata_cstring(svn_stringbuf_t **outstr,
216 const char *string,
217 apr_pool_t *pool)
219 xml_escape_cdata(outstr, string, (apr_size_t) strlen(string), pool);
223 void
224 svn_xml_escape_attr_stringbuf(svn_stringbuf_t **outstr,
225 const svn_stringbuf_t *string,
226 apr_pool_t *pool)
228 xml_escape_attr(outstr, string->data, string->len, pool);
232 void
233 svn_xml_escape_attr_string(svn_stringbuf_t **outstr,
234 const svn_string_t *string,
235 apr_pool_t *pool)
237 xml_escape_attr(outstr, string->data, string->len, pool);
241 void
242 svn_xml_escape_attr_cstring(svn_stringbuf_t **outstr,
243 const char *string,
244 apr_pool_t *pool)
246 xml_escape_attr(outstr, string, (apr_size_t) strlen(string), pool);
250 const char *
251 svn_xml_fuzzy_escape(const char *string, apr_pool_t *pool)
253 const char *end = string + strlen(string);
254 const char *p = string, *q;
255 svn_stringbuf_t *outstr;
256 char escaped_char[6]; /* ? \ u u u \0 */
258 for (q = p; q < end; q++)
260 if (svn_ctype_iscntrl(*q)
261 && ! ((*q == '\n') || (*q == '\r') || (*q == '\t')))
262 break;
265 /* Return original string if no unsafe characters found. */
266 if (q == end)
267 return string;
269 outstr = svn_stringbuf_create("", pool);
270 while (1)
272 q = p;
274 /* Traverse till either unsafe character or eos. */
275 while ((q < end)
276 && ((! svn_ctype_iscntrl(*q))
277 || (*q == '\n') || (*q == '\r') || (*q == '\t')))
278 q++;
280 /* copy chunk before marker */
281 svn_stringbuf_appendbytes(outstr, p, q - p);
283 if (q == end)
284 break;
286 /* Append an escaped version of the unsafe character.
288 ### This format was chosen for consistency with
289 ### svn_utf__cstring_from_utf8_fuzzy(). The two functions
290 ### should probably share code, even though they escape
291 ### different characters.
293 sprintf(escaped_char, "?\\%03u", (unsigned char) *q);
294 svn_stringbuf_appendcstr(outstr, escaped_char);
296 p = q + 1;
299 return outstr->data;
303 /*** Map from the Expat callback types to the SVN XML types. ***/
305 static void expat_start_handler(void *userData,
306 const XML_Char *name,
307 const XML_Char **atts)
309 svn_xml_parser_t *svn_parser = userData;
311 (*svn_parser->start_handler)(svn_parser->baton, name, atts);
314 static void expat_end_handler(void *userData, const XML_Char *name)
316 svn_xml_parser_t *svn_parser = userData;
318 (*svn_parser->end_handler)(svn_parser->baton, name);
321 static void expat_data_handler(void *userData, const XML_Char *s, int len)
323 svn_xml_parser_t *svn_parser = userData;
325 (*svn_parser->data_handler)(svn_parser->baton, s, (apr_size_t)len);
329 /*** Making a parser. ***/
331 svn_xml_parser_t *
332 svn_xml_make_parser(void *baton,
333 svn_xml_start_elem start_handler,
334 svn_xml_end_elem end_handler,
335 svn_xml_char_data data_handler,
336 apr_pool_t *pool)
338 svn_xml_parser_t *svn_parser;
339 apr_pool_t *subpool;
341 XML_Parser parser = XML_ParserCreate(NULL);
343 XML_SetElementHandler(parser,
344 start_handler ? expat_start_handler : NULL,
345 end_handler ? expat_end_handler : NULL);
346 XML_SetCharacterDataHandler(parser,
347 data_handler ? expat_data_handler : NULL);
349 /* ### we probably don't want this pool; or at least we should pass it
350 ### to the callbacks and clear it periodically. */
351 subpool = svn_pool_create(pool);
353 svn_parser = apr_pcalloc(subpool, sizeof(*svn_parser));
355 svn_parser->parser = parser;
356 svn_parser->start_handler = start_handler;
357 svn_parser->end_handler = end_handler;
358 svn_parser->data_handler = data_handler;
359 svn_parser->baton = baton;
360 svn_parser->pool = subpool;
362 /* store our parser info as the UserData in the Expat parser */
363 XML_SetUserData(parser, svn_parser);
365 return svn_parser;
369 /* Free a parser */
370 void
371 svn_xml_free_parser(svn_xml_parser_t *svn_parser)
373 /* Free the expat parser */
374 XML_ParserFree(svn_parser->parser);
376 /* Free the subversion parser */
377 svn_pool_destroy(svn_parser->pool);
383 svn_error_t *
384 svn_xml_parse(svn_xml_parser_t *svn_parser,
385 const char *buf,
386 apr_size_t len,
387 svn_boolean_t is_final)
389 svn_error_t *err;
390 int success;
392 /* Parse some xml data */
393 success = XML_Parse(svn_parser->parser, buf, len, is_final);
395 /* If expat choked internally, return its error. */
396 if (! success)
398 err = svn_error_createf
399 (SVN_ERR_XML_MALFORMED, NULL,
400 _("Malformed XML: %s at line %d"),
401 XML_ErrorString(XML_GetErrorCode(svn_parser->parser)),
402 XML_GetCurrentLineNumber(svn_parser->parser));
404 /* Kill all parsers and return the expat error */
405 svn_xml_free_parser(svn_parser);
406 return err;
409 /* Did an error occur somewhere *inside* the expat callbacks? */
410 if (svn_parser->error)
412 err = svn_parser->error;
413 svn_xml_free_parser(svn_parser);
414 return err;
417 return SVN_NO_ERROR;
422 void svn_xml_signal_bailout(svn_error_t *error,
423 svn_xml_parser_t *svn_parser)
425 /* This will cause the current XML_Parse() call to finish quickly! */
426 XML_SetElementHandler(svn_parser->parser, NULL, NULL);
427 XML_SetCharacterDataHandler(svn_parser->parser, NULL);
429 /* Once outside of XML_Parse(), the existence of this field will
430 cause svn_delta_parse()'s main read-loop to return error. */
431 svn_parser->error = error;
441 /*** Attribute walking. ***/
443 const char *
444 svn_xml_get_attr_value(const char *name, const char **atts)
446 while (atts && (*atts))
448 if (strcmp(atts[0], name) == 0)
449 return atts[1];
450 else
451 atts += 2; /* continue looping */
454 /* Else no such attribute name seen. */
455 return NULL;
460 /*** Printing XML ***/
462 void
463 svn_xml_make_header(svn_stringbuf_t **str, apr_pool_t *pool)
465 if (*str == NULL)
466 *str = svn_stringbuf_create("", pool);
467 svn_stringbuf_appendcstr(*str,
468 "<?xml version=\"1.0\"?>\n");
473 /*** Creating attribute hashes. ***/
475 /* Combine an existing attribute list ATTS with a HASH that itself
476 represents an attribute list. Iff PRESERVE is true, then no value
477 already in HASH will be changed, else values from ATTS will
478 override previous values in HASH. */
479 static void
480 amalgamate(const char **atts,
481 apr_hash_t *ht,
482 svn_boolean_t preserve,
483 apr_pool_t *pool)
485 const char *key;
487 if (atts)
488 for (key = *atts; key; key = *(++atts))
490 const char *val = *(++atts);
491 size_t keylen;
492 assert(key != NULL);
493 /* kff todo: should we also insist that val be non-null here?
494 Probably. */
496 keylen = strlen(key);
497 if (preserve && ((apr_hash_get(ht, key, keylen)) != NULL))
498 continue;
499 else
500 apr_hash_set(ht, apr_pstrndup(pool, key, keylen), keylen,
501 val ? apr_pstrdup(pool, val) : NULL);
506 apr_hash_t *
507 svn_xml_ap_to_hash(va_list ap, apr_pool_t *pool)
509 apr_hash_t *ht = apr_hash_make(pool);
510 const char *key;
512 while ((key = va_arg(ap, char *)) != NULL)
514 const char *val = va_arg(ap, const char *);
515 apr_hash_set(ht, key, APR_HASH_KEY_STRING, val);
518 return ht;
522 apr_hash_t *
523 svn_xml_make_att_hash(const char **atts, apr_pool_t *pool)
525 apr_hash_t *ht = apr_hash_make(pool);
526 amalgamate(atts, ht, 0, pool); /* third arg irrelevant in this case */
527 return ht;
531 void
532 svn_xml_hash_atts_overlaying(const char **atts,
533 apr_hash_t *ht,
534 apr_pool_t *pool)
536 amalgamate(atts, ht, 0, pool);
540 void
541 svn_xml_hash_atts_preserving(const char **atts,
542 apr_hash_t *ht,
543 apr_pool_t *pool)
545 amalgamate(atts, ht, 1, pool);
550 /*** Making XML tags. ***/
553 void
554 svn_xml_make_open_tag_hash(svn_stringbuf_t **str,
555 apr_pool_t *pool,
556 enum svn_xml_open_tag_style style,
557 const char *tagname,
558 apr_hash_t *attributes)
560 apr_hash_index_t *hi;
562 if (*str == NULL)
563 *str = svn_stringbuf_create("", pool);
565 svn_stringbuf_appendcstr(*str, "<");
566 svn_stringbuf_appendcstr(*str, tagname);
568 for (hi = apr_hash_first(pool, attributes); hi; hi = apr_hash_next(hi))
570 const void *key;
571 void *val;
573 apr_hash_this(hi, &key, NULL, &val);
574 assert(val != NULL);
576 svn_stringbuf_appendcstr(*str, "\n ");
577 svn_stringbuf_appendcstr(*str, key);
578 svn_stringbuf_appendcstr(*str, "=\"");
579 svn_xml_escape_attr_cstring(str, val, pool);
580 svn_stringbuf_appendcstr(*str, "\"");
583 if (style == svn_xml_self_closing)
584 svn_stringbuf_appendcstr(*str, "/");
585 svn_stringbuf_appendcstr(*str, ">");
586 if (style != svn_xml_protect_pcdata)
587 svn_stringbuf_appendcstr(*str, "\n");
591 void
592 svn_xml_make_open_tag_v(svn_stringbuf_t **str,
593 apr_pool_t *pool,
594 enum svn_xml_open_tag_style style,
595 const char *tagname,
596 va_list ap)
598 apr_pool_t *subpool = svn_pool_create(pool);
599 apr_hash_t *ht = svn_xml_ap_to_hash(ap, subpool);
601 svn_xml_make_open_tag_hash(str, pool, style, tagname, ht);
602 svn_pool_destroy(subpool);
607 void
608 svn_xml_make_open_tag(svn_stringbuf_t **str,
609 apr_pool_t *pool,
610 enum svn_xml_open_tag_style style,
611 const char *tagname,
612 ...)
614 va_list ap;
616 va_start(ap, tagname);
617 svn_xml_make_open_tag_v(str, pool, style, tagname, ap);
618 va_end(ap);
622 void svn_xml_make_close_tag(svn_stringbuf_t **str,
623 apr_pool_t *pool,
624 const char *tagname)
626 if (*str == NULL)
627 *str = svn_stringbuf_create("", pool);
629 svn_stringbuf_appendcstr(*str, "</");
630 svn_stringbuf_appendcstr(*str, tagname);
631 svn_stringbuf_appendcstr(*str, ">\n");