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