1251881Speter/*
2251881Speter * xml.c:  xml helper code shared among the Subversion libraries.
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <string.h>
27251881Speter#include <assert.h>
28251881Speter
29251881Speter#include "svn_private_config.h"         /* for SVN_HAVE_OLD_EXPAT */
30251881Speter#include "svn_hash.h"
31251881Speter#include "svn_pools.h"
32251881Speter#include "svn_xml.h"
33251881Speter#include "svn_error.h"
34251881Speter#include "svn_ctype.h"
35251881Speter
36251881Speter#include "private/svn_utf_private.h"
37299742Sdim#include "private/svn_subr_private.h"
38251881Speter
39251881Speter#ifdef SVN_HAVE_OLD_EXPAT
40251881Speter#include <xmlparse.h>
41251881Speter#else
42251881Speter#include <expat.h>
43251881Speter#endif
44251881Speter
45251881Speter#ifdef XML_UNICODE
46251881Speter#error Expat is unusable -- it has been compiled for wide characters
47251881Speter#endif
48251881Speter
49309512Speter#ifndef XML_VERSION_AT_LEAST
50309512Speter#define XML_VERSION_AT_LEAST(major,minor,patch)                  \
51309512Speter(((major) < XML_MAJOR_VERSION)                                       \
52309512Speter || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION)    \
53309512Speter || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \
54309512Speter     (patch) <= XML_MICRO_VERSION))
55309512Speter#endif /* XML_VERSION_AT_LEAST */
56309512Speter
57299742Sdimconst char *
58299742Sdimsvn_xml__compiled_version(void)
59299742Sdim{
60299742Sdim  static const char xml_version_str[] = APR_STRINGIFY(XML_MAJOR_VERSION)
61299742Sdim                                        "." APR_STRINGIFY(XML_MINOR_VERSION)
62299742Sdim                                        "." APR_STRINGIFY(XML_MICRO_VERSION);
63299742Sdim
64299742Sdim  return xml_version_str;
65299742Sdim}
66299742Sdim
67299742Sdimconst char *
68299742Sdimsvn_xml__runtime_version(void)
69299742Sdim{
70299742Sdim  const char *expat_version = XML_ExpatVersion();
71299742Sdim
72299742Sdim  if (!strncmp(expat_version, "expat_", 6))
73299742Sdim    expat_version += 6;
74299742Sdim
75299742Sdim  return expat_version;
76299742Sdim}
77299742Sdim
78299742Sdim
79251881Speter/* The private internals for a parser object. */
80251881Speterstruct svn_xml_parser_t
81251881Speter{
82251881Speter  /** the expat parser */
83251881Speter  XML_Parser parser;
84251881Speter
85251881Speter  /** the SVN callbacks to call from the Expat callbacks */
86251881Speter  svn_xml_start_elem start_handler;
87251881Speter  svn_xml_end_elem end_handler;
88251881Speter  svn_xml_char_data data_handler;
89251881Speter
90251881Speter  /** the user's baton for private data */
91251881Speter  void *baton;
92251881Speter
93251881Speter  /** if non-@c NULL, an error happened while parsing */
94251881Speter  svn_error_t *error;
95251881Speter
96251881Speter  /** where this object is allocated, so we can free it easily */
97251881Speter  apr_pool_t *pool;
98251881Speter
99251881Speter};
100251881Speter
101251881Speter
102251881Speter/*** XML character validation ***/
103251881Speter
104251881Spetersvn_boolean_t
105251881Spetersvn_xml_is_xml_safe(const char *data, apr_size_t len)
106251881Speter{
107251881Speter  const char *end = data + len;
108251881Speter  const char *p;
109251881Speter
110251881Speter  if (! svn_utf__is_valid(data, len))
111251881Speter    return FALSE;
112251881Speter
113251881Speter  for (p = data; p < end; p++)
114251881Speter    {
115251881Speter      unsigned char c = *p;
116251881Speter
117251881Speter      if (svn_ctype_iscntrl(c))
118251881Speter        {
119251881Speter          if ((c != SVN_CTYPE_ASCII_TAB)
120251881Speter              && (c != SVN_CTYPE_ASCII_LINEFEED)
121251881Speter              && (c != SVN_CTYPE_ASCII_CARRIAGERETURN)
122251881Speter              && (c != SVN_CTYPE_ASCII_DELETE))
123251881Speter            return FALSE;
124251881Speter        }
125251881Speter    }
126251881Speter  return TRUE;
127251881Speter}
128251881Speter
129251881Speter
130251881Speter
131251881Speter
132251881Speter
133251881Speter/*** XML escaping. ***/
134251881Speter
135251881Speter/* ### ...?
136251881Speter *
137251881Speter * If *OUTSTR is @c NULL, set *OUTSTR to a new stringbuf allocated
138251881Speter * in POOL, else append to the existing stringbuf there.
139251881Speter */
140251881Speterstatic void
141251881Speterxml_escape_cdata(svn_stringbuf_t **outstr,
142251881Speter                 const char *data,
143251881Speter                 apr_size_t len,
144251881Speter                 apr_pool_t *pool)
145251881Speter{
146251881Speter  const char *end = data + len;
147251881Speter  const char *p = data, *q;
148251881Speter
149251881Speter  if (*outstr == NULL)
150251881Speter    *outstr = svn_stringbuf_create_empty(pool);
151251881Speter
152251881Speter  while (1)
153251881Speter    {
154251881Speter      /* Find a character which needs to be quoted and append bytes up
155251881Speter         to that point.  Strictly speaking, '>' only needs to be
156251881Speter         quoted if it follows "]]", but it's easier to quote it all
157251881Speter         the time.
158251881Speter
159251881Speter         So, why are we escaping '\r' here?  Well, according to the
160251881Speter         XML spec, '\r\n' gets converted to '\n' during XML parsing.
161251881Speter         Also, any '\r' not followed by '\n' is converted to '\n'.  By
162251881Speter         golly, if we say we want to escape a '\r', we want to make
163251881Speter         sure it remains a '\r'!  */
164251881Speter      q = p;
165251881Speter      while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '\r')
166251881Speter        q++;
167251881Speter      svn_stringbuf_appendbytes(*outstr, p, q - p);
168251881Speter
169251881Speter      /* We may already be a winner.  */
170251881Speter      if (q == end)
171251881Speter        break;
172251881Speter
173251881Speter      /* Append the entity reference for the character.  */
174251881Speter      if (*q == '&')
175251881Speter        svn_stringbuf_appendcstr(*outstr, "&amp;");
176251881Speter      else if (*q == '<')
177251881Speter        svn_stringbuf_appendcstr(*outstr, "&lt;");
178251881Speter      else if (*q == '>')
179251881Speter        svn_stringbuf_appendcstr(*outstr, "&gt;");
180251881Speter      else if (*q == '\r')
181251881Speter        svn_stringbuf_appendcstr(*outstr, "&#13;");
182251881Speter
183251881Speter      p = q + 1;
184251881Speter    }
185251881Speter}
186251881Speter
187251881Speter/* Essentially the same as xml_escape_cdata, with the addition of
188251881Speter   whitespace and quote characters. */
189251881Speterstatic void
190251881Speterxml_escape_attr(svn_stringbuf_t **outstr,
191251881Speter                const char *data,
192251881Speter                apr_size_t len,
193251881Speter                apr_pool_t *pool)
194251881Speter{
195251881Speter  const char *end = data + len;
196251881Speter  const char *p = data, *q;
197251881Speter
198251881Speter  if (*outstr == NULL)
199251881Speter    *outstr = svn_stringbuf_create_ensure(len, pool);
200251881Speter
201251881Speter  while (1)
202251881Speter    {
203251881Speter      /* Find a character which needs to be quoted and append bytes up
204251881Speter         to that point. */
205251881Speter      q = p;
206251881Speter      while (q < end && *q != '&' && *q != '<' && *q != '>'
207251881Speter             && *q != '"' && *q != '\'' && *q != '\r'
208251881Speter             && *q != '\n' && *q != '\t')
209251881Speter        q++;
210251881Speter      svn_stringbuf_appendbytes(*outstr, p, q - p);
211251881Speter
212251881Speter      /* We may already be a winner.  */
213251881Speter      if (q == end)
214251881Speter        break;
215251881Speter
216251881Speter      /* Append the entity reference for the character.  */
217251881Speter      if (*q == '&')
218251881Speter        svn_stringbuf_appendcstr(*outstr, "&amp;");
219251881Speter      else if (*q == '<')
220251881Speter        svn_stringbuf_appendcstr(*outstr, "&lt;");
221251881Speter      else if (*q == '>')
222251881Speter        svn_stringbuf_appendcstr(*outstr, "&gt;");
223251881Speter      else if (*q == '"')
224251881Speter        svn_stringbuf_appendcstr(*outstr, "&quot;");
225251881Speter      else if (*q == '\'')
226251881Speter        svn_stringbuf_appendcstr(*outstr, "&apos;");
227251881Speter      else if (*q == '\r')
228251881Speter        svn_stringbuf_appendcstr(*outstr, "&#13;");
229251881Speter      else if (*q == '\n')
230251881Speter        svn_stringbuf_appendcstr(*outstr, "&#10;");
231251881Speter      else if (*q == '\t')
232251881Speter        svn_stringbuf_appendcstr(*outstr, "&#9;");
233251881Speter
234251881Speter      p = q + 1;
235251881Speter    }
236251881Speter}
237251881Speter
238251881Speter
239251881Spetervoid
240251881Spetersvn_xml_escape_cdata_stringbuf(svn_stringbuf_t **outstr,
241251881Speter                               const svn_stringbuf_t *string,
242251881Speter                               apr_pool_t *pool)
243251881Speter{
244251881Speter  xml_escape_cdata(outstr, string->data, string->len, pool);
245251881Speter}
246251881Speter
247251881Speter
248251881Spetervoid
249251881Spetersvn_xml_escape_cdata_string(svn_stringbuf_t **outstr,
250251881Speter                            const svn_string_t *string,
251251881Speter                            apr_pool_t *pool)
252251881Speter{
253251881Speter  xml_escape_cdata(outstr, string->data, string->len, pool);
254251881Speter}
255251881Speter
256251881Speter
257251881Spetervoid
258251881Spetersvn_xml_escape_cdata_cstring(svn_stringbuf_t **outstr,
259251881Speter                             const char *string,
260251881Speter                             apr_pool_t *pool)
261251881Speter{
262251881Speter  xml_escape_cdata(outstr, string, (apr_size_t) strlen(string), pool);
263251881Speter}
264251881Speter
265251881Speter
266251881Spetervoid
267251881Spetersvn_xml_escape_attr_stringbuf(svn_stringbuf_t **outstr,
268251881Speter                              const svn_stringbuf_t *string,
269251881Speter                              apr_pool_t *pool)
270251881Speter{
271251881Speter  xml_escape_attr(outstr, string->data, string->len, pool);
272251881Speter}
273251881Speter
274251881Speter
275251881Spetervoid
276251881Spetersvn_xml_escape_attr_string(svn_stringbuf_t **outstr,
277251881Speter                           const svn_string_t *string,
278251881Speter                           apr_pool_t *pool)
279251881Speter{
280251881Speter  xml_escape_attr(outstr, string->data, string->len, pool);
281251881Speter}
282251881Speter
283251881Speter
284251881Spetervoid
285251881Spetersvn_xml_escape_attr_cstring(svn_stringbuf_t **outstr,
286251881Speter                            const char *string,
287251881Speter                            apr_pool_t *pool)
288251881Speter{
289251881Speter  xml_escape_attr(outstr, string, (apr_size_t) strlen(string), pool);
290251881Speter}
291251881Speter
292251881Speter
293251881Speterconst char *
294251881Spetersvn_xml_fuzzy_escape(const char *string, apr_pool_t *pool)
295251881Speter{
296251881Speter  const char *end = string + strlen(string);
297251881Speter  const char *p = string, *q;
298251881Speter  svn_stringbuf_t *outstr;
299251881Speter  char escaped_char[6];   /* ? \ u u u \0 */
300251881Speter
301251881Speter  for (q = p; q < end; q++)
302251881Speter    {
303251881Speter      if (svn_ctype_iscntrl(*q)
304251881Speter          && ! ((*q == '\n') || (*q == '\r') || (*q == '\t')))
305251881Speter        break;
306251881Speter    }
307251881Speter
308251881Speter  /* Return original string if no unsafe characters found. */
309251881Speter  if (q == end)
310251881Speter    return string;
311251881Speter
312251881Speter  outstr = svn_stringbuf_create_empty(pool);
313251881Speter  while (1)
314251881Speter    {
315251881Speter      q = p;
316251881Speter
317251881Speter      /* Traverse till either unsafe character or eos. */
318251881Speter      while ((q < end)
319251881Speter             && ((! svn_ctype_iscntrl(*q))
320251881Speter                 || (*q == '\n') || (*q == '\r') || (*q == '\t')))
321251881Speter        q++;
322251881Speter
323251881Speter      /* copy chunk before marker */
324251881Speter      svn_stringbuf_appendbytes(outstr, p, q - p);
325251881Speter
326251881Speter      if (q == end)
327251881Speter        break;
328251881Speter
329251881Speter      /* Append an escaped version of the unsafe character.
330251881Speter
331251881Speter         ### This format was chosen for consistency with
332251881Speter         ### svn_utf__cstring_from_utf8_fuzzy().  The two functions
333251881Speter         ### should probably share code, even though they escape
334251881Speter         ### different characters.
335251881Speter      */
336251881Speter      apr_snprintf(escaped_char, sizeof(escaped_char), "?\\%03u",
337251881Speter                   (unsigned char) *q);
338251881Speter      svn_stringbuf_appendcstr(outstr, escaped_char);
339251881Speter
340251881Speter      p = q + 1;
341251881Speter    }
342251881Speter
343251881Speter  return outstr->data;
344251881Speter}
345251881Speter
346251881Speter
347251881Speter/*** Map from the Expat callback types to the SVN XML types. ***/
348251881Speter
349251881Speterstatic void expat_start_handler(void *userData,
350251881Speter                                const XML_Char *name,
351251881Speter                                const XML_Char **atts)
352251881Speter{
353251881Speter  svn_xml_parser_t *svn_parser = userData;
354251881Speter
355251881Speter  (*svn_parser->start_handler)(svn_parser->baton, name, atts);
356251881Speter}
357251881Speter
358251881Speterstatic void expat_end_handler(void *userData, const XML_Char *name)
359251881Speter{
360251881Speter  svn_xml_parser_t *svn_parser = userData;
361251881Speter
362251881Speter  (*svn_parser->end_handler)(svn_parser->baton, name);
363251881Speter}
364251881Speter
365251881Speterstatic void expat_data_handler(void *userData, const XML_Char *s, int len)
366251881Speter{
367251881Speter  svn_xml_parser_t *svn_parser = userData;
368251881Speter
369251881Speter  (*svn_parser->data_handler)(svn_parser->baton, s, (apr_size_t)len);
370251881Speter}
371251881Speter
372309512Speter#if XML_VERSION_AT_LEAST(1, 95, 8)
373309512Speterstatic void expat_entity_declaration(void *userData,
374309512Speter                                     const XML_Char *entityName,
375309512Speter                                     int is_parameter_entity,
376309512Speter                                     const XML_Char *value,
377309512Speter                                     int value_length,
378309512Speter                                     const XML_Char *base,
379309512Speter                                     const XML_Char *systemId,
380309512Speter                                     const XML_Char *publicId,
381309512Speter                                     const XML_Char *notationName)
382309512Speter{
383309512Speter  svn_xml_parser_t *svn_parser = userData;
384309512Speter
385309512Speter  /* Stop the parser if an entity declaration is hit. */
386309512Speter  XML_StopParser(svn_parser->parser, 0 /* resumable */);
387309512Speter}
388309512Speter#else
389309512Speter/* A noop default_handler. */
390309512Speterstatic void expat_default_handler(void *userData, const XML_Char *s, int len)
391309512Speter{
392309512Speter}
393309512Speter#endif
394251881Speter
395251881Speter/*** Making a parser. ***/
396251881Speter
397251881Spetersvn_xml_parser_t *
398251881Spetersvn_xml_make_parser(void *baton,
399251881Speter                    svn_xml_start_elem start_handler,
400251881Speter                    svn_xml_end_elem end_handler,
401251881Speter                    svn_xml_char_data data_handler,
402251881Speter                    apr_pool_t *pool)
403251881Speter{
404251881Speter  svn_xml_parser_t *svn_parser;
405251881Speter  apr_pool_t *subpool;
406251881Speter
407251881Speter  XML_Parser parser = XML_ParserCreate(NULL);
408251881Speter
409251881Speter  XML_SetElementHandler(parser,
410251881Speter                        start_handler ? expat_start_handler : NULL,
411251881Speter                        end_handler ? expat_end_handler : NULL);
412251881Speter  XML_SetCharacterDataHandler(parser,
413251881Speter                              data_handler ? expat_data_handler : NULL);
414251881Speter
415309512Speter#if XML_VERSION_AT_LEAST(1, 95, 8)
416309512Speter  XML_SetEntityDeclHandler(parser, expat_entity_declaration);
417309512Speter#else
418309512Speter  XML_SetDefaultHandler(parser, expat_default_handler);
419309512Speter#endif
420309512Speter
421251881Speter  /* ### we probably don't want this pool; or at least we should pass it
422251881Speter     ### to the callbacks and clear it periodically.  */
423251881Speter  subpool = svn_pool_create(pool);
424251881Speter
425251881Speter  svn_parser = apr_pcalloc(subpool, sizeof(*svn_parser));
426251881Speter
427251881Speter  svn_parser->parser = parser;
428251881Speter  svn_parser->start_handler = start_handler;
429251881Speter  svn_parser->end_handler = end_handler;
430251881Speter  svn_parser->data_handler = data_handler;
431251881Speter  svn_parser->baton = baton;
432251881Speter  svn_parser->pool = subpool;
433251881Speter
434251881Speter  /* store our parser info as the UserData in the Expat parser */
435251881Speter  XML_SetUserData(parser, svn_parser);
436251881Speter
437251881Speter  return svn_parser;
438251881Speter}
439251881Speter
440251881Speter
441251881Speter/* Free a parser */
442251881Spetervoid
443251881Spetersvn_xml_free_parser(svn_xml_parser_t *svn_parser)
444251881Speter{
445251881Speter  /* Free the expat parser */
446251881Speter  XML_ParserFree(svn_parser->parser);
447251881Speter
448251881Speter  /* Free the subversion parser */
449251881Speter  svn_pool_destroy(svn_parser->pool);
450251881Speter}
451251881Speter
452251881Speter
453251881Speter
454251881Speter
455251881Spetersvn_error_t *
456251881Spetersvn_xml_parse(svn_xml_parser_t *svn_parser,
457251881Speter              const char *buf,
458251881Speter              apr_size_t len,
459251881Speter              svn_boolean_t is_final)
460251881Speter{
461251881Speter  svn_error_t *err;
462251881Speter  int success;
463251881Speter
464251881Speter  /* Parse some xml data */
465251881Speter  success = XML_Parse(svn_parser->parser, buf, (int) len, is_final);
466251881Speter
467251881Speter  /* If expat choked internally, return its error. */
468251881Speter  if (! success)
469251881Speter    {
470251881Speter      /* Line num is "int" in Expat v1, "long" in v2; hide the difference. */
471251881Speter      long line = XML_GetCurrentLineNumber(svn_parser->parser);
472251881Speter
473251881Speter      err = svn_error_createf
474251881Speter        (SVN_ERR_XML_MALFORMED, NULL,
475251881Speter         _("Malformed XML: %s at line %ld"),
476251881Speter         XML_ErrorString(XML_GetErrorCode(svn_parser->parser)), line);
477251881Speter
478251881Speter      /* Kill all parsers and return the expat error */
479251881Speter      svn_xml_free_parser(svn_parser);
480251881Speter      return err;
481251881Speter    }
482251881Speter
483251881Speter  /* Did an error occur somewhere *inside* the expat callbacks? */
484251881Speter  if (svn_parser->error)
485251881Speter    {
486251881Speter      err = svn_parser->error;
487251881Speter      svn_xml_free_parser(svn_parser);
488251881Speter      return err;
489251881Speter    }
490251881Speter
491251881Speter  return SVN_NO_ERROR;
492251881Speter}
493251881Speter
494251881Speter
495251881Speter
496251881Spetervoid svn_xml_signal_bailout(svn_error_t *error,
497251881Speter                            svn_xml_parser_t *svn_parser)
498251881Speter{
499251881Speter  /* This will cause the current XML_Parse() call to finish quickly! */
500251881Speter  XML_SetElementHandler(svn_parser->parser, NULL, NULL);
501251881Speter  XML_SetCharacterDataHandler(svn_parser->parser, NULL);
502309512Speter#if XML_VERSION_AT_LEAST(1, 95, 8)
503309512Speter  XML_SetEntityDeclHandler(svn_parser->parser, NULL);
504309512Speter#endif
505251881Speter
506251881Speter  /* Once outside of XML_Parse(), the existence of this field will
507251881Speter     cause svn_delta_parse()'s main read-loop to return error. */
508251881Speter  svn_parser->error = error;
509251881Speter}
510251881Speter
511251881Speter
512251881Speter
513251881Speter
514251881Speter
515251881Speter
516251881Speter
517251881Speter
518251881Speter/*** Attribute walking. ***/
519251881Speter
520251881Speterconst char *
521251881Spetersvn_xml_get_attr_value(const char *name, const char *const *atts)
522251881Speter{
523251881Speter  while (atts && (*atts))
524251881Speter    {
525251881Speter      if (strcmp(atts[0], name) == 0)
526251881Speter        return atts[1];
527251881Speter      else
528251881Speter        atts += 2; /* continue looping */
529251881Speter    }
530251881Speter
531251881Speter  /* Else no such attribute name seen. */
532251881Speter  return NULL;
533251881Speter}
534251881Speter
535251881Speter
536251881Speter
537251881Speter/*** Printing XML ***/
538251881Speter
539251881Spetervoid
540251881Spetersvn_xml_make_header2(svn_stringbuf_t **str, const char *encoding,
541251881Speter                     apr_pool_t *pool)
542251881Speter{
543251881Speter
544251881Speter  if (*str == NULL)
545251881Speter    *str = svn_stringbuf_create_empty(pool);
546251881Speter  svn_stringbuf_appendcstr(*str, "<?xml version=\"1.0\"");
547251881Speter  if (encoding)
548251881Speter    {
549251881Speter      encoding = apr_psprintf(pool, " encoding=\"%s\"", encoding);
550251881Speter      svn_stringbuf_appendcstr(*str, encoding);
551251881Speter    }
552251881Speter  svn_stringbuf_appendcstr(*str, "?>\n");
553251881Speter}
554251881Speter
555251881Speter
556251881Speter
557251881Speter/*** Creating attribute hashes. ***/
558251881Speter
559251881Speter/* Combine an existing attribute list ATTS with a HASH that itself
560251881Speter   represents an attribute list.  Iff PRESERVE is true, then no value
561251881Speter   already in HASH will be changed, else values from ATTS will
562251881Speter   override previous values in HASH. */
563251881Speterstatic void
564251881Speteramalgamate(const char **atts,
565251881Speter           apr_hash_t *ht,
566251881Speter           svn_boolean_t preserve,
567251881Speter           apr_pool_t *pool)
568251881Speter{
569251881Speter  const char *key;
570251881Speter
571251881Speter  if (atts)
572251881Speter    for (key = *atts; key; key = *(++atts))
573251881Speter      {
574251881Speter        const char *val = *(++atts);
575251881Speter        size_t keylen;
576251881Speter        assert(key != NULL);
577251881Speter        /* kff todo: should we also insist that val be non-null here?
578251881Speter           Probably. */
579251881Speter
580251881Speter        keylen = strlen(key);
581251881Speter        if (preserve && ((apr_hash_get(ht, key, keylen)) != NULL))
582251881Speter          continue;
583251881Speter        else
584251881Speter          apr_hash_set(ht, apr_pstrndup(pool, key, keylen), keylen,
585251881Speter                       val ? apr_pstrdup(pool, val) : NULL);
586251881Speter      }
587251881Speter}
588251881Speter
589251881Speter
590251881Speterapr_hash_t *
591251881Spetersvn_xml_ap_to_hash(va_list ap, apr_pool_t *pool)
592251881Speter{
593251881Speter  apr_hash_t *ht = apr_hash_make(pool);
594251881Speter  const char *key;
595251881Speter
596251881Speter  while ((key = va_arg(ap, char *)) != NULL)
597251881Speter    {
598251881Speter      const char *val = va_arg(ap, const char *);
599251881Speter      svn_hash_sets(ht, key, val);
600251881Speter    }
601251881Speter
602251881Speter  return ht;
603251881Speter}
604251881Speter
605251881Speter
606251881Speterapr_hash_t *
607251881Spetersvn_xml_make_att_hash(const char **atts, apr_pool_t *pool)
608251881Speter{
609251881Speter  apr_hash_t *ht = apr_hash_make(pool);
610251881Speter  amalgamate(atts, ht, 0, pool);  /* third arg irrelevant in this case */
611251881Speter  return ht;
612251881Speter}
613251881Speter
614251881Speter
615251881Spetervoid
616251881Spetersvn_xml_hash_atts_overlaying(const char **atts,
617251881Speter                             apr_hash_t *ht,
618251881Speter                             apr_pool_t *pool)
619251881Speter{
620251881Speter  amalgamate(atts, ht, 0, pool);
621251881Speter}
622251881Speter
623251881Speter
624251881Spetervoid
625251881Spetersvn_xml_hash_atts_preserving(const char **atts,
626251881Speter                             apr_hash_t *ht,
627251881Speter                             apr_pool_t *pool)
628251881Speter{
629251881Speter  amalgamate(atts, ht, 1, pool);
630251881Speter}
631251881Speter
632251881Speter
633251881Speter
634251881Speter/*** Making XML tags. ***/
635251881Speter
636251881Speter
637251881Spetervoid
638251881Spetersvn_xml_make_open_tag_hash(svn_stringbuf_t **str,
639251881Speter                           apr_pool_t *pool,
640251881Speter                           enum svn_xml_open_tag_style style,
641251881Speter                           const char *tagname,
642251881Speter                           apr_hash_t *attributes)
643251881Speter{
644251881Speter  apr_hash_index_t *hi;
645251881Speter  apr_size_t est_size = strlen(tagname) + 4 + apr_hash_count(attributes) * 30;
646251881Speter
647251881Speter  if (*str == NULL)
648251881Speter    *str = svn_stringbuf_create_ensure(est_size, pool);
649251881Speter
650251881Speter  svn_stringbuf_appendcstr(*str, "<");
651251881Speter  svn_stringbuf_appendcstr(*str, tagname);
652251881Speter
653251881Speter  for (hi = apr_hash_first(pool, attributes); hi; hi = apr_hash_next(hi))
654251881Speter    {
655251881Speter      const void *key;
656251881Speter      void *val;
657251881Speter
658251881Speter      apr_hash_this(hi, &key, NULL, &val);
659251881Speter      assert(val != NULL);
660251881Speter
661251881Speter      svn_stringbuf_appendcstr(*str, "\n   ");
662251881Speter      svn_stringbuf_appendcstr(*str, key);
663251881Speter      svn_stringbuf_appendcstr(*str, "=\"");
664251881Speter      svn_xml_escape_attr_cstring(str, val, pool);
665251881Speter      svn_stringbuf_appendcstr(*str, "\"");
666251881Speter    }
667251881Speter
668251881Speter  if (style == svn_xml_self_closing)
669251881Speter    svn_stringbuf_appendcstr(*str, "/");
670251881Speter  svn_stringbuf_appendcstr(*str, ">");
671251881Speter  if (style != svn_xml_protect_pcdata)
672251881Speter    svn_stringbuf_appendcstr(*str, "\n");
673251881Speter}
674251881Speter
675251881Speter
676251881Spetervoid
677251881Spetersvn_xml_make_open_tag_v(svn_stringbuf_t **str,
678251881Speter                        apr_pool_t *pool,
679251881Speter                        enum svn_xml_open_tag_style style,
680251881Speter                        const char *tagname,
681251881Speter                        va_list ap)
682251881Speter{
683251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
684251881Speter  apr_hash_t *ht = svn_xml_ap_to_hash(ap, subpool);
685251881Speter
686251881Speter  svn_xml_make_open_tag_hash(str, pool, style, tagname, ht);
687251881Speter  svn_pool_destroy(subpool);
688251881Speter}
689251881Speter
690251881Speter
691251881Speter
692251881Spetervoid
693251881Spetersvn_xml_make_open_tag(svn_stringbuf_t **str,
694251881Speter                      apr_pool_t *pool,
695251881Speter                      enum svn_xml_open_tag_style style,
696251881Speter                      const char *tagname,
697251881Speter                      ...)
698251881Speter{
699251881Speter  va_list ap;
700251881Speter
701251881Speter  va_start(ap, tagname);
702251881Speter  svn_xml_make_open_tag_v(str, pool, style, tagname, ap);
703251881Speter  va_end(ap);
704251881Speter}
705251881Speter
706251881Speter
707251881Spetervoid svn_xml_make_close_tag(svn_stringbuf_t **str,
708251881Speter                            apr_pool_t *pool,
709251881Speter                            const char *tagname)
710251881Speter{
711251881Speter  if (*str == NULL)
712251881Speter    *str = svn_stringbuf_create_empty(pool);
713251881Speter
714251881Speter  svn_stringbuf_appendcstr(*str, "</");
715251881Speter  svn_stringbuf_appendcstr(*str, tagname);
716251881Speter  svn_stringbuf_appendcstr(*str, ">\n");
717251881Speter}
718