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 82typedef long prf_magic_t; 83 84/* 85 * This is the structure which stores the profile information for a 86 * particular configuration file. 87 */ 88struct _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 101typedef 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 */ 115struct _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 135struct 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 */ 152struct 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 160static const char *default_filename = "<default>"; 161 162static profile_syntax_err_cb_t syntax_err_cb; 163 164static errcode_t parse_line(char *line, struct parse_state *state); 165 166#ifdef DEBUG_PROGRAM 167static errcode_t profile_write_tree_file 168 (struct profile_node *root, FILE *dstfile); 169 170static errcode_t profile_write_tree_to_buffer 171 (struct profile_node *root, char **buf); 172#endif 173 174 175static void profile_free_node 176 (struct profile_node *relation); 177 178static errcode_t profile_create_node 179 (const char *name, const char *value, 180 struct profile_node **ret_node); 181 182#ifdef DEBUG_PROGRAM 183static errcode_t profile_verify_node 184 (struct profile_node *node); 185#endif 186 187static errcode_t profile_add_node 188 (struct profile_node *section, 189 const char *name, const char *value, 190 struct profile_node **ret_node); 191 192static 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 198static errcode_t profile_node_iterator 199 (void **iter_p, struct profile_node **ret_node, 200 char **ret_name, char **ret_value); 201 202static errcode_t profile_open_file 203 (const char * file, prf_file_t *ret_prof); 204 205static errcode_t profile_update_file 206 (prf_file_t prf); 207 208static void profile_free_file 209 (prf_file_t profile); 210 211static 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 221static 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 229static 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 241static 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; 292errout: 293 closedir(dir); 294 free_list(array); 295 return retval; 296} 297 298errcode_t 299profile_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; 356errout: 357 free_list(array); 358 profile_release(profile); 359 return retval; 360} 361 362void 363profile_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 */ 384errcode_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 455errcode_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 520errcode_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 602void profile_free_file(prf_file_t prf) 603{ 604 if (prf->root) 605 profile_free_node(prf->root); 606 free(prf->filespec); 607 free(prf); 608} 609 610/* Begin the profile parser */ 611 612profile_syntax_err_cb_t profile_set_syntax_err_cb(profile_syntax_err_cb_t hook) 613{ 614 profile_syntax_err_cb_t old; 615 616 old = syntax_err_cb; 617 syntax_err_cb = hook; 618 return(old); 619} 620 621#define STATE_INIT_COMMENT 0 622#define STATE_STD_LINE 1 623#define STATE_GET_OBRACE 2 624 625static char *skip_over_blanks(char *cp) 626{ 627 while (*cp && isspace((int) (*cp))) 628 cp++; 629 return cp; 630} 631 632static int end_or_comment(char ch) 633{ 634 return (ch == 0 || ch == '#' || ch == ';'); 635} 636 637static char *skip_over_nonblanks(char *cp) 638{ 639 while (!end_or_comment(*cp) && !isspace(*cp)) 640 cp++; 641 return cp; 642} 643 644static void strip_line(char *line) 645{ 646 char *p = line + strlen(line); 647 while (p > line && (p[-1] == '\n' || p[-1] == '\r')) 648 *p-- = 0; 649} 650 651static void parse_quoted_string(char *str) 652{ 653 char *to, *from; 654 655 to = from = str; 656 657 for (to = from = str; *from && *from != '"'; to++, from++) { 658 if (*from == '\\') { 659 from++; 660 switch (*from) { 661 case 'n': 662 *to = '\n'; 663 break; 664 case 't': 665 *to = '\t'; 666 break; 667 case 'b': 668 *to = '\b'; 669 break; 670 default: 671 *to = *from; 672 } 673 continue; 674 } 675 *to = *from; 676 } 677 *to = '\0'; 678} 679 680static errcode_t parse_line(char *line, struct parse_state *state) 681{ 682 char *cp, ch, *tag, *value; 683 char *p; 684 errcode_t retval; 685 struct profile_node *node; 686 int do_subsection = 0; 687 void *iter = 0; 688 689 state->line_num++; 690 if (state->state == STATE_GET_OBRACE) { 691 cp = skip_over_blanks(line); 692 if (*cp != '{') 693 return PROF_MISSING_OBRACE; 694 state->state = STATE_STD_LINE; 695 return 0; 696 } 697 if (state->state == STATE_INIT_COMMENT) { 698 if (line[0] != '[') 699 return 0; 700 state->state = STATE_STD_LINE; 701 } 702 703 if (*line == 0) 704 return 0; 705 strip_line(line); 706 cp = skip_over_blanks(line); 707 ch = *cp; 708 if (end_or_comment(ch)) 709 return 0; 710 if (ch == '[') { 711 if (state->group_level > 0) 712 return PROF_SECTION_NOTOP; 713 cp++; 714 cp = skip_over_blanks(cp); 715 p = strchr(cp, ']'); 716 if (p == NULL) 717 return PROF_SECTION_SYNTAX; 718 if (*cp == '"') { 719 cp++; 720 parse_quoted_string(cp); 721 } else { 722 *p-- = '\0'; 723 while (isspace(*p) && (p > cp)) 724 *p-- = '\0'; 725 if (*cp == 0) 726 return PROF_SECTION_SYNTAX; 727 } 728 retval = profile_find_node(state->root_section, cp, 0, 1, 729 &iter, &state->current_section); 730 if (retval == PROF_NO_SECTION) { 731 retval = profile_add_node(state->root_section, 732 cp, 0, 733 &state->current_section); 734 if (retval) 735 return retval; 736 } else if (retval) 737 return retval; 738 739 /* 740 * Finish off the rest of the line. 741 */ 742 cp = p+1; 743 if (*cp == '*') { 744 state->current_section->final = 1; 745 cp++; 746 } 747 /* 748 * Spaces or comments after ']' should not be fatal 749 */ 750 cp = skip_over_blanks(cp); 751 if (!end_or_comment(*cp)) 752 return PROF_SECTION_SYNTAX; 753 return 0; 754 } 755 if (ch == '}') { 756 if (state->group_level == 0) 757 return PROF_EXTRA_CBRACE; 758 if (*(cp+1) == '*') 759 state->current_section->final = 1; 760 state->current_section = state->current_section->parent; 761 state->group_level--; 762 return 0; 763 } 764 /* 765 * Parse the relations 766 */ 767 tag = cp; 768 cp = strchr(cp, '='); 769 if (!cp) 770 return PROF_RELATION_SYNTAX; 771 if (cp == tag) 772 return PROF_RELATION_SYNTAX; 773 *cp = '\0'; 774 if (*tag == '"') { 775 tag++; 776 parse_quoted_string(tag); 777 } else { 778 /* Look for whitespace on left-hand side. */ 779 p = skip_over_nonblanks(tag); 780 if (*p) 781 *p++ = 0; 782 p = skip_over_blanks(p); 783 /* If we have more non-whitespace, it's an error. */ 784 if (*p) 785 return PROF_RELATION_SYNTAX; 786 } 787 788 cp = skip_over_blanks(cp+1); 789 value = cp; 790 ch = value[0]; 791 if (ch == '"') { 792 value++; 793 parse_quoted_string(value); 794 } else if (end_or_comment(ch)) { 795 do_subsection++; 796 state->state = STATE_GET_OBRACE; 797 } else if (value[0] == '{') { 798 cp = skip_over_blanks(value+1); 799 ch = *cp; 800 if (end_or_comment(ch)) 801 do_subsection++; 802 else 803 return PROF_RELATION_SYNTAX; 804 } else { 805 cp = skip_over_nonblanks(value); 806 p = skip_over_blanks(cp); 807 ch = *p; 808 *cp = 0; 809 if (!end_or_comment(ch)) 810 return PROF_RELATION_SYNTAX; 811 } 812 if (do_subsection) { 813 p = strchr(tag, '*'); 814 if (p) 815 *p = '\0'; 816 retval = profile_add_node(state->current_section, 817 tag, 0, &state->current_section); 818 if (retval) 819 return retval; 820 if (p) 821 state->current_section->final = 1; 822 state->group_level++; 823 return 0; 824 } 825 p = strchr(tag, '*'); 826 if (p) 827 *p = '\0'; 828 profile_add_node(state->current_section, tag, value, &node); 829 if (p) 830 node->final = 1; 831 return 0; 832} 833 834#ifdef DEBUG_PROGRAM 835/* 836 * Return TRUE if the string begins or ends with whitespace 837 */ 838static int need_double_quotes(char *str) 839{ 840 if (!str || !*str) 841 return 0; 842 if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1)))) 843 return 1; 844 if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b') || 845 strchr(str, ' ') || strchr(str, '#') || strchr(str, ';')) 846 return 1; 847 return 0; 848} 849 850/* 851 * Output a string with double quotes, doing appropriate backquoting 852 * of characters as necessary. 853 */ 854static void output_quoted_string(char *str, void (*cb)(const char *,void *), 855 void *data) 856{ 857 char ch; 858 char buf[2]; 859 860 cb("\"", data); 861 if (!str) { 862 cb("\"", data); 863 return; 864 } 865 buf[1] = 0; 866 while ((ch = *str++)) { 867 switch (ch) { 868 case '\\': 869 cb("\\\\", data); 870 break; 871 case '\n': 872 cb("\\n", data); 873 break; 874 case '\t': 875 cb("\\t", data); 876 break; 877 case '\b': 878 cb("\\b", data); 879 break; 880 default: 881 /* This would be a lot faster if we scanned 882 forward for the next "interesting" 883 character. */ 884 buf[0] = ch; 885 cb(buf, data); 886 break; 887 } 888 } 889 cb("\"", data); 890} 891 892#ifndef EOL 893#define EOL "\n" 894#endif 895 896/* Errors should be returned, not ignored! */ 897static void dump_profile(struct profile_node *root, int level, 898 void (*cb)(const char *, void *), void *data) 899{ 900 int i; 901 struct profile_node *p; 902 void *iter; 903 long retval; 904 905 iter = 0; 906 do { 907 retval = profile_find_node(root, 0, 0, 0, &iter, &p); 908 if (retval) 909 break; 910 for (i=0; i < level; i++) 911 cb("\t", data); 912 if (need_double_quotes(p->name)) 913 output_quoted_string(p->name, cb, data); 914 else 915 cb(p->name, data); 916 cb(" = ", data); 917 if (need_double_quotes(p->value)) 918 output_quoted_string(p->value, cb, data); 919 else 920 cb(p->value, data); 921 cb(EOL, data); 922 } while (iter != 0); 923 924 iter = 0; 925 do { 926 retval = profile_find_node(root, 0, 0, 1, &iter, &p); 927 if (retval) 928 break; 929 if (level == 0) { /* [xxx] */ 930 cb("[", data); 931 if (need_double_quotes(p->name)) 932 output_quoted_string(p->name, cb, data); 933 else 934 cb(p->name, data); 935 cb("]", data); 936 cb(p->final ? "*" : "", data); 937 cb(EOL, data); 938 dump_profile(p, level+1, cb, data); 939 cb(EOL, data); 940 } else { /* xxx = { ... } */ 941 for (i=0; i < level; i++) 942 cb("\t", data); 943 if (need_double_quotes(p->name)) 944 output_quoted_string(p->name, cb, data); 945 else 946 cb(p->name, data); 947 cb(" = {", data); 948 cb(EOL, data); 949 dump_profile(p, level+1, cb, data); 950 for (i=0; i < level; i++) 951 cb("\t", data); 952 cb("}", data); 953 cb(p->final ? "*" : "", data); 954 cb(EOL, data); 955 } 956 } while (iter != 0); 957} 958 959static void dump_profile_to_file_cb(const char *str, void *data) 960{ 961 fputs(str, data); 962} 963 964errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile) 965{ 966 dump_profile(root, 0, dump_profile_to_file_cb, dstfile); 967 return 0; 968} 969 970struct prof_buf { 971 char *base; 972 size_t cur, max; 973 int err; 974}; 975 976static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len) 977{ 978 if (b->err) 979 return; 980 if (b->max - b->cur < len) { 981 size_t newsize; 982 char *newptr; 983 984 newsize = b->max + (b->max >> 1) + len + 1024; 985 newptr = realloc(b->base, newsize); 986 if (newptr == NULL) { 987 b->err = 1; 988 return; 989 } 990 b->base = newptr; 991 b->max = newsize; 992 } 993 memcpy(b->base + b->cur, d, len); 994 b->cur += len; /* ignore overflow */ 995} 996 997static void dump_profile_to_buffer_cb(const char *str, void *data) 998{ 999 add_data_to_buffer((struct prof_buf *)data, str, strlen(str)); 1000} 1001 1002errcode_t profile_write_tree_to_buffer(struct profile_node *root, 1003 char **buf) 1004{ 1005 struct prof_buf prof_buf = { 0, 0, 0, 0 }; 1006 1007 dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf); 1008 if (prof_buf.err) { 1009 *buf = NULL; 1010 return ENOMEM; 1011 } 1012 add_data_to_buffer(&prof_buf, "", 1); /* append nul */ 1013 if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) { 1014 char *newptr = realloc(prof_buf.base, prof_buf.cur); 1015 if (newptr) 1016 prof_buf.base = newptr; 1017 } 1018 *buf = prof_buf.base; 1019 return 0; 1020} 1021#endif 1022 1023/* 1024 * prof_tree.c --- these routines maintain the parse tree of the 1025 * config file. 1026 * 1027 * All of the details of how the tree is stored is abstracted away in 1028 * this file; all of the other profile routines build, access, and 1029 * modify the tree via the accessor functions found in this file. 1030 * 1031 * Each node may represent either a relation or a section header. 1032 * 1033 * A section header must have its value field set to 0, and may a one 1034 * or more child nodes, pointed to by first_child. 1035 * 1036 * A relation has as its value a pointer to allocated memory 1037 * containing a string. Its first_child pointer must be null. 1038 * 1039 */ 1040 1041/* 1042 * Free a node, and any children 1043 */ 1044void profile_free_node(struct profile_node *node) 1045{ 1046 struct profile_node *child, *next; 1047 1048 if (node->magic != PROF_MAGIC_NODE) 1049 return; 1050 1051 free(node->name); 1052 free(node->value); 1053 1054 for (child=node->first_child; child; child = next) { 1055 next = child->next; 1056 profile_free_node(child); 1057 } 1058 node->magic = 0; 1059 1060 free(node); 1061} 1062 1063#ifndef HAVE_STRDUP 1064#undef strdup 1065#define strdup MYstrdup 1066static char *MYstrdup (const char *s) 1067{ 1068 size_t sz = strlen(s) + 1; 1069 char *p = malloc(sz); 1070 if (p != 0) 1071 memcpy(p, s, sz); 1072 return p; 1073} 1074#endif 1075 1076/* 1077 * Create a node 1078 */ 1079errcode_t profile_create_node(const char *name, const char *value, 1080 struct profile_node **ret_node) 1081{ 1082 struct profile_node *new; 1083 1084 new = malloc(sizeof(struct profile_node)); 1085 if (!new) 1086 return ENOMEM; 1087 memset(new, 0, sizeof(struct profile_node)); 1088 new->name = strdup(name); 1089 if (new->name == 0) { 1090 profile_free_node(new); 1091 return ENOMEM; 1092 } 1093 if (value) { 1094 new->value = strdup(value); 1095 if (new->value == 0) { 1096 profile_free_node(new); 1097 return ENOMEM; 1098 } 1099 } 1100 new->magic = PROF_MAGIC_NODE; 1101 1102 *ret_node = new; 1103 return 0; 1104} 1105 1106/* 1107 * This function verifies that all of the representation invarients of 1108 * the profile are true. If not, we have a programming bug somewhere, 1109 * probably in this file. 1110 */ 1111#ifdef DEBUG_PROGRAM 1112errcode_t profile_verify_node(struct profile_node *node) 1113{ 1114 struct profile_node *p, *last; 1115 errcode_t retval; 1116 1117 CHECK_MAGIC(node); 1118 1119 if (node->value && node->first_child) 1120 return PROF_SECTION_WITH_VALUE; 1121 1122 last = 0; 1123 for (p = node->first_child; p; last = p, p = p->next) { 1124 if (p->prev != last) 1125 return PROF_BAD_LINK_LIST; 1126 if (last && (last->next != p)) 1127 return PROF_BAD_LINK_LIST; 1128 if (node->group_level+1 != p->group_level) 1129 return PROF_BAD_GROUP_LVL; 1130 if (p->parent != node) 1131 return PROF_BAD_PARENT_PTR; 1132 retval = profile_verify_node(p); 1133 if (retval) 1134 return retval; 1135 } 1136 return 0; 1137} 1138#endif 1139 1140/* 1141 * Add a node to a particular section 1142 */ 1143errcode_t profile_add_node(struct profile_node *section, const char *name, 1144 const char *value, struct profile_node **ret_node) 1145{ 1146 errcode_t retval; 1147 struct profile_node *p, *last, *new; 1148 1149 CHECK_MAGIC(section); 1150 1151 if (section->value) 1152 return PROF_ADD_NOT_SECTION; 1153 1154 /* 1155 * Find the place to insert the new node. We look for the 1156 * place *after* the last match of the node name, since 1157 * order matters. 1158 */ 1159 for (p=section->first_child, last = 0; p; last = p, p = p->next) { 1160 int cmp; 1161 cmp = strcmp(p->name, name); 1162 if (cmp > 0) 1163 break; 1164 } 1165 retval = profile_create_node(name, value, &new); 1166 if (retval) 1167 return retval; 1168 new->group_level = section->group_level+1; 1169 new->deleted = 0; 1170 new->parent = section; 1171 new->prev = last; 1172 new->next = p; 1173 if (p) 1174 p->prev = new; 1175 if (last) 1176 last->next = new; 1177 else 1178 section->first_child = new; 1179 if (ret_node) 1180 *ret_node = new; 1181 return 0; 1182} 1183 1184/* 1185 * Iterate through the section, returning the nodes which match 1186 * the given name. If name is NULL, then interate through all the 1187 * nodes in the section. If section_flag is non-zero, only return the 1188 * section which matches the name; don't return relations. If value 1189 * is non-NULL, then only return relations which match the requested 1190 * value. (The value argument is ignored if section_flag is non-zero.) 1191 * 1192 * The first time this routine is called, the state pointer must be 1193 * null. When this profile_find_node_relation() returns, if the state 1194 * pointer is non-NULL, then this routine should be called again. 1195 * (This won't happen if section_flag is non-zero, obviously.) 1196 * 1197 */ 1198errcode_t profile_find_node(struct profile_node *section, const char *name, 1199 const char *value, int section_flag, void **state, 1200 struct profile_node **node) 1201{ 1202 struct profile_node *p; 1203 1204 CHECK_MAGIC(section); 1205 p = *state; 1206 if (p) { 1207 CHECK_MAGIC(p); 1208 } else 1209 p = section->first_child; 1210 1211 for (; p; p = p->next) { 1212 if (name && (strcmp(p->name, name))) 1213 continue; 1214 if (section_flag) { 1215 if (p->value) 1216 continue; 1217 } else { 1218 if (!p->value) 1219 continue; 1220 if (value && (strcmp(p->value, value))) 1221 continue; 1222 } 1223 if (p->deleted) 1224 continue; 1225 /* A match! */ 1226 if (node) 1227 *node = p; 1228 break; 1229 } 1230 if (p == 0) { 1231 *state = 0; 1232 return section_flag ? PROF_NO_SECTION : PROF_NO_RELATION; 1233 } 1234 /* 1235 * OK, we've found one match; now let's try to find another 1236 * one. This way, if we return a non-zero state pointer, 1237 * there's guaranteed to be another match that's returned. 1238 */ 1239 for (p = p->next; p; p = p->next) { 1240 if (name && (strcmp(p->name, name))) 1241 continue; 1242 if (section_flag) { 1243 if (p->value) 1244 continue; 1245 } else { 1246 if (!p->value) 1247 continue; 1248 if (value && (strcmp(p->value, value))) 1249 continue; 1250 } 1251 /* A match! */ 1252 break; 1253 } 1254 *state = p; 1255 return 0; 1256} 1257 1258/* 1259 * This is a general-purpose iterator for returning all nodes that 1260 * match the specified name array. 1261 */ 1262struct profile_iterator { 1263 prf_magic_t magic; 1264 profile_t profile; 1265 int flags; 1266 const char *const *names; 1267 const char *name; 1268 prf_file_t file; 1269 int file_serial; 1270 int done_idx; 1271 struct profile_node *node; 1272 int num; 1273}; 1274 1275errcode_t 1276profile_iterator_create(profile_t profile, const char *const *names, int flags, 1277 void **ret_iter) 1278{ 1279 struct profile_iterator *iter; 1280 int done_idx = 0; 1281 1282 if (profile == 0) 1283 return PROF_NO_PROFILE; 1284 if (profile->magic != PROF_MAGIC_PROFILE) 1285 return PROF_MAGIC_PROFILE; 1286 if (!names) 1287 return PROF_BAD_NAMESET; 1288 if (!(flags & PROFILE_ITER_LIST_SECTION)) { 1289 if (!names[0]) 1290 return PROF_BAD_NAMESET; 1291 done_idx = 1; 1292 } 1293 1294 if ((iter = malloc(sizeof(struct profile_iterator))) == NULL) 1295 return ENOMEM; 1296 1297 iter->magic = PROF_MAGIC_ITERATOR; 1298 iter->profile = profile; 1299 iter->names = names; 1300 iter->flags = flags; 1301 iter->file = profile->first_file; 1302 iter->done_idx = done_idx; 1303 iter->node = 0; 1304 iter->num = 0; 1305 *ret_iter = iter; 1306 return 0; 1307} 1308 1309void profile_iterator_free(void **iter_p) 1310{ 1311 struct profile_iterator *iter; 1312 1313 if (!iter_p) 1314 return; 1315 iter = *iter_p; 1316 if (!iter || iter->magic != PROF_MAGIC_ITERATOR) 1317 return; 1318 free(iter); 1319 *iter_p = 0; 1320} 1321 1322/* 1323 * Note: the returned character strings in ret_name and ret_value 1324 * points to the stored character string in the parse string. Before 1325 * this string value is returned to a calling application 1326 * (profile_node_iterator is not an exported interface), it should be 1327 * strdup()'ed. 1328 */ 1329errcode_t profile_node_iterator(void **iter_p, struct profile_node **ret_node, 1330 char **ret_name, char **ret_value) 1331{ 1332 struct profile_iterator *iter = *iter_p; 1333 struct profile_node *section, *p; 1334 const char *const *cpp; 1335 errcode_t retval; 1336 int skip_num = 0; 1337 1338 if (!iter || iter->magic != PROF_MAGIC_ITERATOR) 1339 return PROF_MAGIC_ITERATOR; 1340 if (iter->file && iter->file->magic != PROF_MAGIC_FILE) 1341 return PROF_MAGIC_FILE; 1342 /* 1343 * If the file has changed, then the node pointer is invalid, 1344 * so we'll have search the file again looking for it. 1345 */ 1346 if (iter->node && (iter->file && 1347 iter->file->upd_serial != iter->file_serial)) { 1348 iter->flags &= ~PROFILE_ITER_FINAL_SEEN; 1349 skip_num = iter->num; 1350 iter->node = 0; 1351 } 1352 if (iter->node && iter->node->magic != PROF_MAGIC_NODE) { 1353 return PROF_MAGIC_NODE; 1354 } 1355get_new_file: 1356 if (iter->node == 0) { 1357 if (iter->file == 0 || 1358 (iter->flags & PROFILE_ITER_FINAL_SEEN)) { 1359 profile_iterator_free(iter_p); 1360 if (ret_node) 1361 *ret_node = 0; 1362 if (ret_name) 1363 *ret_name = 0; 1364 if (ret_value) 1365 *ret_value =0; 1366 return 0; 1367 } 1368 if ((retval = profile_update_file(iter->file))) { 1369 if (retval == ENOENT || retval == EACCES) { 1370 /* XXX memory leak? */ 1371 iter->file = iter->file->next; 1372 skip_num = 0; 1373 retval = 0; 1374 goto get_new_file; 1375 } else { 1376 profile_iterator_free(iter_p); 1377 return retval; 1378 } 1379 } 1380 iter->file_serial = iter->file->upd_serial; 1381 /* 1382 * Find the section to list if we are a LIST_SECTION, 1383 * or find the containing section if not. 1384 */ 1385 section = iter->file->root; 1386 for (cpp = iter->names; cpp[iter->done_idx]; cpp++) { 1387 for (p=section->first_child; p; p = p->next) { 1388 if (!strcmp(p->name, *cpp) && !p->value) 1389 break; 1390 } 1391 if (!p) { 1392 section = 0; 1393 break; 1394 } 1395 section = p; 1396 if (p->final) 1397 iter->flags |= PROFILE_ITER_FINAL_SEEN; 1398 } 1399 if (!section) { 1400 iter->file = iter->file->next; 1401 skip_num = 0; 1402 goto get_new_file; 1403 } 1404 iter->name = *cpp; 1405 iter->node = section->first_child; 1406 } 1407 /* 1408 * OK, now we know iter->node is set up correctly. Let's do 1409 * the search. 1410 */ 1411 for (p = iter->node; p; p = p->next) { 1412 if (iter->name && strcmp(p->name, iter->name)) 1413 continue; 1414 if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) && 1415 p->value) 1416 continue; 1417 if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) && 1418 !p->value) 1419 continue; 1420 if (skip_num > 0) { 1421 skip_num--; 1422 continue; 1423 } 1424 if (p->deleted) 1425 continue; 1426 break; 1427 } 1428 iter->num++; 1429 if (!p) { 1430 iter->file = iter->file->next; 1431 iter->node = 0; 1432 skip_num = 0; 1433 goto get_new_file; 1434 } 1435 if ((iter->node = p->next) == NULL) 1436 iter->file = iter->file->next; 1437 if (ret_node) 1438 *ret_node = p; 1439 if (ret_name) 1440 *ret_name = p->name; 1441 if (ret_value) 1442 *ret_value = p->value; 1443 return 0; 1444} 1445 1446 1447/* 1448 * prof_get.c --- routines that expose the public interfaces for 1449 * querying items from the profile. 1450 * 1451 */ 1452 1453/* 1454 * This function only gets the first value from the file; it is a 1455 * helper function for profile_get_string, profile_get_integer, etc. 1456 */ 1457errcode_t profile_get_value(profile_t profile, const char *name, 1458 const char *subname, const char *subsubname, 1459 const char **ret_value) 1460{ 1461 errcode_t retval; 1462 void *state; 1463 char *value; 1464 const char *names[4]; 1465 1466 names[0] = name; 1467 names[1] = subname; 1468 names[2] = subsubname; 1469 names[3] = 0; 1470 1471 if ((retval = profile_iterator_create(profile, names, 1472 PROFILE_ITER_RELATIONS_ONLY, 1473 &state))) 1474 return retval; 1475 1476 if ((retval = profile_node_iterator(&state, 0, 0, &value))) 1477 goto cleanup; 1478 1479 if (value) 1480 *ret_value = value; 1481 else 1482 retval = PROF_NO_RELATION; 1483 1484cleanup: 1485 profile_iterator_free(&state); 1486 return retval; 1487} 1488 1489errcode_t 1490profile_get_string(profile_t profile, const char *name, const char *subname, 1491 const char *subsubname, const char *def_val, 1492 char **ret_string) 1493{ 1494 const char *value; 1495 errcode_t retval; 1496 1497 if (profile) { 1498 retval = profile_get_value(profile, name, subname, 1499 subsubname, &value); 1500 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) 1501 value = def_val; 1502 else if (retval) 1503 return retval; 1504 } else 1505 value = def_val; 1506 1507 if (value) { 1508 *ret_string = malloc(strlen(value)+1); 1509 if (*ret_string == 0) 1510 return ENOMEM; 1511 strcpy(*ret_string, value); 1512 } else 1513 *ret_string = 0; 1514 return 0; 1515} 1516 1517errcode_t 1518profile_get_integer(profile_t profile, const char *name, const char *subname, 1519 const char *subsubname, int def_val, int *ret_int) 1520{ 1521 const char *value; 1522 errcode_t retval; 1523 char *end_value; 1524 long ret_long; 1525 1526 *ret_int = def_val; 1527 if (profile == 0) 1528 return 0; 1529 1530 retval = profile_get_value(profile, name, subname, subsubname, &value); 1531 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { 1532 *ret_int = def_val; 1533 return 0; 1534 } else if (retval) 1535 return retval; 1536 1537 if (value[0] == 0) 1538 /* Empty string is no good. */ 1539 return PROF_BAD_INTEGER; 1540 errno = 0; 1541 ret_long = strtol (value, &end_value, 10); 1542 1543 /* Overflow or underflow. */ 1544 if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0) 1545 return PROF_BAD_INTEGER; 1546 /* Value outside "int" range. */ 1547 if ((long) (int) ret_long != ret_long) 1548 return PROF_BAD_INTEGER; 1549 /* Garbage in string. */ 1550 if (end_value != value + strlen (value)) 1551 return PROF_BAD_INTEGER; 1552 1553 1554 *ret_int = ret_long; 1555 return 0; 1556} 1557 1558errcode_t 1559profile_get_uint(profile_t profile, const char *name, const char *subname, 1560 const char *subsubname, unsigned int def_val, 1561 unsigned int *ret_int) 1562{ 1563 const char *value; 1564 errcode_t retval; 1565 char *end_value; 1566 unsigned long ret_long; 1567 1568 *ret_int = def_val; 1569 if (profile == 0) 1570 return 0; 1571 1572 retval = profile_get_value(profile, name, subname, subsubname, &value); 1573 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { 1574 *ret_int = def_val; 1575 return 0; 1576 } else if (retval) 1577 return retval; 1578 1579 if (value[0] == 0) 1580 /* Empty string is no good. */ 1581 return PROF_BAD_INTEGER; 1582 errno = 0; 1583 ret_long = strtoul (value, &end_value, 10); 1584 1585 /* Overflow or underflow. */ 1586 if ((ret_long == ULONG_MAX) && errno != 0) 1587 return PROF_BAD_INTEGER; 1588 /* Value outside "int" range. */ 1589 if ((unsigned long) (unsigned int) ret_long != ret_long) 1590 return PROF_BAD_INTEGER; 1591 /* Garbage in string. */ 1592 if (end_value != value + strlen (value)) 1593 return PROF_BAD_INTEGER; 1594 1595 *ret_int = ret_long; 1596 return 0; 1597} 1598 1599static const char *const conf_yes[] = { 1600 "y", "yes", "true", "t", "1", "on", 1601 0, 1602}; 1603 1604static const char *const conf_no[] = { 1605 "n", "no", "false", "nil", "0", "off", 1606 0, 1607}; 1608 1609static errcode_t 1610profile_parse_boolean(const char *s, int *ret_boolean) 1611{ 1612 const char *const *p; 1613 1614 if (ret_boolean == NULL) 1615 return PROF_EINVAL; 1616 1617 for(p=conf_yes; *p; p++) { 1618 if (!strcasecmp(*p,s)) { 1619 *ret_boolean = 1; 1620 return 0; 1621 } 1622 } 1623 1624 for(p=conf_no; *p; p++) { 1625 if (!strcasecmp(*p,s)) { 1626 *ret_boolean = 0; 1627 return 0; 1628 } 1629 } 1630 1631 return PROF_BAD_BOOLEAN; 1632} 1633 1634errcode_t 1635profile_get_boolean(profile_t profile, const char *name, const char *subname, 1636 const char *subsubname, int def_val, int *ret_boolean) 1637{ 1638 const char *value; 1639 errcode_t retval; 1640 1641 if (profile == 0) { 1642 *ret_boolean = def_val; 1643 return 0; 1644 } 1645 1646 retval = profile_get_value(profile, name, subname, subsubname, &value); 1647 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { 1648 *ret_boolean = def_val; 1649 return 0; 1650 } else if (retval) 1651 return retval; 1652 1653 return profile_parse_boolean (value, ret_boolean); 1654} 1655 1656errcode_t 1657profile_iterator(void **iter_p, char **ret_name, char **ret_value) 1658{ 1659 char *name, *value; 1660 errcode_t retval; 1661 1662 retval = profile_node_iterator(iter_p, 0, &name, &value); 1663 if (retval) 1664 return retval; 1665 1666 if (ret_name) { 1667 if (name) { 1668 *ret_name = malloc(strlen(name)+1); 1669 if (!*ret_name) 1670 return ENOMEM; 1671 strcpy(*ret_name, name); 1672 } else 1673 *ret_name = 0; 1674 } 1675 if (ret_value) { 1676 if (value) { 1677 *ret_value = malloc(strlen(value)+1); 1678 if (!*ret_value) { 1679 if (ret_name) { 1680 free(*ret_name); 1681 *ret_name = 0; 1682 } 1683 return ENOMEM; 1684 } 1685 strcpy(*ret_value, value); 1686 } else 1687 *ret_value = 0; 1688 } 1689 return 0; 1690} 1691 1692#ifdef DEBUG_PROGRAM 1693 1694/* 1695 * test_profile.c --- testing program for the profile routine 1696 */ 1697 1698#include "argv_parse.h" 1699#include "profile_helpers.h" 1700 1701const char *program_name = "test_profile"; 1702 1703#define PRINT_VALUE 1 1704#define PRINT_VALUES 2 1705 1706static void do_cmd(profile_t profile, char **argv) 1707{ 1708 errcode_t retval; 1709 const char **names, *value; 1710 char **values, **cpp; 1711 char *cmd; 1712 int print_status; 1713 1714 cmd = *(argv); 1715 names = (const char **) argv + 1; 1716 print_status = 0; 1717 retval = 0; 1718 if (cmd == 0) 1719 return; 1720 if (!strcmp(cmd, "query")) { 1721 retval = profile_get_values(profile, names, &values); 1722 print_status = PRINT_VALUES; 1723 } else if (!strcmp(cmd, "query1")) { 1724 const char *name = 0; 1725 const char *subname = 0; 1726 const char *subsubname = 0; 1727 1728 name = names[0]; 1729 if (name) 1730 subname = names[1]; 1731 if (subname) 1732 subsubname = names[2]; 1733 if (subsubname && names[3]) { 1734 fprintf(stderr, 1735 "Only 3 levels are allowed with query1\n"); 1736 retval = EINVAL; 1737 } else 1738 retval = profile_get_value(profile, name, subname, 1739 subsubname, &value); 1740 print_status = PRINT_VALUE; 1741 } else if (!strcmp(cmd, "list_sections")) { 1742 retval = profile_get_subsection_names(profile, names, 1743 &values); 1744 print_status = PRINT_VALUES; 1745 } else if (!strcmp(cmd, "list_relations")) { 1746 retval = profile_get_relation_names(profile, names, 1747 &values); 1748 print_status = PRINT_VALUES; 1749 } else if (!strcmp(cmd, "dump")) { 1750 retval = profile_write_tree_file 1751 (profile->first_file->root, stdout); 1752#if 0 1753 } else if (!strcmp(cmd, "clear")) { 1754 retval = profile_clear_relation(profile, names); 1755 } else if (!strcmp(cmd, "update")) { 1756 retval = profile_update_relation(profile, names+2, 1757 *names, *(names+1)); 1758#endif 1759 } else if (!strcmp(cmd, "verify")) { 1760 retval = profile_verify_node 1761 (profile->first_file->root); 1762#if 0 1763 } else if (!strcmp(cmd, "rename_section")) { 1764 retval = profile_rename_section(profile, names+1, *names); 1765 } else if (!strcmp(cmd, "add")) { 1766 value = *names; 1767 if (strcmp(value, "NULL") == 0) 1768 value = NULL; 1769 retval = profile_add_relation(profile, names+1, value); 1770 } else if (!strcmp(cmd, "flush")) { 1771 retval = profile_flush(profile); 1772#endif 1773 } else { 1774 printf("Invalid command.\n"); 1775 } 1776 if (retval) { 1777 com_err(cmd, retval, ""); 1778 print_status = 0; 1779 } 1780 switch (print_status) { 1781 case PRINT_VALUE: 1782 printf("%s\n", value); 1783 break; 1784 case PRINT_VALUES: 1785 for (cpp = values; *cpp; cpp++) 1786 printf("%s\n", *cpp); 1787 profile_free_list(values); 1788 break; 1789 } 1790} 1791 1792static void do_batchmode(profile_t profile) 1793{ 1794 int argc, ret; 1795 char **argv; 1796 char buf[256]; 1797 1798 while (!feof(stdin)) { 1799 if (fgets(buf, sizeof(buf), stdin) == NULL) 1800 break; 1801 printf(">%s", buf); 1802 ret = argv_parse(buf, &argc, &argv); 1803 if (ret != 0) { 1804 printf("Argv_parse returned %d!\n", ret); 1805 continue; 1806 } 1807 do_cmd(profile, argv); 1808 printf("\n"); 1809 argv_free(argv); 1810 } 1811 profile_release(profile); 1812 exit(0); 1813 1814} 1815 1816void syntax_err_report(const char *filename, long err, int line_num) 1817{ 1818 fprintf(stderr, "Syntax error in %s, line number %d: %s\n", 1819 filename, line_num, error_message(err)); 1820 exit(1); 1821} 1822 1823const char *default_str = "[foo]\n\tbar=quux\n\tsub = {\n\t\twin = true\n}\n"; 1824 1825int main(int argc, char **argv) 1826{ 1827 profile_t profile; 1828 long retval; 1829 char *cmd; 1830 1831 if (argc < 2) { 1832 fprintf(stderr, "Usage: %s filename [cmd argset]\n", program_name); 1833 exit(1); 1834 } 1835 1836 initialize_prof_error_table(); 1837 1838 profile_set_syntax_err_cb(syntax_err_report); 1839 1840 retval = profile_init_path(argv[1], &profile); 1841 if (retval) { 1842 com_err(program_name, retval, "while initializing profile"); 1843 exit(1); 1844 } 1845 retval = profile_set_default(profile, default_str); 1846 if (retval) { 1847 com_err(program_name, retval, "while setting default"); 1848 exit(1); 1849 } 1850 1851 cmd = *(argv+2); 1852 if (!cmd || !strcmp(cmd, "batch")) 1853 do_batchmode(profile); 1854 else 1855 do_cmd(profile, argv+2); 1856 profile_release(profile); 1857 1858 return 0; 1859} 1860 1861#endif 1862