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