1 /* 2 * profile.c -- A simple configuration file parsing "library in a file" 3 * 4 * The profile library was originally written by Theodore Ts'o in 1995 5 * for use in the MIT Kerberos v5 library. It has been 6 * modified/enhanced/bug-fixed over time by other members of the MIT 7 * Kerberos team. This version was originally taken from the Kerberos 8 * v5 distribution, version 1.4.2, and radically simplified for use in 9 * e2fsprogs. (Support for locking for multi-threaded operations, 10 * being able to modify and update the configuration file 11 * programmatically, and Mac/Windows portability have been removed. 12 * It has been folded into a single C source file to make it easier to 13 * fold into an application program.) 14 * 15 * Copyright (C) 2005, 2006 by Theodore Ts'o. 16 * 17 * %Begin-Header% 18 * This file may be redistributed under the terms of the GNU Public 19 * License. 20 * %End-Header% 21 * 22 * Copyright (C) 1985-2005 by the Massachusetts Institute of Technology. 23 * 24 * All rights reserved. 25 * 26 * Export of this software from the United States of America may require 27 * a specific license from the United States Government. It is the 28 * responsibility of any person or organization contemplating export to 29 * obtain such a license before exporting. 30 * 31 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 32 * distribute this software and its documentation for any purpose and 33 * without fee is hereby granted, provided that the above copyright 34 * notice appear in all copies and that both that copyright notice and 35 * this permission notice appear in supporting documentation, and that 36 * the name of M.I.T. not be used in advertising or publicity pertaining 37 * to distribution of the software without specific, written prior 38 * permission. Furthermore if you modify this software you must label 39 * your software as modified software and not distribute it in such a 40 * fashion that it might be confused with the original MIT software. 41 * M.I.T. makes no representations about the suitability of this software 42 * for any purpose. It is provided "as is" without express or implied 43 * warranty. 44 * 45 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 46 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 47 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 48 * 49 */ 50 51 #include "config.h" 52 #ifdef HAVE_UNISTD_H 53 #include <unistd.h> 54 #endif 55 #include <stdio.h> 56 #ifdef HAVE_STDLIB_H 57 #include <stdlib.h> 58 #endif 59 #include <time.h> 60 #include <string.h> 61 #include <strings.h> 62 #include <errno.h> 63 #include <ctype.h> 64 #include <limits.h> 65 #include <stddef.h> 66 #include <sys/types.h> 67 #include <sys/stat.h> 68 #include <dirent.h> 69 #ifdef HAVE_PWD_H 70 #include <pwd.h> 71 #endif 72 73 #include <et/com_err.h> 74 #include "profile.h" 75 #include "prof_err.h" 76 77 #undef STAT_ONCE_PER_SECOND 78 #undef HAVE_STAT 79 80 /* 81 * prof_int.h 82 */ 83 84 typedef long prf_magic_t; 85 86 /* 87 * This is the structure which stores the profile information for a 88 * particular configuration file. 89 */ 90 struct _prf_file_t { 91 prf_magic_t magic; 92 char *filespec; 93 #ifdef STAT_ONCE_PER_SECOND 94 time_t last_stat; 95 #endif 96 time_t timestamp; /* time tree was last updated from file */ 97 int flags; /* r/w, dirty */ 98 int upd_serial; /* incremented when data changes */ 99 struct profile_node *root; 100 struct _prf_file_t *next; 101 }; 102 103 typedef struct _prf_file_t *prf_file_t; 104 105 /* 106 * The profile flags 107 */ 108 #define PROFILE_FILE_RW 0x0001 109 #define PROFILE_FILE_DIRTY 0x0002 110 #define PROFILE_FILE_NO_RELOAD 0x0004 111 112 /* 113 * This structure defines the high-level, user visible profile_t 114 * object, which is used as a handle by users who need to query some 115 * configuration file(s) 116 */ 117 struct _profile_t { 118 prf_magic_t magic; 119 prf_file_t first_file; 120 }; 121 122 /* 123 * Used by the profile iterator in prof_get.c 124 */ 125 #define PROFILE_ITER_LIST_SECTION 0x0001 126 #define PROFILE_ITER_SECTIONS_ONLY 0x0002 127 #define PROFILE_ITER_RELATIONS_ONLY 0x0004 128 129 #define PROFILE_ITER_FINAL_SEEN 0x0100 130 131 /* 132 * Check if a filespec is last in a list (NULL on UNIX, invalid FSSpec on MacOS 133 */ 134 135 #define PROFILE_LAST_FILESPEC(x) (((x) == NULL) || ((x)[0] == '\0')) 136 137 struct profile_node { 138 errcode_t magic; 139 char *name; 140 char *value; 141 int group_level; 142 unsigned int final:1; /* Indicate don't search next file */ 143 unsigned int deleted:1; 144 struct profile_node *first_child; 145 struct profile_node *parent; 146 struct profile_node *next, *prev; 147 }; 148 149 #define CHECK_MAGIC(node) \ 150 if ((node)->magic != PROF_MAGIC_NODE) \ 151 return PROF_MAGIC_NODE; 152 153 /* profile parser declarations */ 154 struct parse_state { 155 int state; 156 int group_level; 157 int line_num; 158 struct profile_node *root_section; 159 struct profile_node *current_section; 160 }; 161 162 static const char *default_filename = "<default>"; 163 164 static profile_syntax_err_cb_t syntax_err_cb; 165 166 static errcode_t parse_line(char *line, struct parse_state *state); 167 168 #ifdef DEBUG_PROGRAM 169 static errcode_t profile_write_tree_file 170 (struct profile_node *root, FILE *dstfile); 171 172 static errcode_t profile_write_tree_to_buffer 173 (struct profile_node *root, char **buf); 174 #endif 175 176 177 static void profile_free_node 178 (struct profile_node *relation); 179 180 static errcode_t profile_create_node 181 (const char *name, const char *value, 182 struct profile_node **ret_node); 183 184 #ifdef DEBUG_PROGRAM 185 static errcode_t profile_verify_node 186 (struct profile_node *node); 187 #endif 188 189 static errcode_t profile_add_node 190 (struct profile_node *section, 191 const char *name, const char *value, 192 struct profile_node **ret_node); 193 194 static errcode_t profile_find_node 195 (struct profile_node *section, 196 const char *name, const char *value, 197 int section_flag, void **state, 198 struct profile_node **node); 199 200 static errcode_t profile_node_iterator 201 (void **iter_p, struct profile_node **ret_node, 202 char **ret_name, char **ret_value); 203 204 static errcode_t profile_open_file 205 (const char * file, prf_file_t *ret_prof); 206 207 static errcode_t profile_update_file 208 (prf_file_t prf); 209 210 static void profile_free_file 211 (prf_file_t profile); 212 213 static errcode_t profile_get_value(profile_t profile, const char *name, 214 const char *subname, const char *subsubname, 215 const char **ret_value); 216 217 218 /* 219 * prof_init.c --- routines that manipulate the user-visible profile_t 220 * object. 221 */ 222 223 static int compstr(const void *m1, const void *m2) 224 { 225 const char *s1 = *((const char * const *) m1); 226 const char *s2 = *((const char * const *) m2); 227 228 return strcmp(s1, s2); 229 } 230 231 static void free_list(char **list) 232 { 233 char **cp; 234 235 if (list == 0) 236 return; 237 238 for (cp = list; *cp; cp++) 239 free(*cp); 240 free(list); 241 } 242 243 static errcode_t get_dirlist(const char *dirname, char***ret_array) 244 { 245 DIR *dir; 246 struct dirent *de; 247 struct stat st; 248 errcode_t retval; 249 char *fn, *cp; 250 char **array = 0, **new_array; 251 int max = 0, num = 0; 252 253 dir = opendir(dirname); 254 if (!dir) 255 return errno; 256 257 while ((de = readdir(dir)) != NULL) { 258 for (cp = de->d_name; *cp; cp++) { 259 if (!isalnum(*cp) && 260 (*cp != '-') && 261 (*cp != '_')) 262 break; 263 } 264 if (*cp) 265 continue; 266 fn = malloc(strlen(dirname) + strlen(de->d_name) + 2); 267 if (!fn) { 268 retval = ENOMEM; 269 goto errout; 270 } 271 sprintf(fn, "%s/%s", dirname, de->d_name); 272 if ((stat(fn, &st) < 0) || !S_ISREG(st.st_mode)) { 273 free(fn); 274 continue; 275 } 276 if (num >= max) { 277 max += 10; 278 new_array = realloc(array, sizeof(char *) * (max+1)); 279 if (!new_array) { 280 retval = ENOMEM; 281 free(fn); 282 goto errout; 283 } 284 array = new_array; 285 } 286 array[num++] = fn; 287 } 288 if (array) { 289 qsort(array, num, sizeof(char *), compstr); 290 array[num++] = 0; 291 } 292 *ret_array = array; 293 closedir(dir); 294 return 0; 295 errout: 296 if (array) 297 array[num] = 0; 298 closedir(dir); 299 free_list(array); 300 return retval; 301 } 302 303 errcode_t 304 profile_init(const char * const *files, profile_t *ret_profile) 305 { 306 const char * const *fs; 307 profile_t profile; 308 prf_file_t new_file, *last; 309 errcode_t retval = 0; 310 char **cpp, *cp, **array = 0; 311 312 profile = malloc(sizeof(struct _profile_t)); 313 if (!profile) 314 return ENOMEM; 315 memset(profile, 0, sizeof(struct _profile_t)); 316 profile->magic = PROF_MAGIC_PROFILE; 317 last = &profile->first_file; 318 319 /* if the filenames list is not specified return an empty profile */ 320 if ( files ) { 321 for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) { 322 if (array) 323 free_list(array); 324 array = NULL; 325 retval = get_dirlist(*fs, &array); 326 if (retval == 0) { 327 if (!array) 328 continue; 329 for (cpp = array; (cp = *cpp); cpp++) { 330 retval = profile_open_file(cp, &new_file); 331 if (retval == EACCES) 332 continue; 333 if (retval) 334 goto errout; 335 *last = new_file; 336 last = &new_file->next; 337 } 338 } else if ((retval != ENOTDIR) && 339 strcmp(*fs, default_filename)) 340 goto errout; 341 342 retval = profile_open_file(*fs, &new_file); 343 /* if this file is missing, skip to the next */ 344 if (retval == ENOENT || retval == EACCES) { 345 continue; 346 } 347 if (retval) 348 goto errout; 349 *last = new_file; 350 last = &new_file->next; 351 } 352 /* 353 * If all the files were not found, return the appropriate error. 354 */ 355 if (!profile->first_file) { 356 retval = ENOENT; 357 goto errout; 358 } 359 } 360 361 free_list(array); 362 *ret_profile = profile; 363 return 0; 364 errout: 365 free_list(array); 366 profile_release(profile); 367 return retval; 368 } 369 370 void 371 profile_release(profile_t profile) 372 { 373 prf_file_t p, next; 374 375 if (!profile || profile->magic != PROF_MAGIC_PROFILE) 376 return; 377 378 for (p = profile->first_file; p; p = next) { 379 next = p->next; 380 profile_free_file(p); 381 } 382 profile->magic = 0; 383 free(profile); 384 } 385 386 /* 387 * This function sets the value of the pseudo file "<default>". If 388 * the file "<default>" had previously been passed to profile_init(), 389 * then def_string parameter will be parsed and used as the profile 390 * information for the "<default>" file. 391 */ 392 errcode_t profile_set_default(profile_t profile, const char *def_string) 393 { 394 struct parse_state state; 395 prf_file_t prf; 396 errcode_t retval; 397 const char *in; 398 char *line, *p, *end; 399 int line_size, len; 400 401 if (!def_string || !profile || profile->magic != PROF_MAGIC_PROFILE) 402 return PROF_MAGIC_PROFILE; 403 404 for (prf = profile->first_file; prf; prf = prf->next) { 405 if (strcmp(prf->filespec, default_filename) == 0) 406 break; 407 } 408 if (!prf) 409 return 0; 410 411 if (prf->root) { 412 profile_free_node(prf->root); 413 prf->root = 0; 414 } 415 416 memset(&state, 0, sizeof(struct parse_state)); 417 retval = profile_create_node("(root)", 0, &state.root_section); 418 if (retval) 419 return retval; 420 421 line = 0; 422 line_size = 0; 423 in = def_string; 424 while (*in) { 425 end = strchr(in, '\n'); 426 len = end ? (end - in) : (int) strlen(in); 427 if (len >= line_size) { 428 line_size = len+1; 429 p = realloc(line, line_size); 430 if (!p) { 431 retval = ENOMEM; 432 goto errout; 433 } 434 line = p; 435 } 436 memcpy(line, in, len); 437 line[len] = 0; 438 retval = parse_line(line, &state); 439 if (retval) { 440 errout: 441 if (syntax_err_cb) 442 (syntax_err_cb)(prf->filespec, retval, 443 state.line_num); 444 free(line); 445 if (prf->root) 446 profile_free_node(prf->root); 447 return retval; 448 } 449 if (!end) 450 break; 451 in = end+1; 452 } 453 prf->root = state.root_section; 454 free(line); 455 456 return 0; 457 } 458 459 /* 460 * prof_file.c ---- routines that manipulate an individual profile file. 461 */ 462 463 errcode_t profile_open_file(const char * filespec, 464 prf_file_t *ret_prof) 465 { 466 prf_file_t prf; 467 errcode_t retval; 468 char *home_env = 0; 469 unsigned int len; 470 char *expanded_filename; 471 472 prf = malloc(sizeof(struct _prf_file_t)); 473 if (!prf) 474 return ENOMEM; 475 memset(prf, 0, sizeof(struct _prf_file_t)); 476 prf->magic = PROF_MAGIC_FILE; 477 478 len = strlen(filespec)+1; 479 if (filespec[0] == '~' && filespec[1] == '/') { 480 home_env = getenv("HOME"); 481 #ifdef HAVE_PWD_H 482 if (home_env == NULL) { 483 #ifdef HAVE_GETWUID_R 484 struct passwd *pw, pwx; 485 uid_t uid; 486 char pwbuf[BUFSIZ]; 487 488 uid = getuid(); 489 if (!getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) 490 && pw != NULL && pw->pw_dir[0] != 0) 491 home_env = pw->pw_dir; 492 #else 493 struct passwd *pw; 494 495 pw = getpwuid(getuid()); 496 home_env = pw->pw_dir; 497 #endif 498 } 499 #endif 500 if (home_env) 501 len += strlen(home_env); 502 } 503 expanded_filename = malloc(len); 504 if (expanded_filename == 0) { 505 profile_free_file(prf); 506 return errno; 507 } 508 if (home_env) { 509 strcpy(expanded_filename, home_env); 510 strcat(expanded_filename, filespec+1); 511 } else 512 memcpy(expanded_filename, filespec, len); 513 514 prf->filespec = expanded_filename; 515 516 if (strcmp(prf->filespec, default_filename) != 0) { 517 retval = profile_update_file(prf); 518 if (retval) { 519 profile_free_file(prf); 520 return retval; 521 } 522 } 523 524 *ret_prof = prf; 525 return 0; 526 } 527 528 errcode_t profile_update_file(prf_file_t prf) 529 { 530 errcode_t retval; 531 #ifdef HAVE_STAT 532 struct stat st; 533 #ifdef STAT_ONCE_PER_SECOND 534 time_t now; 535 #endif 536 #endif 537 FILE *f; 538 char buf[2048]; 539 struct parse_state state; 540 541 if (prf->flags & PROFILE_FILE_NO_RELOAD) 542 return 0; 543 544 #ifdef HAVE_STAT 545 #ifdef STAT_ONCE_PER_SECOND 546 now = time(0); 547 if (now == prf->last_stat && prf->root != NULL) { 548 return 0; 549 } 550 #endif 551 if (stat(prf->filespec, &st)) { 552 retval = errno; 553 return retval; 554 } 555 #ifdef STAT_ONCE_PER_SECOND 556 prf->last_stat = now; 557 #endif 558 if (st.st_mtime == prf->timestamp && prf->root != NULL) { 559 return 0; 560 } 561 if (prf->root) { 562 profile_free_node(prf->root); 563 prf->root = 0; 564 } 565 #else 566 /* 567 * If we don't have the stat() call, assume that our in-core 568 * memory image is correct. That is, we won't reread the 569 * profile file if it changes. 570 */ 571 if (prf->root) { 572 return 0; 573 } 574 #endif 575 memset(&state, 0, sizeof(struct parse_state)); 576 retval = profile_create_node("(root)", 0, &state.root_section); 577 if (retval) 578 return retval; 579 errno = 0; 580 f = fopen(prf->filespec, "r"); 581 if (f == NULL) { 582 retval = errno; 583 if (retval == 0) 584 retval = ENOENT; 585 return retval; 586 } 587 prf->upd_serial++; 588 while (!feof(f)) { 589 if (fgets(buf, sizeof(buf), f) == NULL) 590 break; 591 retval = parse_line(buf, &state); 592 if (retval) { 593 if (syntax_err_cb) 594 (syntax_err_cb)(prf->filespec, retval, 595 state.line_num); 596 fclose(f); 597 return retval; 598 } 599 } 600 prf->root = state.root_section; 601 602 fclose(f); 603 604 #ifdef HAVE_STAT 605 prf->timestamp = st.st_mtime; 606 #endif 607 return 0; 608 } 609 610 void profile_free_file(prf_file_t prf) 611 { 612 if (prf->root) 613 profile_free_node(prf->root); 614 free(prf->filespec); 615 free(prf); 616 } 617 618 /* Begin the profile parser */ 619 620 profile_syntax_err_cb_t profile_set_syntax_err_cb(profile_syntax_err_cb_t hook) 621 { 622 profile_syntax_err_cb_t old; 623 624 old = syntax_err_cb; 625 syntax_err_cb = hook; 626 return(old); 627 } 628 629 #define STATE_INIT_COMMENT 0 630 #define STATE_STD_LINE 1 631 #define STATE_GET_OBRACE 2 632 633 static char *skip_over_blanks(char *cp) 634 { 635 while (*cp && isspace((int) (*cp))) 636 cp++; 637 return cp; 638 } 639 640 static int end_or_comment(char ch) 641 { 642 return (ch == 0 || ch == '#' || ch == ';'); 643 } 644 645 static char *skip_over_nonblanks(char *cp) 646 { 647 while (!end_or_comment(*cp) && !isspace(*cp)) 648 cp++; 649 return cp; 650 } 651 652 static void strip_line(char *line) 653 { 654 char *p = line + strlen(line); 655 while (p > line && (p[-1] == '\n' || p[-1] == '\r')) 656 *p-- = 0; 657 } 658 659 static void parse_quoted_string(char *str) 660 { 661 char *to, *from; 662 663 to = from = str; 664 665 for (to = from = str; *from && *from != '"'; to++, from++) { 666 if (*from == '\\') { 667 from++; 668 switch (*from) { 669 case 'n': 670 *to = '\n'; 671 break; 672 case 't': 673 *to = '\t'; 674 break; 675 case 'b': 676 *to = '\b'; 677 break; 678 default: 679 *to = *from; 680 } 681 continue; 682 } 683 *to = *from; 684 } 685 *to = '\0'; 686 } 687 688 static errcode_t parse_line(char *line, struct parse_state *state) 689 { 690 char *cp, ch, *tag, *value; 691 char *p; 692 errcode_t retval; 693 struct profile_node *node; 694 int do_subsection = 0; 695 void *iter = 0; 696 697 state->line_num++; 698 if (state->state == STATE_GET_OBRACE) { 699 cp = skip_over_blanks(line); 700 if (*cp != '{') 701 return PROF_MISSING_OBRACE; 702 state->state = STATE_STD_LINE; 703 return 0; 704 } 705 if (state->state == STATE_INIT_COMMENT) { 706 if (line[0] != '[') 707 return 0; 708 state->state = STATE_STD_LINE; 709 } 710 711 if (*line == 0) 712 return 0; 713 strip_line(line); 714 cp = skip_over_blanks(line); 715 ch = *cp; 716 if (end_or_comment(ch)) 717 return 0; 718 if (ch == '[') { 719 if (state->group_level > 0) 720 return PROF_SECTION_NOTOP; 721 cp++; 722 cp = skip_over_blanks(cp); 723 p = strchr(cp, ']'); 724 if (p == NULL) 725 return PROF_SECTION_SYNTAX; 726 if (*cp == '"') { 727 cp++; 728 parse_quoted_string(cp); 729 } else { 730 *p-- = '\0'; 731 while (isspace(*p) && (p > cp)) 732 *p-- = '\0'; 733 if (*cp == 0) 734 return PROF_SECTION_SYNTAX; 735 } 736 retval = profile_find_node(state->root_section, cp, 0, 1, 737 &iter, &state->current_section); 738 if (retval == PROF_NO_SECTION) { 739 retval = profile_add_node(state->root_section, 740 cp, 0, 741 &state->current_section); 742 if (retval) 743 return retval; 744 } else if (retval) 745 return retval; 746 747 /* 748 * Finish off the rest of the line. 749 */ 750 cp = p+1; 751 if (*cp == '*') { 752 state->current_section->final = 1; 753 cp++; 754 } 755 /* 756 * Spaces or comments after ']' should not be fatal 757 */ 758 cp = skip_over_blanks(cp); 759 if (!end_or_comment(*cp)) 760 return PROF_SECTION_SYNTAX; 761 return 0; 762 } 763 if (ch == '}') { 764 if (state->group_level == 0) 765 return PROF_EXTRA_CBRACE; 766 if (*(cp+1) == '*') 767 state->current_section->final = 1; 768 state->current_section = state->current_section->parent; 769 state->group_level--; 770 return 0; 771 } 772 /* 773 * Parse the relations 774 */ 775 tag = cp; 776 cp = strchr(cp, '='); 777 if (!cp) 778 return PROF_RELATION_SYNTAX; 779 if (cp == tag) 780 return PROF_RELATION_SYNTAX; 781 *cp = '\0'; 782 if (*tag == '"') { 783 tag++; 784 parse_quoted_string(tag); 785 } else { 786 /* Look for whitespace on left-hand side. */ 787 p = skip_over_nonblanks(tag); 788 if (*p) 789 *p++ = 0; 790 p = skip_over_blanks(p); 791 /* If we have more non-whitespace, it's an error. */ 792 if (*p) 793 return PROF_RELATION_SYNTAX; 794 } 795 796 cp = skip_over_blanks(cp+1); 797 value = cp; 798 ch = value[0]; 799 if (ch == '"') { 800 value++; 801 parse_quoted_string(value); 802 } else if (end_or_comment(ch)) { 803 do_subsection++; 804 state->state = STATE_GET_OBRACE; 805 } else if (value[0] == '{') { 806 cp = skip_over_blanks(value+1); 807 ch = *cp; 808 if (end_or_comment(ch)) 809 do_subsection++; 810 else 811 return PROF_RELATION_SYNTAX; 812 } else { 813 cp = skip_over_nonblanks(value); 814 p = skip_over_blanks(cp); 815 ch = *p; 816 *cp = 0; 817 if (!end_or_comment(ch)) 818 return PROF_RELATION_SYNTAX; 819 } 820 if (do_subsection) { 821 p = strchr(tag, '*'); 822 if (p) 823 *p = '\0'; 824 retval = profile_add_node(state->current_section, 825 tag, 0, &state->current_section); 826 if (retval) 827 return retval; 828 if (p) 829 state->current_section->final = 1; 830 state->group_level++; 831 return 0; 832 } 833 p = strchr(tag, '*'); 834 if (p) 835 *p = '\0'; 836 profile_add_node(state->current_section, tag, value, &node); 837 if (p) 838 node->final = 1; 839 return 0; 840 } 841 842 #ifdef DEBUG_PROGRAM 843 /* 844 * Return TRUE if the string begins or ends with whitespace 845 */ 846 static int need_double_quotes(char *str) 847 { 848 if (!str || !*str) 849 return 0; 850 if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1)))) 851 return 1; 852 if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b') || 853 strchr(str, ' ') || strchr(str, '#') || strchr(str, ';')) 854 return 1; 855 return 0; 856 } 857 858 /* 859 * Output a string with double quotes, doing appropriate backquoting 860 * of characters as necessary. 861 */ 862 static void output_quoted_string(char *str, void (*cb)(const char *,void *), 863 void *data) 864 { 865 char ch; 866 char buf[2]; 867 868 cb("\"", data); 869 if (!str) { 870 cb("\"", data); 871 return; 872 } 873 buf[1] = 0; 874 while ((ch = *str++)) { 875 switch (ch) { 876 case '\\': 877 cb("\\\\", data); 878 break; 879 case '\n': 880 cb("\\n", data); 881 break; 882 case '\t': 883 cb("\\t", data); 884 break; 885 case '\b': 886 cb("\\b", data); 887 break; 888 default: 889 /* This would be a lot faster if we scanned 890 forward for the next "interesting" 891 character. */ 892 buf[0] = ch; 893 cb(buf, data); 894 break; 895 } 896 } 897 cb("\"", data); 898 } 899 900 #ifndef EOL 901 #define EOL "\n" 902 #endif 903 904 /* Errors should be returned, not ignored! */ 905 static void dump_profile(struct profile_node *root, int level, 906 void (*cb)(const char *, void *), void *data) 907 { 908 int i; 909 struct profile_node *p; 910 void *iter; 911 long retval; 912 913 iter = 0; 914 do { 915 retval = profile_find_node(root, 0, 0, 0, &iter, &p); 916 if (retval) 917 break; 918 for (i=0; i < level; i++) 919 cb("\t", data); 920 if (need_double_quotes(p->name)) 921 output_quoted_string(p->name, cb, data); 922 else 923 cb(p->name, data); 924 cb(" = ", data); 925 if (need_double_quotes(p->value)) 926 output_quoted_string(p->value, cb, data); 927 else 928 cb(p->value, data); 929 cb(EOL, data); 930 } while (iter != 0); 931 932 iter = 0; 933 do { 934 retval = profile_find_node(root, 0, 0, 1, &iter, &p); 935 if (retval) 936 break; 937 if (level == 0) { /* [xxx] */ 938 cb("[", data); 939 if (need_double_quotes(p->name)) 940 output_quoted_string(p->name, cb, data); 941 else 942 cb(p->name, data); 943 cb("]", data); 944 cb(p->final ? "*" : "", data); 945 cb(EOL, data); 946 dump_profile(p, level+1, cb, data); 947 cb(EOL, data); 948 } else { /* xxx = { ... } */ 949 for (i=0; i < level; i++) 950 cb("\t", data); 951 if (need_double_quotes(p->name)) 952 output_quoted_string(p->name, cb, data); 953 else 954 cb(p->name, data); 955 cb(" = {", data); 956 cb(EOL, data); 957 dump_profile(p, level+1, cb, data); 958 for (i=0; i < level; i++) 959 cb("\t", data); 960 cb("}", data); 961 cb(p->final ? "*" : "", data); 962 cb(EOL, data); 963 } 964 } while (iter != 0); 965 } 966 967 static void dump_profile_to_file_cb(const char *str, void *data) 968 { 969 fputs(str, data); 970 } 971 972 errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile) 973 { 974 dump_profile(root, 0, dump_profile_to_file_cb, dstfile); 975 return 0; 976 } 977 978 struct prof_buf { 979 char *base; 980 size_t cur, max; 981 int err; 982 }; 983 984 static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len) 985 { 986 if (b->err) 987 return; 988 if (b->max - b->cur < len) { 989 size_t newsize; 990 char *newptr; 991 992 newsize = b->max + (b->max >> 1) + len + 1024; 993 newptr = realloc(b->base, newsize); 994 if (newptr == NULL) { 995 b->err = 1; 996 return; 997 } 998 b->base = newptr; 999 b->max = newsize; 1000 } 1001 memcpy(b->base + b->cur, d, len); 1002 b->cur += len; /* ignore overflow */ 1003 } 1004 1005 static void dump_profile_to_buffer_cb(const char *str, void *data) 1006 { 1007 add_data_to_buffer((struct prof_buf *)data, str, strlen(str)); 1008 } 1009 1010 errcode_t profile_write_tree_to_buffer(struct profile_node *root, 1011 char **buf) 1012 { 1013 struct prof_buf prof_buf = { 0, 0, 0, 0 }; 1014 1015 dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf); 1016 if (prof_buf.err) { 1017 *buf = NULL; 1018 return ENOMEM; 1019 } 1020 add_data_to_buffer(&prof_buf, "", 1); /* append nul */ 1021 if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) { 1022 char *newptr = realloc(prof_buf.base, prof_buf.cur); 1023 if (newptr) 1024 prof_buf.base = newptr; 1025 } 1026 *buf = prof_buf.base; 1027 return 0; 1028 } 1029 #endif 1030 1031 /* 1032 * prof_tree.c --- these routines maintain the parse tree of the 1033 * config file. 1034 * 1035 * All of the details of how the tree is stored is abstracted away in 1036 * this file; all of the other profile routines build, access, and 1037 * modify the tree via the accessor functions found in this file. 1038 * 1039 * Each node may represent either a relation or a section header. 1040 * 1041 * A section header must have its value field set to 0, and may a one 1042 * or more child nodes, pointed to by first_child. 1043 * 1044 * A relation has as its value a pointer to allocated memory 1045 * containing a string. Its first_child pointer must be null. 1046 * 1047 */ 1048 1049 /* 1050 * Free a node, and any children 1051 */ 1052 void profile_free_node(struct profile_node *node) 1053 { 1054 struct profile_node *child, *next; 1055 1056 if (node->magic != PROF_MAGIC_NODE) 1057 return; 1058 1059 free(node->name); 1060 free(node->value); 1061 1062 for (child=node->first_child; child; child = next) { 1063 next = child->next; 1064 profile_free_node(child); 1065 } 1066 node->magic = 0; 1067 1068 free(node); 1069 } 1070 1071 #ifndef HAVE_STRDUP 1072 #undef strdup 1073 #define strdup MYstrdup 1074 static char *MYstrdup (const char *s) 1075 { 1076 size_t sz = strlen(s) + 1; 1077 char *p = malloc(sz); 1078 if (p != 0) 1079 memcpy(p, s, sz); 1080 return p; 1081 } 1082 #endif 1083 1084 /* 1085 * Create a node 1086 */ 1087 errcode_t profile_create_node(const char *name, const char *value, 1088 struct profile_node **ret_node) 1089 { 1090 struct profile_node *new; 1091 1092 new = malloc(sizeof(struct profile_node)); 1093 if (!new) 1094 return ENOMEM; 1095 memset(new, 0, sizeof(struct profile_node)); 1096 new->name = strdup(name); 1097 if (new->name == 0) { 1098 profile_free_node(new); 1099 return ENOMEM; 1100 } 1101 if (value) { 1102 new->value = strdup(value); 1103 if (new->value == 0) { 1104 profile_free_node(new); 1105 return ENOMEM; 1106 } 1107 } 1108 new->magic = PROF_MAGIC_NODE; 1109 1110 *ret_node = new; 1111 return 0; 1112 } 1113 1114 /* 1115 * This function verifies that all of the representation invariants of 1116 * the profile are true. If not, we have a programming bug somewhere, 1117 * probably in this file. 1118 */ 1119 #ifdef DEBUG_PROGRAM 1120 errcode_t profile_verify_node(struct profile_node *node) 1121 { 1122 struct profile_node *p, *last; 1123 errcode_t retval; 1124 1125 CHECK_MAGIC(node); 1126 1127 if (node->value && node->first_child) 1128 return PROF_SECTION_WITH_VALUE; 1129 1130 last = 0; 1131 for (p = node->first_child; p; last = p, p = p->next) { 1132 if (p->prev != last) 1133 return PROF_BAD_LINK_LIST; 1134 if (last && (last->next != p)) 1135 return PROF_BAD_LINK_LIST; 1136 if (node->group_level+1 != p->group_level) 1137 return PROF_BAD_GROUP_LVL; 1138 if (p->parent != node) 1139 return PROF_BAD_PARENT_PTR; 1140 retval = profile_verify_node(p); 1141 if (retval) 1142 return retval; 1143 } 1144 return 0; 1145 } 1146 #endif 1147 1148 /* 1149 * Add a node to a particular section 1150 */ 1151 errcode_t profile_add_node(struct profile_node *section, const char *name, 1152 const char *value, struct profile_node **ret_node) 1153 { 1154 errcode_t retval; 1155 struct profile_node *p, *last, *new; 1156 1157 CHECK_MAGIC(section); 1158 1159 if (section->value) 1160 return PROF_ADD_NOT_SECTION; 1161 1162 /* 1163 * Find the place to insert the new node. We look for the 1164 * place *after* the last match of the node name, since 1165 * order matters. 1166 */ 1167 for (p=section->first_child, last = 0; p; last = p, p = p->next) { 1168 int cmp; 1169 cmp = strcmp(p->name, name); 1170 if (cmp > 0) 1171 break; 1172 } 1173 retval = profile_create_node(name, value, &new); 1174 if (retval) 1175 return retval; 1176 new->group_level = section->group_level+1; 1177 new->deleted = 0; 1178 new->parent = section; 1179 new->prev = last; 1180 new->next = p; 1181 if (p) 1182 p->prev = new; 1183 if (last) 1184 last->next = new; 1185 else 1186 section->first_child = new; 1187 if (ret_node) 1188 *ret_node = new; 1189 return 0; 1190 } 1191 1192 /* 1193 * Iterate through the section, returning the nodes which match 1194 * the given name. If name is NULL, then interate through all the 1195 * nodes in the section. If section_flag is non-zero, only return the 1196 * section which matches the name; don't return relations. If value 1197 * is non-NULL, then only return relations which match the requested 1198 * value. (The value argument is ignored if section_flag is non-zero.) 1199 * 1200 * The first time this routine is called, the state pointer must be 1201 * null. When this profile_find_node_relation() returns, if the state 1202 * pointer is non-NULL, then this routine should be called again. 1203 * (This won't happen if section_flag is non-zero, obviously.) 1204 * 1205 */ 1206 errcode_t profile_find_node(struct profile_node *section, const char *name, 1207 const char *value, int section_flag, void **state, 1208 struct profile_node **node) 1209 { 1210 struct profile_node *p; 1211 1212 CHECK_MAGIC(section); 1213 p = *state; 1214 if (p) { 1215 CHECK_MAGIC(p); 1216 } else 1217 p = section->first_child; 1218 1219 for (; p; p = p->next) { 1220 if (name && (strcmp(p->name, name))) 1221 continue; 1222 if (section_flag) { 1223 if (p->value) 1224 continue; 1225 } else { 1226 if (!p->value) 1227 continue; 1228 if (value && (strcmp(p->value, value))) 1229 continue; 1230 } 1231 if (p->deleted) 1232 continue; 1233 /* A match! */ 1234 if (node) 1235 *node = p; 1236 break; 1237 } 1238 if (p == 0) { 1239 *state = 0; 1240 return section_flag ? PROF_NO_SECTION : PROF_NO_RELATION; 1241 } 1242 /* 1243 * OK, we've found one match; now let's try to find another 1244 * one. This way, if we return a non-zero state pointer, 1245 * there's guaranteed to be another match that's returned. 1246 */ 1247 for (p = p->next; p; p = p->next) { 1248 if (name && (strcmp(p->name, name))) 1249 continue; 1250 if (section_flag) { 1251 if (p->value) 1252 continue; 1253 } else { 1254 if (!p->value) 1255 continue; 1256 if (value && (strcmp(p->value, value))) 1257 continue; 1258 } 1259 /* A match! */ 1260 break; 1261 } 1262 *state = p; 1263 return 0; 1264 } 1265 1266 /* 1267 * This is a general-purpose iterator for returning all nodes that 1268 * match the specified name array. 1269 */ 1270 struct profile_iterator { 1271 prf_magic_t magic; 1272 profile_t profile; 1273 int flags; 1274 const char *const *names; 1275 const char *name; 1276 prf_file_t file; 1277 int file_serial; 1278 int done_idx; 1279 struct profile_node *node; 1280 int num; 1281 }; 1282 1283 errcode_t 1284 profile_iterator_create(profile_t profile, const char *const *names, int flags, 1285 void **ret_iter) 1286 { 1287 struct profile_iterator *iter; 1288 int done_idx = 0; 1289 1290 if (profile == 0) 1291 return PROF_NO_PROFILE; 1292 if (profile->magic != PROF_MAGIC_PROFILE) 1293 return PROF_MAGIC_PROFILE; 1294 if (!names) 1295 return PROF_BAD_NAMESET; 1296 if (!(flags & PROFILE_ITER_LIST_SECTION)) { 1297 if (!names[0]) 1298 return PROF_BAD_NAMESET; 1299 done_idx = 1; 1300 } 1301 1302 if ((iter = malloc(sizeof(struct profile_iterator))) == NULL) 1303 return ENOMEM; 1304 1305 iter->magic = PROF_MAGIC_ITERATOR; 1306 iter->profile = profile; 1307 iter->names = names; 1308 iter->flags = flags; 1309 iter->file = profile->first_file; 1310 iter->done_idx = done_idx; 1311 iter->node = 0; 1312 iter->num = 0; 1313 *ret_iter = iter; 1314 return 0; 1315 } 1316 1317 void profile_iterator_free(void **iter_p) 1318 { 1319 struct profile_iterator *iter; 1320 1321 if (!iter_p) 1322 return; 1323 iter = *iter_p; 1324 if (!iter || iter->magic != PROF_MAGIC_ITERATOR) 1325 return; 1326 free(iter); 1327 *iter_p = 0; 1328 } 1329 1330 /* 1331 * Note: the returned character strings in ret_name and ret_value 1332 * points to the stored character string in the parse string. Before 1333 * this string value is returned to a calling application 1334 * (profile_node_iterator is not an exported interface), it should be 1335 * strdup()'ed. 1336 */ 1337 errcode_t profile_node_iterator(void **iter_p, struct profile_node **ret_node, 1338 char **ret_name, char **ret_value) 1339 { 1340 struct profile_iterator *iter = *iter_p; 1341 struct profile_node *section, *p; 1342 const char *const *cpp; 1343 errcode_t retval; 1344 int skip_num = 0; 1345 1346 if (!iter || iter->magic != PROF_MAGIC_ITERATOR) 1347 return PROF_MAGIC_ITERATOR; 1348 if (iter->file && iter->file->magic != PROF_MAGIC_FILE) 1349 return PROF_MAGIC_FILE; 1350 /* 1351 * If the file has changed, then the node pointer is invalid, 1352 * so we'll have search the file again looking for it. 1353 */ 1354 if (iter->node && (iter->file && 1355 iter->file->upd_serial != iter->file_serial)) { 1356 iter->flags &= ~PROFILE_ITER_FINAL_SEEN; 1357 skip_num = iter->num; 1358 iter->node = 0; 1359 } 1360 if (iter->node && iter->node->magic != PROF_MAGIC_NODE) { 1361 return PROF_MAGIC_NODE; 1362 } 1363 get_new_file: 1364 if (iter->node == 0) { 1365 if (iter->file == NULL || 1366 (iter->flags & PROFILE_ITER_FINAL_SEEN)) { 1367 profile_iterator_free(iter_p); 1368 if (ret_node) 1369 *ret_node = 0; 1370 if (ret_name) 1371 *ret_name = 0; 1372 if (ret_value) 1373 *ret_value =0; 1374 return 0; 1375 } 1376 if ((retval = profile_update_file(iter->file))) { 1377 if (retval == ENOENT || retval == EACCES) { 1378 /* XXX memory leak? */ 1379 if (iter->file) 1380 iter->file = iter->file->next; 1381 skip_num = 0; 1382 retval = 0; 1383 goto get_new_file; 1384 } else { 1385 profile_iterator_free(iter_p); 1386 return retval; 1387 } 1388 } 1389 iter->file_serial = iter->file->upd_serial; 1390 /* 1391 * Find the section to list if we are a LIST_SECTION, 1392 * or find the containing section if not. 1393 */ 1394 section = iter->file->root; 1395 for (cpp = iter->names; cpp[iter->done_idx]; cpp++) { 1396 for (p=section->first_child; p; p = p->next) { 1397 if (!strcmp(p->name, *cpp) && !p->value) 1398 break; 1399 } 1400 if (!p) { 1401 section = 0; 1402 break; 1403 } 1404 section = p; 1405 if (p->final) 1406 iter->flags |= PROFILE_ITER_FINAL_SEEN; 1407 } 1408 if (!section) { 1409 if (iter->file) 1410 iter->file = iter->file->next; 1411 skip_num = 0; 1412 goto get_new_file; 1413 } 1414 iter->name = *cpp; 1415 iter->node = section->first_child; 1416 } 1417 /* 1418 * OK, now we know iter->node is set up correctly. Let's do 1419 * the search. 1420 */ 1421 for (p = iter->node; p; p = p->next) { 1422 if (iter->name && strcmp(p->name, iter->name)) 1423 continue; 1424 if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) && 1425 p->value) 1426 continue; 1427 if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) && 1428 !p->value) 1429 continue; 1430 if (skip_num > 0) { 1431 skip_num--; 1432 continue; 1433 } 1434 if (p->deleted) 1435 continue; 1436 break; 1437 } 1438 iter->num++; 1439 if (!p) { 1440 if (iter->file) 1441 iter->file = iter->file->next; 1442 iter->node = 0; 1443 skip_num = 0; 1444 goto get_new_file; 1445 } 1446 if ((iter->node = p->next) == NULL) 1447 if (iter->file) 1448 iter->file = iter->file->next; 1449 if (ret_node) 1450 *ret_node = p; 1451 if (ret_name) 1452 *ret_name = p->name; 1453 if (ret_value) 1454 *ret_value = p->value; 1455 return 0; 1456 } 1457 1458 1459 /* 1460 * prof_get.c --- routines that expose the public interfaces for 1461 * querying items from the profile. 1462 * 1463 */ 1464 1465 /* 1466 * This function only gets the first value from the file; it is a 1467 * helper function for profile_get_string, profile_get_integer, etc. 1468 */ 1469 errcode_t profile_get_value(profile_t profile, const char *name, 1470 const char *subname, const char *subsubname, 1471 const char **ret_value) 1472 { 1473 errcode_t retval; 1474 void *state; 1475 char *value; 1476 const char *names[4]; 1477 1478 names[0] = name; 1479 names[1] = subname; 1480 names[2] = subsubname; 1481 names[3] = 0; 1482 1483 if ((retval = profile_iterator_create(profile, names, 1484 PROFILE_ITER_RELATIONS_ONLY, 1485 &state))) 1486 return retval; 1487 1488 if ((retval = profile_node_iterator(&state, 0, 0, &value))) 1489 goto cleanup; 1490 1491 if (value) 1492 *ret_value = value; 1493 else 1494 retval = PROF_NO_RELATION; 1495 1496 cleanup: 1497 profile_iterator_free(&state); 1498 return retval; 1499 } 1500 1501 errcode_t 1502 profile_get_string(profile_t profile, const char *name, const char *subname, 1503 const char *subsubname, const char *def_val, 1504 char **ret_string) 1505 { 1506 const char *value; 1507 errcode_t retval; 1508 1509 if (profile) { 1510 retval = profile_get_value(profile, name, subname, 1511 subsubname, &value); 1512 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) 1513 value = def_val; 1514 else if (retval) 1515 return retval; 1516 } else 1517 value = def_val; 1518 1519 if (value) { 1520 *ret_string = malloc(strlen(value)+1); 1521 if (*ret_string == 0) 1522 return ENOMEM; 1523 strcpy(*ret_string, value); 1524 } else 1525 *ret_string = 0; 1526 return 0; 1527 } 1528 1529 errcode_t 1530 profile_get_integer(profile_t profile, const char *name, const char *subname, 1531 const char *subsubname, int def_val, int *ret_int) 1532 { 1533 const char *value; 1534 errcode_t retval; 1535 char *end_value; 1536 long ret_long; 1537 1538 *ret_int = def_val; 1539 if (profile == 0) 1540 return 0; 1541 1542 retval = profile_get_value(profile, name, subname, subsubname, &value); 1543 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { 1544 *ret_int = def_val; 1545 return 0; 1546 } else if (retval) 1547 return retval; 1548 1549 if (value[0] == 0) 1550 /* Empty string is no good. */ 1551 return PROF_BAD_INTEGER; 1552 errno = 0; 1553 ret_long = strtol(value, &end_value, 0); 1554 1555 /* Overflow or underflow. */ 1556 if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0) 1557 return PROF_BAD_INTEGER; 1558 /* Value outside "int" range. */ 1559 if ((long) (int) ret_long != ret_long) 1560 return PROF_BAD_INTEGER; 1561 /* Garbage in string. */ 1562 if (end_value != value + strlen (value)) 1563 return PROF_BAD_INTEGER; 1564 1565 1566 *ret_int = ret_long; 1567 return 0; 1568 } 1569 1570 errcode_t 1571 profile_get_uint(profile_t profile, const char *name, const char *subname, 1572 const char *subsubname, unsigned int def_val, 1573 unsigned int *ret_int) 1574 { 1575 const char *value; 1576 errcode_t retval; 1577 char *end_value; 1578 unsigned long ret_long; 1579 1580 *ret_int = def_val; 1581 if (profile == 0) 1582 return 0; 1583 1584 retval = profile_get_value(profile, name, subname, subsubname, &value); 1585 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { 1586 *ret_int = def_val; 1587 return 0; 1588 } else if (retval) 1589 return retval; 1590 1591 if (value[0] == 0) 1592 /* Empty string is no good. */ 1593 return PROF_BAD_INTEGER; 1594 errno = 0; 1595 ret_long = strtoul(value, &end_value, 0); 1596 1597 /* Overflow or underflow. */ 1598 if ((ret_long == ULONG_MAX) && errno != 0) 1599 return PROF_BAD_INTEGER; 1600 /* Value outside "int" range. */ 1601 if ((unsigned long) (unsigned int) ret_long != ret_long) 1602 return PROF_BAD_INTEGER; 1603 /* Garbage in string. */ 1604 if (end_value != value + strlen (value)) 1605 return PROF_BAD_INTEGER; 1606 1607 *ret_int = ret_long; 1608 return 0; 1609 } 1610 1611 errcode_t 1612 profile_get_double(profile_t profile, const char *name, const char *subname, 1613 const char *subsubname, double def_val, double *ret_double) 1614 { 1615 const char *value; 1616 errcode_t retval; 1617 char *end_value; 1618 double double_val; 1619 1620 *ret_double = def_val; 1621 if (profile == 0) 1622 return 0; 1623 1624 retval = profile_get_value(profile, name, subname, subsubname, &value); 1625 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { 1626 *ret_double = def_val; 1627 return 0; 1628 } else if (retval) 1629 return retval; 1630 1631 if (value[0] == 0) 1632 /* Empty string is no good. */ 1633 return PROF_BAD_INTEGER; 1634 errno = 0; 1635 double_val = strtod(value, &end_value); 1636 1637 /* Overflow or underflow. */ 1638 if (errno != 0) 1639 return PROF_BAD_INTEGER; 1640 /* Garbage in string. */ 1641 if (end_value != value + strlen(value)) 1642 return PROF_BAD_INTEGER; 1643 1644 *ret_double = double_val; 1645 return 0; 1646 } 1647 1648 static const char *const conf_yes[] = { 1649 "y", "yes", "true", "t", "1", "on", 1650 0, 1651 }; 1652 1653 static const char *const conf_no[] = { 1654 "n", "no", "false", "nil", "0", "off", 1655 0, 1656 }; 1657 1658 static errcode_t 1659 profile_parse_boolean(const char *s, int *ret_boolean) 1660 { 1661 const char *const *p; 1662 1663 if (ret_boolean == NULL) 1664 return PROF_EINVAL; 1665 1666 for(p=conf_yes; *p; p++) { 1667 if (!strcasecmp(*p,s)) { 1668 *ret_boolean = 1; 1669 return 0; 1670 } 1671 } 1672 1673 for(p=conf_no; *p; p++) { 1674 if (!strcasecmp(*p,s)) { 1675 *ret_boolean = 0; 1676 return 0; 1677 } 1678 } 1679 1680 return PROF_BAD_BOOLEAN; 1681 } 1682 1683 errcode_t 1684 profile_get_boolean(profile_t profile, const char *name, const char *subname, 1685 const char *subsubname, int def_val, int *ret_boolean) 1686 { 1687 const char *value; 1688 errcode_t retval; 1689 1690 if (profile == 0) { 1691 *ret_boolean = def_val; 1692 return 0; 1693 } 1694 1695 retval = profile_get_value(profile, name, subname, subsubname, &value); 1696 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { 1697 *ret_boolean = def_val; 1698 return 0; 1699 } else if (retval) 1700 return retval; 1701 1702 return profile_parse_boolean (value, ret_boolean); 1703 } 1704 1705 errcode_t 1706 profile_iterator(void **iter_p, char **ret_name, char **ret_value) 1707 { 1708 char *name, *value; 1709 errcode_t retval; 1710 1711 retval = profile_node_iterator(iter_p, 0, &name, &value); 1712 if (retval) 1713 return retval; 1714 1715 if (ret_name) { 1716 if (name) { 1717 *ret_name = malloc(strlen(name)+1); 1718 if (!*ret_name) 1719 return ENOMEM; 1720 strcpy(*ret_name, name); 1721 } else 1722 *ret_name = 0; 1723 } 1724 if (ret_value) { 1725 if (value) { 1726 *ret_value = malloc(strlen(value)+1); 1727 if (!*ret_value) { 1728 if (ret_name) { 1729 free(*ret_name); 1730 *ret_name = 0; 1731 } 1732 return ENOMEM; 1733 } 1734 strcpy(*ret_value, value); 1735 } else 1736 *ret_value = 0; 1737 } 1738 return 0; 1739 } 1740 1741 #ifdef DEBUG_PROGRAM 1742 1743 /* 1744 * test_profile.c --- testing program for the profile routine 1745 */ 1746 1747 #include "argv_parse.h" 1748 #include "profile_helpers.h" 1749 1750 const char *program_name = "test_profile"; 1751 1752 #define PRINT_VALUE 1 1753 #define PRINT_VALUES 2 1754 1755 static void do_cmd(profile_t profile, char **argv) 1756 { 1757 errcode_t retval; 1758 const char **names, *value; 1759 char **values, **cpp; 1760 char *cmd; 1761 int print_status; 1762 1763 cmd = *(argv); 1764 names = (const char **) argv + 1; 1765 print_status = 0; 1766 retval = 0; 1767 if (cmd == 0) 1768 return; 1769 if (!strcmp(cmd, "query")) { 1770 retval = profile_get_values(profile, names, &values); 1771 print_status = PRINT_VALUES; 1772 } else if (!strcmp(cmd, "query1")) { 1773 const char *name = 0; 1774 const char *subname = 0; 1775 const char *subsubname = 0; 1776 1777 name = names[0]; 1778 if (name) 1779 subname = names[1]; 1780 if (subname) 1781 subsubname = names[2]; 1782 if (subsubname && names[3]) { 1783 fprintf(stderr, 1784 "Only 3 levels are allowed with query1\n"); 1785 retval = EINVAL; 1786 } else 1787 retval = profile_get_value(profile, name, subname, 1788 subsubname, &value); 1789 print_status = PRINT_VALUE; 1790 } else if (!strcmp(cmd, "list_sections")) { 1791 retval = profile_get_subsection_names(profile, names, 1792 &values); 1793 print_status = PRINT_VALUES; 1794 } else if (!strcmp(cmd, "list_relations")) { 1795 retval = profile_get_relation_names(profile, names, 1796 &values); 1797 print_status = PRINT_VALUES; 1798 } else if (!strcmp(cmd, "dump")) { 1799 retval = profile_write_tree_file 1800 (profile->first_file->root, stdout); 1801 #if 0 1802 } else if (!strcmp(cmd, "clear")) { 1803 retval = profile_clear_relation(profile, names); 1804 } else if (!strcmp(cmd, "update")) { 1805 retval = profile_update_relation(profile, names+2, 1806 *names, *(names+1)); 1807 #endif 1808 } else if (!strcmp(cmd, "verify")) { 1809 retval = profile_verify_node 1810 (profile->first_file->root); 1811 #if 0 1812 } else if (!strcmp(cmd, "rename_section")) { 1813 retval = profile_rename_section(profile, names+1, *names); 1814 } else if (!strcmp(cmd, "add")) { 1815 value = *names; 1816 if (strcmp(value, "NULL") == 0) 1817 value = NULL; 1818 retval = profile_add_relation(profile, names+1, value); 1819 } else if (!strcmp(cmd, "flush")) { 1820 retval = profile_flush(profile); 1821 #endif 1822 } else { 1823 printf("Invalid command.\n"); 1824 } 1825 if (retval) { 1826 com_err(cmd, retval, ""); 1827 print_status = 0; 1828 } 1829 switch (print_status) { 1830 case PRINT_VALUE: 1831 printf("%s\n", value); 1832 break; 1833 case PRINT_VALUES: 1834 for (cpp = values; *cpp; cpp++) 1835 printf("%s\n", *cpp); 1836 profile_free_list(values); 1837 break; 1838 } 1839 } 1840 1841 static void do_batchmode(profile_t profile) 1842 { 1843 int argc, ret; 1844 char **argv; 1845 char buf[256]; 1846 1847 while (!feof(stdin)) { 1848 if (fgets(buf, sizeof(buf), stdin) == NULL) 1849 break; 1850 printf(">%s", buf); 1851 ret = argv_parse(buf, &argc, &argv); 1852 if (ret != 0) { 1853 printf("Argv_parse returned %d!\n", ret); 1854 continue; 1855 } 1856 do_cmd(profile, argv); 1857 printf("\n"); 1858 argv_free(argv); 1859 } 1860 profile_release(profile); 1861 exit(0); 1862 1863 } 1864 1865 void syntax_err_report(const char *filename, long err, int line_num) 1866 { 1867 fprintf(stderr, "Syntax error in %s, line number %d: %s\n", 1868 filename, line_num, error_message(err)); 1869 exit(1); 1870 } 1871 1872 const char *default_str = "[foo]\n\tbar=quux\n\tsub = {\n\t\twin = true\n}\n"; 1873 1874 int main(int argc, char **argv) 1875 { 1876 profile_t profile; 1877 long retval; 1878 char *cmd; 1879 1880 if (argc < 2) { 1881 fprintf(stderr, "Usage: %s filename [cmd argset]\n", program_name); 1882 exit(1); 1883 } 1884 1885 initialize_prof_error_table(); 1886 1887 profile_set_syntax_err_cb(syntax_err_report); 1888 1889 retval = profile_init_path(argv[1], &profile); 1890 if (retval) { 1891 com_err(program_name, retval, "while initializing profile"); 1892 exit(1); 1893 } 1894 retval = profile_set_default(profile, default_str); 1895 if (retval) { 1896 com_err(program_name, retval, "while setting default"); 1897 exit(1); 1898 } 1899 1900 cmd = *(argv+2); 1901 if (!cmd || !strcmp(cmd, "batch")) 1902 do_batchmode(profile); 1903 else 1904 do_cmd(profile, argv+2); 1905 profile_release(profile); 1906 1907 return 0; 1908 } 1909 1910 #endif