| /* |
| * config.c : reading configuration information |
| * |
| * ==================================================================== |
| * Copyright (c) 2000-2002 CollabNet. All rights reserved. |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at http://subversion.tigris.org/license-1.html. |
| * If newer versions of this license are posted there, you may use a |
| * newer version instead, at your option. |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals. For exact contribution history, see the revision |
| * history and logs, available at http://subversion.tigris.org/. |
| * ==================================================================== |
| */ |
| |
| |
| |
| #define APR_WANT_STRFUNC |
| #define APR_WANT_MEMFUNC |
| #include <apr_want.h> |
| |
| #include <apr_lib.h> |
| #include <apr_user.h> |
| #include "svn_error.h" |
| #include "config_impl.h" |
| |
| |
| |
| |
| /* Section table entries. */ |
| typedef struct cfg_section_t cfg_section_t; |
| struct cfg_section_t |
| { |
| /* The section name. */ |
| const char *name; |
| |
| /* The section name, converted into a hash key. */ |
| const char *hash_key; |
| |
| /* Table of cfg_option_t's. */ |
| apr_hash_t *options; |
| }; |
| |
| |
| /* Option table entries. */ |
| typedef struct cfg_option_t cfg_option_t; |
| struct cfg_option_t |
| { |
| /* The option name. */ |
| const char *name; |
| |
| /* The option name, converted into a hash key. */ |
| const char *hash_key; |
| |
| /* The unexpanded option value. */ |
| const char *value; |
| |
| /* The expanded option value. */ |
| const char *x_value; |
| |
| /* Expansion flag. If this is TRUE, this value has already been expanded. |
| In this case, if x_value is NULL, no expansions were necessary, |
| and value should be used directly. */ |
| svn_boolean_t expanded; |
| }; |
| |
| |
| |
| svn_error_t * |
| svn_config_read (svn_config_t **cfgp, const char *file, |
| svn_boolean_t must_exist, apr_pool_t *pool) |
| { |
| svn_config_t *cfg = apr_palloc (pool, sizeof (*cfg)); |
| svn_error_t *err; |
| |
| cfg->sections = apr_hash_make (pool); |
| cfg->pool = pool; |
| cfg->x_pool = svn_pool_create (pool); |
| cfg->x_values = FALSE; |
| cfg->tmp_key = svn_stringbuf_create ("", pool); |
| |
| /* Yes, this is platform-specific code in Subversion, but there's no |
| practical way to migrate it into APR, as it's simultaneously |
| Subversion-specific and Windows-specific. Even if we eventually |
| want to have APR offer a generic config-reading interface, it |
| makes sense to test it here first and migrate it later. */ |
| #ifdef SVN_WIN32 |
| if (0 == strncmp (file, SVN_REGISTRY_PREFIX, SVN_REGISTRY_PREFIX_LEN)) |
| err = svn_config__parse_registry (cfg, file + SVN_REGISTRY_PREFIX_LEN, |
| must_exist); |
| else |
| #endif /* SVN_WIN32 */ |
| err = svn_config__parse_file (cfg, file, must_exist); |
| |
| if (err != SVN_NO_ERROR) |
| return err; |
| else |
| *cfgp = cfg; |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| /* Read various configuration sources into *CFGP, in this order, with |
| * later reads overriding the results of earlier ones: |
| * |
| * 1. SYS_REGISTRY_PATH (only on SVN_WIN32) |
| * |
| * 2. USR_REGISTRY_PATH (only on SVN_WIN32) |
| * |
| * 2. SYS_FILE_PATH (everywhere, but ignored if NULL) |
| * |
| * 3. USR_FILE_PATH (everywhere, but ignored if NULL) |
| * |
| * Allocate *CFGP in POOL. Even if no configurations are read, |
| * allocate an empty *CFGP. |
| */ |
| static svn_error_t * |
| read_all (svn_config_t **cfgp, |
| #ifdef SVN_WIN32 |
| const char *sys_registry_path, |
| const char *usr_registry_path, |
| #endif /* SVN_WIN32 */ |
| const char *sys_file_path, |
| const char *usr_file_path, |
| apr_pool_t *pool) |
| { |
| svn_boolean_t red_config = FALSE; /* "red" is the past tense of "read" */ |
| |
| #ifdef SVN_WIN32 |
| SVN_ERR (svn_config_read (cfgp, sys_registry_path, FALSE, pool)); |
| red_config = TRUE; |
| SVN_ERR (svn_config_merge (*cfgp, usr_registry_path, FALSE)); |
| #endif /* SVN_WIN32 */ |
| |
| if (sys_file_path) |
| { |
| if (red_config) |
| SVN_ERR (svn_config_merge (*cfgp, sys_file_path, FALSE)); |
| else |
| { |
| svn_config_read (cfgp, sys_file_path, FALSE, pool); |
| red_config = TRUE; |
| } |
| } |
| |
| if (usr_file_path) |
| { |
| if (red_config) |
| SVN_ERR (svn_config_merge (*cfgp, usr_file_path, FALSE)); |
| else |
| { |
| SVN_ERR (svn_config_read (cfgp, usr_file_path, FALSE, pool)); |
| red_config = TRUE; |
| } |
| } |
| |
| if (! red_config) |
| *cfgp = NULL; |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| svn_error_t * |
| svn_config_read_proxies (svn_config_t **cfgp, apr_pool_t *pool) |
| { |
| svn_error_t *err; |
| const char *usr_cfg_path, *sys_cfg_path; |
| |
| SVN_ERR (svn_config__sys_config_path (&sys_cfg_path, |
| SVN_CONFIG__USR_PROXY_FILE, |
| pool)); |
| |
| SVN_ERR (svn_config__user_config_path (&usr_cfg_path, |
| SVN_CONFIG__USR_PROXY_FILE, |
| pool)); |
| |
| /* Can't use #ifdefs inside SVN_ERR, so catch error manually */ |
| err = read_all (cfgp, |
| #ifdef SVN_WIN32 |
| SVN_REGISTRY_SYS_CONFIG_PROXY_PATH, |
| SVN_REGISTRY_USR_CONFIG_PROXY_PATH, |
| #endif /* SVN_WIN32 */ |
| sys_cfg_path, |
| usr_cfg_path, |
| pool); |
| |
| return err; |
| } |
| |
| |
| |
| /* Iterate through CFG, passing BATON to CALLBACK for every (SECTION, OPTION) |
| pair. Stop if CALLBACK returns TRUE. Allocate from POOL. */ |
| static void |
| for_each_option (svn_config_t *cfg, void *baton, apr_pool_t *pool, |
| svn_boolean_t callback (void *same_baton, |
| cfg_section_t *section, |
| cfg_option_t *option)) |
| { |
| apr_hash_index_t *sec_ndx; |
| for (sec_ndx = apr_hash_first (pool, cfg->sections); |
| sec_ndx != NULL; |
| sec_ndx = apr_hash_next (sec_ndx)) |
| { |
| void *sec_ptr; |
| cfg_section_t *sec; |
| apr_hash_index_t *opt_ndx; |
| |
| apr_hash_this (sec_ndx, NULL, NULL, &sec_ptr); |
| sec = sec_ptr; |
| |
| for (opt_ndx = apr_hash_first (pool, sec->options); |
| opt_ndx != NULL; |
| opt_ndx = apr_hash_next (opt_ndx)) |
| { |
| void *opt_ptr; |
| cfg_option_t *opt; |
| |
| apr_hash_this (opt_ndx, NULL, NULL, &opt_ptr); |
| opt = opt_ptr; |
| |
| if (callback (baton, sec, opt)) |
| return; |
| } |
| } |
| } |
| |
| |
| |
| static svn_boolean_t |
| merge_callback (void *baton, cfg_section_t *section, cfg_option_t *option) |
| { |
| svn_config_set (baton, section->name, option->name, option->value); |
| return FALSE; |
| } |
| |
| svn_error_t * |
| svn_config_merge (svn_config_t *cfg, const char *file, |
| svn_boolean_t must_exist) |
| { |
| /* The original config hash shouldn't change if there's an error |
| while reading the confguration, so read into a temporary table. |
| ### We could use a tmp subpool for this, since merge_cfg is going |
| to be tossed afterwards. Premature optimization, though? */ |
| svn_config_t *merge_cfg; |
| SVN_ERR (svn_config_read (&merge_cfg, file, must_exist, cfg->pool)); |
| |
| /* Now copy the new options into the original table. */ |
| for_each_option (merge_cfg, cfg, merge_cfg->pool, merge_callback); |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| /* Remove variable expansions from CFG. Walk through the options tree, |
| killing all expanded values, then clear the expanded value pool. */ |
| static svn_boolean_t |
| rmex_callback (void *baton, cfg_section_t *section, cfg_option_t *option) |
| { |
| /* Only clear the `expanded' flag if the value actually contains |
| variable expansions. */ |
| if (option->expanded && option->x_value != NULL) |
| { |
| option->x_value = NULL; |
| option->expanded = FALSE; |
| } |
| |
| (void)(baton); /* Unused parameter. */ |
| (void)(section); /* Unused parameter. */ |
| return FALSE; |
| } |
| |
| static void |
| remove_expansions (svn_config_t *cfg) |
| { |
| if (!cfg->x_values) |
| return; |
| |
| for_each_option (cfg, NULL, cfg->x_pool, rmex_callback); |
| apr_pool_clear (cfg->x_pool); |
| cfg->x_values = FALSE; |
| } |
| |
| |
| |
| /* Canonicalize a string for hashing. Modifies KEY in place. */ |
| static APR_INLINE char * |
| make_hash_key (char *key) |
| { |
| register char *p; |
| for (p = key; *p != 0; ++p) |
| *p = apr_tolower (*p); |
| return key; |
| } |
| |
| |
| /* Return a pointer to an option in CFG, or NULL if it doesn't exist. |
| if SECTIONP is non-null, return a pointer to the option's section. |
| OPTION may be NULL. */ |
| static cfg_option_t * |
| find_option (svn_config_t *cfg, const char *section, const char *option, |
| cfg_section_t **sectionp) |
| { |
| void *sec_ptr; |
| |
| /* Canonicalize the hash key */ |
| svn_stringbuf_set (cfg->tmp_key, section); |
| make_hash_key (cfg->tmp_key->data); |
| |
| sec_ptr = apr_hash_get (cfg->sections, cfg->tmp_key->data, |
| cfg->tmp_key->len); |
| if (sectionp != NULL) |
| *sectionp = sec_ptr; |
| |
| if (sec_ptr != NULL && option != NULL) |
| { |
| cfg_section_t *sec = sec_ptr; |
| |
| /* Canonicalize the option key */ |
| svn_stringbuf_set (cfg->tmp_key, option); |
| make_hash_key (cfg->tmp_key->data); |
| |
| return apr_hash_get (sec->options, cfg->tmp_key->data, |
| cfg->tmp_key->len); |
| } |
| |
| return NULL; |
| } |
| |
| |
| /* Set *VALUEP according to the OPT's value. */ |
| static void |
| make_string_from_option (const char **valuep, |
| svn_config_t *cfg, cfg_option_t *opt) |
| { |
| /* ### TODO: Expand the option's value */ |
| (void)(cfg); |
| |
| /* For legacy reasons, the cfg is still using counted-length strings |
| internally. But the public interfaces just use null-terminated |
| C strings now, so below we ignore length and use only data. */ |
| |
| if (opt->x_value) |
| *valuep = opt->x_value; |
| else |
| *valuep = opt->value; |
| } |
| |
| |
| |
| void |
| svn_config_get (svn_config_t *cfg, const char **valuep, |
| const char *section, const char *option, |
| const char *default_value) |
| { |
| cfg_option_t *opt = find_option (cfg, section, option, NULL); |
| if (opt != NULL) |
| make_string_from_option (valuep, cfg, opt); |
| else |
| *valuep = default_value; /* ### TODO: Expand default_value */ |
| } |
| |
| |
| |
| void |
| svn_config_set (svn_config_t *cfg, |
| const char *section, const char *option, |
| const char *value) |
| { |
| cfg_section_t *sec; |
| cfg_option_t *opt; |
| |
| remove_expansions (cfg); |
| |
| opt = find_option (cfg, section, option, &sec); |
| if (opt != NULL) |
| { |
| /* Replace the option's value. */ |
| opt->value = apr_pstrdup (cfg->pool, value); |
| opt->expanded = FALSE; |
| return; |
| } |
| |
| /* Create a new option */ |
| opt = apr_palloc (cfg->pool, sizeof (*opt)); |
| opt->name = apr_pstrdup (cfg->pool, option); |
| opt->hash_key = make_hash_key (apr_pstrdup (cfg->pool, option)); |
| |
| opt->value = apr_pstrdup (cfg->pool, value); |
| opt->x_value = NULL; |
| opt->expanded = FALSE; |
| |
| if (sec == NULL) |
| { |
| /* Even the section doesn't exist. Create it. */ |
| sec = apr_palloc (cfg->pool, sizeof (*sec)); |
| sec->name = apr_pstrdup (cfg->pool, section); |
| sec->hash_key = make_hash_key (apr_pstrdup (cfg->pool, section)); |
| sec->options = apr_hash_make (cfg->pool); |
| apr_hash_set (cfg->sections, sec->hash_key, APR_HASH_KEY_STRING, sec); |
| } |
| |
| apr_hash_set (sec->options, opt->hash_key, APR_HASH_KEY_STRING, opt); |
| } |
| |
| |
| |
| int |
| svn_config_enumerate (svn_config_t *cfg, const char *section, |
| svn_config_enumerator_t callback, void *baton) |
| { |
| cfg_section_t *sec; |
| apr_hash_index_t *opt_ndx; |
| int count; |
| |
| find_option (cfg, section, NULL, &sec); |
| if (sec == NULL) |
| return 0; |
| |
| count = 0; |
| for (opt_ndx = apr_hash_first (cfg->x_pool, sec->options); |
| opt_ndx != NULL; |
| opt_ndx = apr_hash_next (opt_ndx)) |
| { |
| void *opt_ptr; |
| cfg_option_t *opt; |
| const char *temp_value; |
| |
| apr_hash_this (opt_ndx, NULL, NULL, &opt_ptr); |
| opt = opt_ptr; |
| |
| ++count; |
| make_string_from_option (&temp_value, cfg, opt); |
| if (!callback (opt->name, temp_value, baton)) |
| break; |
| } |
| |
| return count; |
| } |
| |
| |
| |
| /* |
| * local variables: |
| * eval: (load-file "../../tools/dev/svn-dev.el") |
| * end: |
| */ |