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