1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 #include <stdio.h> 27 #include <unistd.h> 28 #include <sys/types.h> 29 #include <inttypes.h> 30 #include <assert.h> 31 #include <libxml/xmlreader.h> 32 #include <strings.h> 33 #include <ctype.h> 34 #include <stdlib.h> 35 #include <syslog.h> 36 #include <sys/stat.h> 37 38 #include "iscsitgt_impl.h" 39 40 /* 41 * Forward declarations 42 */ 43 static char *strip_space(char *value); 44 static tgt_node_t *node_alloc(); 45 static void node_free(tgt_node_t *x); 46 static Boolean_t node_name(tgt_node_t *x, const xmlChar *n); 47 static Boolean_t node_value(tgt_node_t *x, const xmlChar *n, Boolean_t s); 48 static tgt_node_t *node_parent(tgt_node_t *x); 49 static tgt_node_t *node_child(tgt_node_t *x); 50 static tgt_node_t *node_alloc_attr(tgt_node_t *x); 51 static void buf_add_node_attr(char **b, tgt_node_t *x); 52 static void buf_add_comment(char **b, char *comment); 53 static void buf_add_str(char **b, char *str); 54 55 #define XML_COMMENT_STR "!--" 56 #define XML_COMMENT_END "--" 57 58 void 59 tgt_node_free(tgt_node_t *n) 60 { 61 tgt_node_t *c; 62 tgt_node_t *c1; 63 64 if (n == NULL) 65 return; 66 for (c = n->x_child; c; ) { 67 c1 = c->x_sibling; 68 tgt_node_free(c); 69 c = c1; 70 } 71 for (c = n->x_attr; c; ) { 72 c1 = c->x_sibling; 73 node_free(c); 74 c = c1; 75 } 76 node_free(n); 77 78 } 79 80 /* 81 * tgt_dump2buf -- dumps node tree to buffer, allocating memory as it goes 82 * 83 * It is up to the caller, when finished with 'buf', to call free() 84 */ 85 void 86 tgt_dump2buf(tgt_node_t *n, char **buf) 87 { 88 tgt_node_t *c; 89 90 if (n == NULL) 91 return; 92 if (strcmp(n->x_name, XML_COMMENT_STR) == 0) { 93 buf_add_comment(buf, n->x_value); 94 return; 95 } 96 buf_add_node_attr(buf, n); 97 if (n->x_value != NULL) 98 tgt_buf_add_tag(buf, n->x_value, Tag_String); 99 for (c = n->x_child; c; c = c->x_sibling) 100 tgt_dump2buf(c, buf); 101 tgt_buf_add_tag(buf, n->x_name, Tag_End); 102 } 103 104 char *common_attr_list[] = { 105 XML_ELEMENT_NAME, 106 XML_ELEMENT_VERS, 107 XML_ELEMENT_INCORE, 108 0 109 }; 110 111 Boolean_t 112 tgt_node_process(xmlTextReaderPtr r, tgt_node_t **node) 113 { 114 const xmlChar *name; 115 const xmlChar *value; 116 char **ap; 117 xmlElementType node_type; 118 tgt_node_t *n; 119 tgt_node_t *an; 120 121 n = *node; 122 if (n == NULL) { 123 n = node_alloc(); 124 if (n == NULL) 125 return (False); 126 *node = n; 127 } 128 129 name = (xmlChar *)xmlTextReaderConstName(r); 130 if (name == NULL) { 131 node_free(n); 132 *node = NULL; 133 return (False); 134 } 135 136 node_type = (xmlElementType)xmlTextReaderNodeType(r); 137 138 value = (xmlChar *)xmlTextReaderConstValue(r); 139 140 if (node_type == XML_ELEMENT_NODE) { 141 if (n->x_state != NodeAlloc) { 142 n = node_child(n); 143 *node = n; 144 if (n == NULL) 145 return (False); 146 } 147 if (xmlTextReaderAttributeCount(r) > 0) { 148 149 for (ap = common_attr_list; *ap; ap++) { 150 value = xmlTextReaderGetAttribute(r, 151 (xmlChar *)*ap); 152 153 if (value != NULL) { 154 if ((an = node_alloc_attr(n)) == NULL) 155 return (False); 156 if (node_name(an, (xmlChar *)*ap) == 157 False) { 158 node_free(an); 159 return (False); 160 } 161 if (node_value(an, value, True) == 162 False) { 163 node_free(an); 164 return (False); 165 } 166 free((char *)value); 167 } 168 } 169 } 170 171 if (node_name(n, name) == False) { 172 node_free(n); 173 *node = NULL; 174 return (False); 175 } 176 } else if ((value != NULL) && (node_type == XML_TEXT_NODE)) { 177 if (node_value(n, value, True) == False) { 178 node_free(n); 179 *node = NULL; 180 return (False); 181 } 182 } else if (node_type == XML_ELEMENT_DECL) { 183 n = node_parent(n); 184 if (n == NULL) 185 return (False); 186 *node = n; 187 } else if (node_type == XML_COMMENT_NODE) { 188 n = node_child(n); 189 if (node_name(n, (xmlChar *)XML_COMMENT_STR) == False) { 190 node_free(n); 191 *node = NULL; 192 return (False); 193 } 194 if (node_value(n, (xmlChar *)value, False) == False) { 195 node_free(n); 196 *node = NULL; 197 return (False); 198 } 199 } else if (node_type != XML_DTD_NODE) { 200 node_free(n); 201 *node = NULL; 202 return (False); 203 } 204 return (True); 205 } 206 207 Boolean_t 208 tgt_find_attr_str(tgt_node_t *n, char *attr, char **value) 209 { 210 tgt_node_t *a; 211 212 if ((n == NULL) || (n->x_attr == NULL)) 213 return (False); 214 215 for (a = n->x_attr; a; a = a->x_sibling) 216 if (strcmp(a->x_name, attr) == 0) { 217 *value = a->x_value ? strdup(a->x_value) : NULL; 218 return (True); 219 } 220 return (False); 221 } 222 223 Boolean_t 224 tgt_find_value_str(tgt_node_t *n, char *name, char **value) 225 { 226 tgt_node_t *c; 227 228 if ((n == NULL) || (n->x_name == NULL)) 229 return (False); 230 231 if (strcmp(n->x_name, name) == 0) { 232 *value = n->x_value ? strdup(n->x_value) : NULL; 233 return (True); 234 } 235 for (c = n->x_child; c; c = c->x_sibling) { 236 if (tgt_find_value_str(c, name, value) == True) 237 return (True); 238 } 239 return (False); 240 } 241 242 Boolean_t 243 tgt_find_value_int(tgt_node_t *n, char *name, int *value) 244 { 245 tgt_node_t *c; 246 247 if ((n == NULL) || (n->x_name == NULL)) 248 return (False); 249 250 if (strcmp(n->x_name, name) == 0) { 251 if (n->x_value == NULL) 252 return (False); 253 *value = strtol(n->x_value, NULL, 0); 254 return (True); 255 } 256 for (c = n->x_child; c; c = c->x_sibling) { 257 if (tgt_find_value_int(c, name, value) == True) 258 return (True); 259 } 260 return (False); 261 } 262 263 /* 264 * []---- 265 * | xml_find_value_intchk -- if node exists, check to see if value is okay 266 * []---- 267 */ 268 Boolean_t 269 tgt_find_value_intchk(tgt_node_t *n, char *name, int *value) 270 { 271 char *str; 272 char chk[32]; 273 Boolean_t rval; 274 275 if (tgt_find_value_str(n, name, &str) == True) { 276 277 *value = strtol(str, NULL, 0); 278 /* 279 * Validate that the input string hasn't overrun what 280 * what an integer can handle. This is done by simply 281 * printing out the result of the conversion into a buffer 282 * and comparing it to the incoming string. That way when 283 * someone enters 4294967296 which strtol returns as 0 284 * we'll catch it. 285 */ 286 if ((str[0] == '0') && (str[1] != '\0')) { 287 if (str[1] == 'x') 288 (void) snprintf(chk, sizeof (chk), "0x%x", 289 *value); 290 else if (str[1] == 'X') 291 (void) snprintf(chk, sizeof (chk), "0X%x", 292 *value); 293 else 294 (void) snprintf(chk, sizeof (chk), "0%o", 295 *value); 296 } else 297 (void) snprintf(chk, sizeof (chk), "%d", *value); 298 if (strcmp(chk, str) == 0) 299 rval = True; 300 else 301 rval = False; 302 free(str); 303 return (rval); 304 } else 305 return (True); 306 } 307 308 Boolean_t 309 tgt_find_value_boolean(tgt_node_t *n, char *name, Boolean_t *value) 310 { 311 tgt_node_t *c; 312 313 if ((n == NULL) || (n->x_name == NULL)) 314 return (False); 315 316 if (strcmp(n->x_name, name) == 0) { 317 if (n->x_value == NULL) 318 return (False); 319 *value = strcmp(n->x_value, "true") == 0 ? True : False; 320 return (True); 321 } 322 for (c = n->x_child; c; c = c->x_sibling) { 323 if (tgt_find_value_boolean(c, name, value) == True) 324 return (True); 325 } 326 return (False); 327 } 328 329 tgt_node_t * 330 tgt_node_next(tgt_node_t *n, char *name, tgt_node_t *cur) 331 { 332 tgt_node_t *x; 333 tgt_node_t *p; 334 335 if (n == NULL) 336 return (NULL); 337 338 if (cur != NULL) { 339 for (x = cur->x_sibling; x; x = x->x_sibling) 340 if (strcmp(x->x_name, name) == 0) 341 return (x); 342 return (NULL); 343 } 344 345 if (n->x_name == NULL) 346 return (NULL); 347 348 if (strcmp(n->x_name, name) == 0) 349 return (n); 350 for (x = n->x_child; x; x = x->x_sibling) 351 if ((p = tgt_node_next(x, name, 0)) != NULL) 352 return (p); 353 return (NULL); 354 } 355 356 tgt_node_t * 357 tgt_node_next_child(tgt_node_t *n, char *name, tgt_node_t *cur) 358 { 359 if (cur != NULL) { 360 n = cur->x_sibling; 361 } else { 362 if (n != NULL) 363 n = n->x_child; 364 else 365 return (NULL); 366 } 367 while (n) { 368 if (strcmp(n->x_name, name) == 0) 369 return (n); 370 n = n->x_sibling; 371 } 372 return (NULL); 373 } 374 375 void 376 tgt_node_add(tgt_node_t *p, tgt_node_t *c) 377 { 378 if ((p == NULL) || (c == NULL)) 379 return; 380 381 c->x_parent = p; 382 if (p->x_child == NULL) 383 p->x_child = c; 384 else { 385 c->x_sibling = p->x_child; 386 p->x_child = c; 387 } 388 } 389 390 void 391 tgt_node_add_attr(tgt_node_t *p, tgt_node_t *a) 392 { 393 if ((p == NULL) || (a == NULL)) 394 return; 395 396 if (p->x_attr == NULL) 397 p->x_attr = a; 398 else { 399 a->x_sibling = p->x_attr; 400 p->x_attr = a; 401 } 402 } 403 404 tgt_node_t * 405 tgt_node_alloc(char *name, xml_val_type_t type, void *value) 406 { 407 tgt_node_t *d = node_alloc(); 408 int value_len = 0; 409 char *value_str = NULL; 410 411 if (d == NULL) 412 return (NULL); 413 switch (type) { 414 case String: 415 if (value) 416 value_len = strlen((char *)value) + 1; 417 break; 418 case Int: 419 value_len = sizeof (int) * 2 + 3; 420 break; 421 case Uint64: 422 value_len = sizeof (uint64_t) * 2 + 3; 423 break; 424 } 425 if (value_len && 426 (value_str = (char *)calloc(sizeof (char), value_len)) == NULL) 427 return (NULL); 428 if (node_name(d, (xmlChar *)name) == False) { 429 free(value_str); 430 return (NULL); 431 } 432 if (value_str) { 433 switch (type) { 434 case String: 435 (void) snprintf(value_str, value_len, "%s", 436 (char *)value); 437 break; 438 case Int: 439 (void) snprintf(value_str, value_len, "%d", 440 *(int *)value); 441 break; 442 case Uint64: 443 (void) snprintf(value_str, value_len, "0x%llx", 444 *(uint64_t *)value); 445 break; 446 } 447 } 448 (void) node_value(d, (xmlChar *)value_str, True); 449 free(value_str); 450 451 return (d); 452 } 453 454 Boolean_t 455 tgt_xml_encode(uint8_t *ip, size_t ip_size, char **buf, size_t *buf_size) 456 { 457 char *bp; 458 *buf_size = (ip_size * 2) + 1; 459 460 if ((*buf = (char *)malloc(*buf_size)) == NULL) { 461 *buf_size = 0; 462 return (False); 463 } 464 465 for (bp = *buf; ip_size; ip_size--) { 466 (void) sprintf(bp, "%.2x", *ip); 467 ip++; 468 bp += 2; 469 } 470 471 /* make it null terminated */ 472 *bp = 0; 473 474 return (True); 475 } 476 477 Boolean_t 478 tgt_xml_decode(char *buf, uint8_t **ip, size_t *ip_size) 479 { 480 uint8_t *i; 481 size_t buf_size = strlen(buf); 482 *ip_size = buf_size / 2; 483 484 if ((*ip = (uint8_t *)malloc(*ip_size)) == NULL) { 485 *ip_size = 0; 486 return (False); 487 } 488 489 for (i = *ip; buf_size; buf_size -= 2) { 490 char x[3]; 491 bcopy(buf, x, 2); 492 x[2] = 0; 493 *i++ = strtol(x, NULL, 16); 494 buf += 2; 495 } 496 return (True); 497 } 498 499 Boolean_t 500 tgt_node_remove(tgt_node_t *parent, tgt_node_t *child, match_type_t m) 501 { 502 tgt_node_t *s; 503 tgt_node_t *c = NULL; 504 505 if ((parent == NULL) || (child == NULL)) 506 return (False); 507 508 for (s = parent->x_child; s; c = s, s = s->x_sibling) { 509 510 /* 511 * See if the new child node matches one of the children 512 * in the parent. 513 */ 514 if ((strcmp(s->x_name, child->x_name) == 0) && 515 ((m == MatchName) || (strcmp(s->x_value, 516 child->x_value) == 0))) { 517 518 if (parent->x_child == s) { 519 parent->x_child = s->x_sibling; 520 } else { 521 c->x_sibling = s->x_sibling; 522 } 523 tgt_node_free(s); 524 break; 525 } 526 } 527 if (s == NULL) 528 return (False); 529 else 530 return (True); 531 } 532 533 void 534 tgt_node_replace(tgt_node_t *parent, tgt_node_t *child, match_type_t m) 535 { 536 tgt_node_t *s; 537 tgt_node_t *c; 538 539 if ((parent == NULL) || (child == NULL)) 540 return; 541 542 for (s = parent->x_child; s; s = s->x_sibling) { 543 544 /* 545 * See if the new child node matches one of the children 546 * in the parent. 547 */ 548 if ((strcmp(s->x_name, child->x_name) == 0) && 549 ((m == MatchName) || (strcmp(s->x_value, 550 child->x_value) == 0))) { 551 552 /* 553 * We have a match. Now save the values of the new 554 * child in this current node. 555 */ 556 free(s->x_name); 557 free(s->x_value); 558 s->x_name = strdup(child->x_name); 559 s->x_value = strdup(child->x_value); 560 if (s->x_child) { 561 tgt_node_free(s->x_child); 562 s->x_child = NULL; 563 } 564 for (c = child->x_child; c; c = c->x_sibling) 565 (void) tgt_node_add(s, tgt_node_dup(c)); 566 break; 567 } 568 } 569 570 if (s == NULL) { 571 /* 572 * Never found the child so add it 573 */ 574 (void) tgt_node_add(parent, tgt_node_dup(child)); 575 } 576 } 577 578 Boolean_t 579 tgt_update_value_str(tgt_node_t *node, char *name, char *str) 580 { 581 if ((node == NULL) || (strcmp(name, node->x_name) != 0)) 582 return (False); 583 if (node->x_value != NULL) 584 free(node->x_value); 585 node->x_value = strdup(str); 586 node->x_state = NodeValue; 587 return (True); 588 } 589 590 tgt_node_t * 591 tgt_node_find(tgt_node_t *n, char *name) 592 { 593 tgt_node_t *rval; 594 595 for (rval = n->x_child; rval; rval = rval->x_sibling) 596 if (strcmp(rval->x_name, name) == 0) 597 break; 598 return (rval); 599 } 600 601 tgt_node_t * 602 tgt_node_dup(tgt_node_t *n) 603 { 604 tgt_node_t *d = node_alloc(); 605 tgt_node_t *c; 606 607 if (d == NULL) 608 return (NULL); 609 if (node_name(d, (xmlChar *)n->x_name) == False) 610 return (NULL); 611 if (n->x_value && (node_value(d, (xmlChar *)n->x_value, True) == False)) 612 return (NULL); 613 for (c = n->x_child; c; c = c->x_sibling) 614 (void) tgt_node_add(d, tgt_node_dup(c)); 615 for (c = n->x_attr; c; c = c->x_sibling) 616 (void) tgt_node_add_attr(d, tgt_node_dup(c)); 617 return (d); 618 } 619 620 #define MAX_REPLACEMENT_ENTITY 8 621 #define MAX_REPLACEMENT_BUFFER 1024 622 void 623 tgt_buf_add(char **b, char *element, const char *cdata) 624 { 625 char entity[MAX_REPLACEMENT_ENTITY]; 626 char buf[MAX_REPLACEMENT_BUFFER]; 627 int len, i; 628 629 bzero(buf, sizeof (buf)); 630 631 tgt_buf_add_tag(b, element, Tag_Start); 632 /* 633 * we have to transform the predefined xml entities; 634 */ 635 if (cdata != NULL) { 636 len = strlen(cdata); 637 for (i = 0; i < len; i++) { 638 switch (cdata[i]) { 639 case '&': 640 (void) strcpy(entity, "&"); 641 break; 642 case '<': 643 (void) strcpy(entity, "<"); 644 break; 645 case '>': 646 (void) strcpy(entity, ">"); 647 break; 648 case '\'': 649 (void) strcpy(entity, "'"); 650 break; 651 case '"': 652 (void) strcpy(entity, """); 653 break; 654 default: 655 entity[0] = cdata[i]; 656 entity[1] = '\0'; 657 break; 658 } 659 (void) strlcat(buf, entity, sizeof (buf)); 660 } 661 tgt_buf_add_tag(b, buf, Tag_String); 662 } 663 tgt_buf_add_tag(b, element, Tag_End); 664 } 665 666 /* 667 * []---- 668 * | tgt_buf_add_tag -- adds string to buffer allocating space, sets up tags too 669 * | 670 * | Helper function to build a string by allocating memory as we go. 671 * | If the string argument 'str' is defined to be a start or end tag 672 * | as declared by 'type' argument add the appropriate characters. 673 * []---- 674 */ 675 void 676 tgt_buf_add_tag(char **b, const char *str, val_type_t type) 677 { 678 char *buf; 679 int len; 680 681 /* 682 * We will add potentially up to 3 extra characters plus the NULL byte 683 */ 684 len = strlen(str) + 4; 685 if ((buf = malloc(len)) == NULL) 686 return; 687 688 (void) snprintf(buf, len, "%s%s%s%s", type == Tag_String ? "" : "<", 689 type == Tag_End ? "/" : "", str, type == Tag_String ? "" : ">"); 690 buf_add_str(b, buf); 691 free(buf); 692 } 693 694 /* 695 * []---- 696 * | tgt_buf_add_tag_and_attr -- variant on tgt_buf_add_tag which also gives 697 * | attr 698 * []---- 699 */ 700 void 701 tgt_buf_add_tag_and_attr(char **b, char *str, char *attr) 702 { 703 char *buf; 704 int len; 705 706 /* 707 * In addition to the 'str' and 'attr' strings the code will add 708 * three characters plus a null byte. 709 */ 710 len = strlen(str) + strlen(attr) + 4; 711 if ((buf = malloc(len)) == NULL) 712 return; 713 714 (void) snprintf(buf, len, "<%s %s>", str, attr); 715 buf_add_str(b, buf); 716 free(buf); 717 } 718 719 /* 720 * []---- 721 * | Utility functions 722 * []---- 723 */ 724 static tgt_node_t * 725 node_alloc() 726 { 727 tgt_node_t *x = (tgt_node_t *)calloc(sizeof (tgt_node_t), 1); 728 729 if (x == NULL) 730 return (NULL); 731 732 x->x_state = NodeAlloc; 733 return (x); 734 } 735 736 static void 737 node_free(tgt_node_t *x) 738 { 739 x->x_state = NodeFree; 740 if (x->x_name) 741 free(x->x_name); 742 if (x->x_value) 743 free(x->x_value); 744 free(x); 745 } 746 747 static Boolean_t 748 node_name(tgt_node_t *x, const xmlChar *n) 749 { 750 assert(x->x_state == NodeAlloc); 751 if ((n == NULL) || (strlen((char *)n) == 0)) 752 return (False); 753 754 x->x_state = NodeName; 755 x->x_name = strip_space((char *)n); 756 return (True); 757 } 758 759 static Boolean_t 760 node_value(tgt_node_t *x, const xmlChar *n, Boolean_t do_strip) 761 { 762 assert(x->x_state == NodeName); 763 if ((n == NULL) || (strlen((char *)n) == NULL)) 764 return (False); 765 766 x->x_state = NodeValue; 767 x->x_value = (do_strip == True) ? 768 strip_space((char *)n) : strdup((char *)n); 769 return (True); 770 } 771 772 static tgt_node_t * 773 node_parent(tgt_node_t *x) 774 { 775 return (x->x_parent); 776 } 777 778 static tgt_node_t * 779 node_child(tgt_node_t *x) 780 { 781 tgt_node_t *n; 782 783 if ((n = node_alloc()) == NULL) 784 return (NULL); 785 786 if (x->x_child == NULL) { 787 x->x_child = n; 788 } else { 789 n->x_sibling = x->x_child; 790 x->x_child = n; 791 } 792 n->x_parent = x; 793 return (n); 794 } 795 796 static tgt_node_t * 797 node_alloc_attr(tgt_node_t *x) 798 { 799 tgt_node_t *n; 800 tgt_node_t *next; 801 802 n = node_alloc(); 803 if (x->x_attr == NULL) { 804 x->x_attr = n; 805 } else { 806 for (next = x->x_attr; next->x_sibling; next = next->x_sibling) 807 ; 808 next->x_sibling = n; 809 } 810 if (n != NULL) 811 n->x_parent = x; 812 return (n); 813 } 814 815 static void 816 buf_add_str(char **b, char *str) 817 { 818 int len; 819 int olen = 0; 820 char *p = *b; 821 822 /* 823 * Make sure we have enough room for the string and tag characters 824 * plus a NULL byte. 825 */ 826 if (str == NULL) 827 return; 828 829 len = strlen(str) + 1; 830 if (p == NULL) { 831 if ((p = malloc(len)) == NULL) 832 return; 833 } else { 834 olen = strlen(p); 835 p = realloc(p, olen + len); 836 } 837 (void) strncpy(p + olen, str, len); 838 *b = p; 839 } 840 841 static void 842 buf_add_node_attr(char **b, tgt_node_t *x) 843 { 844 char *buf; 845 tgt_node_t *n; 846 int len; 847 848 /* ---- null byte and starting '<' character ---- */ 849 len = strlen(x->x_name) + 2; 850 if ((buf = malloc(len)) == NULL) 851 return; 852 (void) snprintf(buf, len, "<%s", x->x_name); 853 buf_add_str(b, buf); 854 free(buf); 855 856 for (n = x->x_attr; n; n = n->x_sibling) { 857 len = strlen(n->x_name) + strlen(n->x_value) + 5; 858 if ((buf = malloc(len)) == NULL) 859 return; 860 (void) snprintf(buf, len, " %s='%s'", n->x_name, n->x_value); 861 buf_add_str(b, buf); 862 free(buf); 863 } 864 buf_add_str(b, ">"); 865 } 866 867 static void 868 buf_add_comment(char **b, char *comment) 869 { 870 char *p = *b; 871 int len; 872 int olen; 873 874 if (comment == NULL) 875 return; 876 877 /* 878 * Room for the strings, plus the brackets and NULL byte 879 */ 880 len = strlen(comment) + strlen(XML_COMMENT_STR) + 881 strlen(XML_COMMENT_END) + 3; 882 883 if (p == NULL) 884 p = malloc(len); 885 else { 886 olen = strlen(p); 887 p = realloc(p, olen + len); 888 } 889 (void) snprintf(p + olen, len, "<%s%s%s>", XML_COMMENT_STR, comment, 890 XML_COMMENT_END); 891 *b = p; 892 } 893 894 static char * 895 strip_space(char *value) 896 { 897 char *p; 898 char *n; 899 900 for (p = value; p && *p; p++) 901 if (!isspace(*p)) 902 break; 903 if ((p == NULL) || (*p == '\0')) 904 return (NULL); 905 906 p = strdup(p); 907 for (n = (p + strlen(p) - 1); n >= p; n--) 908 if (!isspace(*n)) { 909 n++; 910 break; 911 } 912 *n = '\0'; 913 return (p); 914 } 915