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, "&"); 176251881Speter else if (*q == '<') 177251881Speter svn_stringbuf_appendcstr(*outstr, "<"); 178251881Speter else if (*q == '>') 179251881Speter svn_stringbuf_appendcstr(*outstr, ">"); 180251881Speter else if (*q == '\r') 181251881Speter svn_stringbuf_appendcstr(*outstr, " "); 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, "&"); 219251881Speter else if (*q == '<') 220251881Speter svn_stringbuf_appendcstr(*outstr, "<"); 221251881Speter else if (*q == '>') 222251881Speter svn_stringbuf_appendcstr(*outstr, ">"); 223251881Speter else if (*q == '"') 224251881Speter svn_stringbuf_appendcstr(*outstr, """); 225251881Speter else if (*q == '\'') 226251881Speter svn_stringbuf_appendcstr(*outstr, "'"); 227251881Speter else if (*q == '\r') 228251881Speter svn_stringbuf_appendcstr(*outstr, " "); 229251881Speter else if (*q == '\n') 230251881Speter svn_stringbuf_appendcstr(*outstr, " "); 231251881Speter else if (*q == '\t') 232251881Speter svn_stringbuf_appendcstr(*outstr, "	"); 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