xref: /trueos/contrib/subversion/subversion/libsvn_subr/config.c (revision 1e5107043085964bba002e6a91aa04e864f74d02)
1 /*
2  * config.c :  reading configuration information
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23 
24 
25 
26 #define APR_WANT_STRFUNC
27 #define APR_WANT_MEMFUNC
28 #include <apr_want.h>
29 
30 #include <apr_general.h>
31 #include <apr_lib.h>
32 #include "svn_hash.h"
33 #include "svn_error.h"
34 #include "svn_pools.h"
35 #include "config_impl.h"
36 
37 #include "svn_private_config.h"
38 #include "private/svn_dep_compat.h"
39 
40 
41 
42 
43 /* Section table entries. */
44 typedef struct cfg_section_t cfg_section_t;
45 struct cfg_section_t
46 {
47   /* The section name. */
48   const char *name;
49 
50   /* Table of cfg_option_t's. */
51   apr_hash_t *options;
52 };
53 
54 
55 /* Option table entries. */
56 typedef struct cfg_option_t cfg_option_t;
57 struct cfg_option_t
58 {
59   /* The option name. */
60   const char *name;
61 
62   /* The option name, converted into a hash key. */
63   const char *hash_key;
64 
65   /* The unexpanded option value. */
66   const char *value;
67 
68   /* The expanded option value. */
69   const char *x_value;
70 
71   /* Expansion flag. If this is TRUE, this value has already been expanded.
72      In this case, if x_value is NULL, no expansions were necessary,
73      and value should be used directly. */
74   svn_boolean_t expanded;
75 };
76 
77 
78 
79 svn_error_t *
svn_config_create2(svn_config_t ** cfgp,svn_boolean_t section_names_case_sensitive,svn_boolean_t option_names_case_sensitive,apr_pool_t * result_pool)80 svn_config_create2(svn_config_t **cfgp,
81                    svn_boolean_t section_names_case_sensitive,
82                    svn_boolean_t option_names_case_sensitive,
83                    apr_pool_t *result_pool)
84 {
85   svn_config_t *cfg = apr_palloc(result_pool, sizeof(*cfg));
86 
87   cfg->sections = apr_hash_make(result_pool);
88   cfg->pool = result_pool;
89   cfg->x_pool = svn_pool_create(result_pool);
90   cfg->x_values = FALSE;
91   cfg->tmp_key = svn_stringbuf_create_empty(result_pool);
92   cfg->tmp_value = svn_stringbuf_create_empty(result_pool);
93   cfg->section_names_case_sensitive = section_names_case_sensitive;
94   cfg->option_names_case_sensitive = option_names_case_sensitive;
95 
96   *cfgp = cfg;
97   return SVN_NO_ERROR;
98 }
99 
100 svn_error_t *
svn_config_read3(svn_config_t ** cfgp,const char * file,svn_boolean_t must_exist,svn_boolean_t section_names_case_sensitive,svn_boolean_t option_names_case_sensitive,apr_pool_t * result_pool)101 svn_config_read3(svn_config_t **cfgp, const char *file,
102                  svn_boolean_t must_exist,
103                  svn_boolean_t section_names_case_sensitive,
104                  svn_boolean_t option_names_case_sensitive,
105                  apr_pool_t *result_pool)
106 {
107   svn_config_t *cfg;
108   svn_error_t *err;
109 
110   SVN_ERR(svn_config_create2(&cfg,
111                              section_names_case_sensitive,
112                              option_names_case_sensitive,
113                              result_pool));
114 
115   /* Yes, this is platform-specific code in Subversion, but there's no
116      practical way to migrate it into APR, as it's simultaneously
117      Subversion-specific and Windows-specific.  Even if we eventually
118      want to have APR offer a generic config-reading interface, it
119      makes sense to test it here first and migrate it later. */
120 #ifdef WIN32
121   if (0 == strncmp(file, SVN_REGISTRY_PREFIX, SVN_REGISTRY_PREFIX_LEN))
122     err = svn_config__parse_registry(cfg, file + SVN_REGISTRY_PREFIX_LEN,
123                                      must_exist, result_pool);
124   else
125 #endif /* WIN32 */
126     err = svn_config__parse_file(cfg, file, must_exist, result_pool);
127 
128   if (err != SVN_NO_ERROR)
129     return err;
130   else
131     *cfgp = cfg;
132 
133   return SVN_NO_ERROR;
134 }
135 
136 svn_error_t *
svn_config_parse(svn_config_t ** cfgp,svn_stream_t * stream,svn_boolean_t section_names_case_sensitive,svn_boolean_t option_names_case_sensitive,apr_pool_t * result_pool)137 svn_config_parse(svn_config_t **cfgp, svn_stream_t *stream,
138                  svn_boolean_t section_names_case_sensitive,
139                  svn_boolean_t option_names_case_sensitive,
140                  apr_pool_t *result_pool)
141 {
142   svn_config_t *cfg;
143   svn_error_t *err;
144   apr_pool_t *scratch_pool = svn_pool_create(result_pool);
145 
146   err = svn_config_create2(&cfg,
147                            section_names_case_sensitive,
148                            option_names_case_sensitive,
149                            result_pool);
150 
151   if (err == SVN_NO_ERROR)
152     err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
153 
154   if (err == SVN_NO_ERROR)
155     *cfgp = cfg;
156 
157   svn_pool_destroy(scratch_pool);
158 
159   return err;
160 }
161 
162 /* Read various configuration sources into *CFGP, in this order, with
163  * later reads overriding the results of earlier ones:
164  *
165  *    1. SYS_REGISTRY_PATH   (only on Win32, but ignored if NULL)
166  *
167  *    2. SYS_FILE_PATH       (everywhere, but ignored if NULL)
168  *
169  *    3. USR_REGISTRY_PATH   (only on Win32, but ignored if NULL)
170  *
171  *    4. USR_FILE_PATH       (everywhere, but ignored if NULL)
172  *
173  * Allocate *CFGP in POOL.  Even if no configurations are read,
174  * allocate an empty *CFGP.
175  */
176 static svn_error_t *
read_all(svn_config_t ** cfgp,const char * sys_registry_path,const char * usr_registry_path,const char * sys_file_path,const char * usr_file_path,apr_pool_t * pool)177 read_all(svn_config_t **cfgp,
178          const char *sys_registry_path,
179          const char *usr_registry_path,
180          const char *sys_file_path,
181          const char *usr_file_path,
182          apr_pool_t *pool)
183 {
184   svn_boolean_t red_config = FALSE;  /* "red" is the past tense of "read" */
185 
186   /*** Read system-wide configurations first... ***/
187 
188 #ifdef WIN32
189   if (sys_registry_path)
190     {
191       SVN_ERR(svn_config_read2(cfgp, sys_registry_path, FALSE, FALSE, pool));
192       red_config = TRUE;
193     }
194 #endif /* WIN32 */
195 
196   if (sys_file_path)
197     {
198       if (red_config)
199         SVN_ERR(svn_config_merge(*cfgp, sys_file_path, FALSE));
200       else
201         {
202           SVN_ERR(svn_config_read3(cfgp, sys_file_path,
203                                    FALSE, FALSE, FALSE, pool));
204           red_config = TRUE;
205         }
206     }
207 
208   /*** ...followed by per-user configurations. ***/
209 
210 #ifdef WIN32
211   if (usr_registry_path)
212     {
213       if (red_config)
214         SVN_ERR(svn_config_merge(*cfgp, usr_registry_path, FALSE));
215       else
216         {
217           SVN_ERR(svn_config_read2(cfgp, usr_registry_path,
218                                    FALSE, FALSE, pool));
219           red_config = TRUE;
220         }
221     }
222 #endif /* WIN32 */
223 
224   if (usr_file_path)
225     {
226       if (red_config)
227         SVN_ERR(svn_config_merge(*cfgp, usr_file_path, FALSE));
228       else
229         {
230           SVN_ERR(svn_config_read3(cfgp, usr_file_path,
231                                    FALSE, FALSE, FALSE, pool));
232           red_config = TRUE;
233         }
234     }
235 
236   if (! red_config)
237     SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
238 
239   return SVN_NO_ERROR;
240 }
241 
242 
243 /* CONFIG_DIR provides an override for the default behavior of reading
244    the default set of overlay files described by read_all()'s doc
245    string. */
246 static svn_error_t *
get_category_config(svn_config_t ** cfg,const char * config_dir,const char * category,apr_pool_t * pool)247 get_category_config(svn_config_t **cfg,
248                     const char *config_dir,
249                     const char *category,
250                     apr_pool_t *pool)
251 {
252   const char *usr_reg_path = NULL, *sys_reg_path = NULL;
253   const char *usr_cfg_path, *sys_cfg_path;
254   svn_error_t *err = NULL;
255 
256   *cfg = NULL;
257 
258   if (! config_dir)
259     {
260 #ifdef WIN32
261       sys_reg_path = apr_pstrcat(pool, SVN_REGISTRY_SYS_CONFIG_PATH,
262                                  category, NULL);
263       usr_reg_path = apr_pstrcat(pool, SVN_REGISTRY_USR_CONFIG_PATH,
264                                  category, NULL);
265 #endif /* WIN32 */
266 
267       err = svn_config__sys_config_path(&sys_cfg_path, category, pool);
268       if ((err) && (err->apr_err == SVN_ERR_BAD_FILENAME))
269         {
270           sys_cfg_path = NULL;
271           svn_error_clear(err);
272         }
273       else if (err)
274         return err;
275     }
276   else
277     sys_cfg_path = NULL;
278 
279   SVN_ERR(svn_config_get_user_config_path(&usr_cfg_path, config_dir, category,
280                                           pool));
281   return read_all(cfg, sys_reg_path, usr_reg_path,
282                   sys_cfg_path, usr_cfg_path, pool);
283 }
284 
285 
286 svn_error_t *
svn_config_get_config(apr_hash_t ** cfg_hash,const char * config_dir,apr_pool_t * pool)287 svn_config_get_config(apr_hash_t **cfg_hash,
288                       const char *config_dir,
289                       apr_pool_t *pool)
290 {
291   svn_config_t *cfg;
292   *cfg_hash = apr_hash_make(pool);
293 
294 #define CATLEN (sizeof(SVN_CONFIG_CATEGORY_SERVERS) - 1)
295   SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_SERVERS,
296                               pool));
297   if (cfg)
298     apr_hash_set(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, CATLEN, cfg);
299 #undef CATLEN
300 
301 #define CATLEN (sizeof(SVN_CONFIG_CATEGORY_CONFIG) - 1)
302   SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_CONFIG,
303                               pool));
304   if (cfg)
305     apr_hash_set(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, CATLEN, cfg);
306 #undef CATLEN
307 
308   return SVN_NO_ERROR;
309 }
310 
311 
312 
313 /* Iterate through CFG, passing BATON to CALLBACK for every (SECTION, OPTION)
314    pair.  Stop if CALLBACK returns TRUE.  Allocate from POOL. */
315 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))316 for_each_option(svn_config_t *cfg, void *baton, apr_pool_t *pool,
317                 svn_boolean_t callback(void *same_baton,
318                                        cfg_section_t *section,
319                                        cfg_option_t *option))
320 {
321   apr_hash_index_t *sec_ndx;
322   for (sec_ndx = apr_hash_first(pool, cfg->sections);
323        sec_ndx != NULL;
324        sec_ndx = apr_hash_next(sec_ndx))
325     {
326       void *sec_ptr;
327       cfg_section_t *sec;
328       apr_hash_index_t *opt_ndx;
329 
330       apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
331       sec = sec_ptr;
332 
333       for (opt_ndx = apr_hash_first(pool, sec->options);
334            opt_ndx != NULL;
335            opt_ndx = apr_hash_next(opt_ndx))
336         {
337           void *opt_ptr;
338           cfg_option_t *opt;
339 
340           apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
341           opt = opt_ptr;
342 
343           if (callback(baton, sec, opt))
344             return;
345         }
346     }
347 }
348 
349 
350 
351 static svn_boolean_t
merge_callback(void * baton,cfg_section_t * section,cfg_option_t * option)352 merge_callback(void *baton, cfg_section_t *section, cfg_option_t *option)
353 {
354   svn_config_set(baton, section->name, option->name, option->value);
355   return FALSE;
356 }
357 
358 svn_error_t *
svn_config_merge(svn_config_t * cfg,const char * file,svn_boolean_t must_exist)359 svn_config_merge(svn_config_t *cfg, const char *file,
360                  svn_boolean_t must_exist)
361 {
362   /* The original config hash shouldn't change if there's an error
363      while reading the confguration, so read into a temporary table.
364      ### We could use a tmp subpool for this, since merge_cfg is going
365      to be tossed afterwards.  Premature optimization, though? */
366   svn_config_t *merge_cfg;
367   SVN_ERR(svn_config_read3(&merge_cfg, file, must_exist,
368                            cfg->section_names_case_sensitive,
369                            cfg->option_names_case_sensitive,
370                            cfg->pool));
371 
372   /* Now copy the new options into the original table. */
373   for_each_option(merge_cfg, cfg, merge_cfg->pool, merge_callback);
374   return SVN_NO_ERROR;
375 }
376 
377 
378 
379 /* Remove variable expansions from CFG.  Walk through the options tree,
380    killing all expanded values, then clear the expanded value pool. */
381 static svn_boolean_t
rmex_callback(void * baton,cfg_section_t * section,cfg_option_t * option)382 rmex_callback(void *baton, cfg_section_t *section, cfg_option_t *option)
383 {
384   /* Only clear the `expanded' flag if the value actually contains
385      variable expansions. */
386   if (option->expanded && option->x_value != NULL)
387     {
388       option->x_value = NULL;
389       option->expanded = FALSE;
390     }
391 
392   return FALSE;
393 }
394 
395 static void
remove_expansions(svn_config_t * cfg)396 remove_expansions(svn_config_t *cfg)
397 {
398   if (!cfg->x_values)
399     return;
400 
401   for_each_option(cfg, NULL, cfg->x_pool, rmex_callback);
402   svn_pool_clear(cfg->x_pool);
403   cfg->x_values = FALSE;
404 }
405 
406 
407 
408 /* Canonicalize a string for hashing.  Modifies KEY in place. */
409 static APR_INLINE char *
make_hash_key(char * key)410 make_hash_key(char *key)
411 {
412   register char *p;
413   for (p = key; *p != 0; ++p)
414     *p = (char)apr_tolower(*p);
415   return key;
416 }
417 
418 
419 /* Return a pointer to an option in CFG, or NULL if it doesn't exist.
420    if SECTIONP is non-null, return a pointer to the option's section.
421    OPTION may be NULL. */
422 static cfg_option_t *
find_option(svn_config_t * cfg,const char * section,const char * option,cfg_section_t ** sectionp)423 find_option(svn_config_t *cfg, const char *section, const char *option,
424             cfg_section_t **sectionp)
425 {
426   void *sec_ptr;
427 
428   /* Canonicalize the hash key */
429   svn_stringbuf_set(cfg->tmp_key, section);
430   if (! cfg->section_names_case_sensitive)
431     make_hash_key(cfg->tmp_key->data);
432 
433   sec_ptr = apr_hash_get(cfg->sections, cfg->tmp_key->data,
434                          cfg->tmp_key->len);
435   if (sectionp != NULL)
436     *sectionp = sec_ptr;
437 
438   if (sec_ptr != NULL && option != NULL)
439     {
440       cfg_section_t *sec = sec_ptr;
441       cfg_option_t *opt;
442 
443       /* Canonicalize the option key */
444       svn_stringbuf_set(cfg->tmp_key, option);
445       if (! cfg->option_names_case_sensitive)
446         make_hash_key(cfg->tmp_key->data);
447 
448       opt = apr_hash_get(sec->options, cfg->tmp_key->data,
449                          cfg->tmp_key->len);
450       /* NOTE: ConfigParser's sections are case sensitive. */
451       if (opt == NULL
452           && apr_strnatcasecmp(section, SVN_CONFIG__DEFAULT_SECTION) != 0)
453         /* Options which aren't found in the requested section are
454            also sought after in the default section. */
455         opt = find_option(cfg, SVN_CONFIG__DEFAULT_SECTION, option, &sec);
456       return opt;
457     }
458 
459   return NULL;
460 }
461 
462 
463 /* Has a bi-directional dependency with make_string_from_option(). */
464 static void
465 expand_option_value(svn_config_t *cfg, cfg_section_t *section,
466                     const char *opt_value, const char **opt_x_valuep,
467                     apr_pool_t *x_pool);
468 
469 
470 /* Set *VALUEP according to the OPT's value.  A value for X_POOL must
471    only ever be passed into this function by expand_option_value(). */
472 static void
make_string_from_option(const char ** valuep,svn_config_t * cfg,cfg_section_t * section,cfg_option_t * opt,apr_pool_t * x_pool)473 make_string_from_option(const char **valuep, svn_config_t *cfg,
474                         cfg_section_t *section, cfg_option_t *opt,
475                         apr_pool_t* x_pool)
476 {
477   /* Expand the option value if necessary. */
478   if (!opt->expanded)
479     {
480       /* before attempting to expand an option, check for the placeholder.
481        * If none is there, there is no point in calling expand_option_value.
482        */
483       if (opt->value && strchr(opt->value, '%'))
484         {
485           apr_pool_t *tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool));
486 
487           expand_option_value(cfg, section, opt->value, &opt->x_value, tmp_pool);
488           opt->expanded = TRUE;
489 
490           if (!x_pool)
491             {
492               /* Grab the fully expanded value from tmp_pool before its
493                  disappearing act. */
494               if (opt->x_value)
495                 opt->x_value = apr_pstrmemdup(cfg->x_pool, opt->x_value,
496                                               strlen(opt->x_value));
497               svn_pool_destroy(tmp_pool);
498             }
499         }
500       else
501         {
502           opt->expanded = TRUE;
503         }
504     }
505 
506   if (opt->x_value)
507     *valuep = opt->x_value;
508   else
509     *valuep = opt->value;
510 }
511 
512 
513 /* Start of variable-replacement placeholder */
514 #define FMT_START     "%("
515 #define FMT_START_LEN (sizeof(FMT_START) - 1)
516 
517 /* End of variable-replacement placeholder */
518 #define FMT_END       ")s"
519 #define FMT_END_LEN   (sizeof(FMT_END) - 1)
520 
521 
522 /* Expand OPT_VALUE (which may be NULL) in SECTION into *OPT_X_VALUEP.
523    If no variable replacements are done, set *OPT_X_VALUEP to
524    NULL. Allocate from X_POOL. */
525 static void
expand_option_value(svn_config_t * cfg,cfg_section_t * section,const char * opt_value,const char ** opt_x_valuep,apr_pool_t * x_pool)526 expand_option_value(svn_config_t *cfg, cfg_section_t *section,
527                     const char *opt_value, const char **opt_x_valuep,
528                     apr_pool_t *x_pool)
529 {
530   svn_stringbuf_t *buf = NULL;
531   const char *parse_from = opt_value;
532   const char *copy_from = parse_from;
533   const char *name_start, *name_end;
534 
535   while (parse_from != NULL
536          && *parse_from != '\0'
537          && (name_start = strstr(parse_from, FMT_START)) != NULL)
538     {
539       name_start += FMT_START_LEN;
540       if (*name_start == '\0')
541         /* FMT_START at end of opt_value. */
542         break;
543 
544       name_end = strstr(name_start, FMT_END);
545       if (name_end != NULL)
546         {
547           cfg_option_t *x_opt;
548           apr_size_t len = name_end - name_start;
549           char *name = apr_pstrmemdup(x_pool, name_start, len);
550 
551           x_opt = find_option(cfg, section->name, name, NULL);
552 
553           if (x_opt != NULL)
554             {
555               const char *cstring;
556 
557               /* Pass back the sub-pool originally provided by
558                  make_string_from_option() as an indication of when it
559                  should terminate. */
560               make_string_from_option(&cstring, cfg, section, x_opt, x_pool);
561 
562               /* Append the plain text preceding the expansion. */
563               len = name_start - FMT_START_LEN - copy_from;
564               if (buf == NULL)
565                 {
566                   buf = svn_stringbuf_ncreate(copy_from, len, x_pool);
567                   cfg->x_values = TRUE;
568                 }
569               else
570                 svn_stringbuf_appendbytes(buf, copy_from, len);
571 
572               /* Append the expansion and adjust parse pointers. */
573               svn_stringbuf_appendcstr(buf, cstring);
574               parse_from = name_end + FMT_END_LEN;
575               copy_from = parse_from;
576             }
577           else
578             /* Though ConfigParser considers the failure to resolve
579                the requested expansion an exception condition, we
580                consider it to be plain text, and look for the start of
581                the next one. */
582             parse_from = name_end + FMT_END_LEN;
583         }
584       else
585         /* Though ConfigParser treats unterminated format specifiers
586            as an exception condition, we consider them to be plain
587            text.  The fact that there are no more format specifier
588            endings means we're done parsing. */
589         parse_from = NULL;
590     }
591 
592   if (buf != NULL)
593     {
594       /* Copy the remainder of the plain text. */
595       svn_stringbuf_appendcstr(buf, copy_from);
596       *opt_x_valuep = buf->data;
597     }
598   else
599     *opt_x_valuep = NULL;
600 }
601 
602 static cfg_section_t *
svn_config_addsection(svn_config_t * cfg,const char * section)603 svn_config_addsection(svn_config_t *cfg,
604                       const char *section)
605 {
606   cfg_section_t *s;
607   const char *hash_key;
608 
609   s = apr_palloc(cfg->pool, sizeof(cfg_section_t));
610   s->name = apr_pstrdup(cfg->pool, section);
611   if(cfg->section_names_case_sensitive)
612     hash_key = s->name;
613   else
614     hash_key = make_hash_key(apr_pstrdup(cfg->pool, section));
615   s->options = apr_hash_make(cfg->pool);
616   svn_hash_sets(cfg->sections, hash_key, s);
617 
618   return s;
619 }
620 
621 static void
svn_config_create_option(cfg_option_t ** opt,const char * option,const char * value,svn_boolean_t option_names_case_sensitive,apr_pool_t * pool)622 svn_config_create_option(cfg_option_t **opt,
623                          const char *option,
624                          const char *value,
625                          svn_boolean_t option_names_case_sensitive,
626                          apr_pool_t *pool)
627 {
628   cfg_option_t *o;
629 
630   o = apr_palloc(pool, sizeof(cfg_option_t));
631   o->name = apr_pstrdup(pool, option);
632   if(option_names_case_sensitive)
633     o->hash_key = o->name;
634   else
635     o->hash_key = make_hash_key(apr_pstrdup(pool, option));
636 
637   o->value = apr_pstrdup(pool, value);
638   o->x_value = NULL;
639   o->expanded = FALSE;
640 
641   *opt = o;
642 }
643 
644 
645 void
svn_config_get(svn_config_t * cfg,const char ** valuep,const char * section,const char * option,const char * default_value)646 svn_config_get(svn_config_t *cfg, const char **valuep,
647                const char *section, const char *option,
648                const char *default_value)
649 {
650   *valuep = default_value;
651   if (cfg)
652     {
653       cfg_section_t *sec;
654       cfg_option_t *opt = find_option(cfg, section, option, &sec);
655       if (opt != NULL)
656         {
657           make_string_from_option(valuep, cfg, sec, opt, NULL);
658         }
659       else
660         /* before attempting to expand an option, check for the placeholder.
661          * If none is there, there is no point in calling expand_option_value.
662          */
663         if (default_value && strchr(default_value, '%'))
664           {
665             apr_pool_t *tmp_pool = svn_pool_create(cfg->x_pool);
666             const char *x_default;
667             expand_option_value(cfg, sec, default_value, &x_default, tmp_pool);
668             if (x_default)
669               {
670                 svn_stringbuf_set(cfg->tmp_value, x_default);
671                 *valuep = cfg->tmp_value->data;
672               }
673             svn_pool_destroy(tmp_pool);
674           }
675     }
676 }
677 
678 
679 
680 void
svn_config_set(svn_config_t * cfg,const char * section,const char * option,const char * value)681 svn_config_set(svn_config_t *cfg,
682                const char *section, const char *option,
683                const char *value)
684 {
685   cfg_section_t *sec;
686   cfg_option_t *opt;
687 
688   remove_expansions(cfg);
689 
690   opt = find_option(cfg, section, option, &sec);
691   if (opt != NULL)
692     {
693       /* Replace the option's value. */
694       opt->value = apr_pstrdup(cfg->pool, value);
695       opt->expanded = FALSE;
696       return;
697     }
698 
699   /* Create a new option */
700   svn_config_create_option(&opt, option, value,
701                            cfg->option_names_case_sensitive,
702                            cfg->pool);
703 
704   if (sec == NULL)
705     {
706       /* Even the section doesn't exist. Create it. */
707       sec = svn_config_addsection(cfg, section);
708     }
709 
710   svn_hash_sets(sec->options, opt->hash_key, opt);
711 }
712 
713 
714 
715 /* Set *BOOLP to true or false depending (case-insensitively) on INPUT.
716    If INPUT is null, set *BOOLP to DEFAULT_VALUE.
717 
718    INPUT is a string indicating truth or falsehood in any of the usual
719    ways: "true"/"yes"/"on"/etc, "false"/"no"/"off"/etc.
720 
721    If INPUT is neither NULL nor a recognized string, return an error
722    with code SVN_ERR_BAD_CONFIG_VALUE; use SECTION and OPTION in
723    constructing the error string. */
724 static svn_error_t *
get_bool(svn_boolean_t * boolp,const char * input,svn_boolean_t default_value,const char * section,const char * option)725 get_bool(svn_boolean_t *boolp, const char *input, svn_boolean_t default_value,
726          const char *section, const char *option)
727 {
728   svn_tristate_t value = svn_tristate__from_word(input);
729 
730   if (value == svn_tristate_true)
731     *boolp = TRUE;
732   else if (value == svn_tristate_false)
733     *boolp = FALSE;
734   else if (input == NULL) /* no value provided */
735     *boolp = default_value;
736 
737   else if (section) /* unrecognized value */
738     return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
739                              _("Config error: invalid boolean "
740                                "value '%s' for '[%s] %s'"),
741                              input, section, option);
742   else
743     return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
744                              _("Config error: invalid boolean "
745                                "value '%s' for '%s'"),
746                              input, option);
747 
748   return SVN_NO_ERROR;
749 }
750 
751 
752 svn_error_t *
svn_config_get_bool(svn_config_t * cfg,svn_boolean_t * valuep,const char * section,const char * option,svn_boolean_t default_value)753 svn_config_get_bool(svn_config_t *cfg, svn_boolean_t *valuep,
754                     const char *section, const char *option,
755                     svn_boolean_t default_value)
756 {
757   const char *tmp_value;
758   svn_config_get(cfg, &tmp_value, section, option, NULL);
759   return get_bool(valuep, tmp_value, default_value, section, option);
760 }
761 
762 
763 
764 void
svn_config_set_bool(svn_config_t * cfg,const char * section,const char * option,svn_boolean_t value)765 svn_config_set_bool(svn_config_t *cfg,
766                     const char *section, const char *option,
767                     svn_boolean_t value)
768 {
769   svn_config_set(cfg, section, option,
770                  (value ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE));
771 }
772 
773 svn_error_t *
svn_config_get_int64(svn_config_t * cfg,apr_int64_t * valuep,const char * section,const char * option,apr_int64_t default_value)774 svn_config_get_int64(svn_config_t *cfg,
775                      apr_int64_t *valuep,
776                      const char *section,
777                      const char *option,
778                      apr_int64_t default_value)
779 {
780   const char *tmp_value;
781   svn_config_get(cfg, &tmp_value, section, option, NULL);
782   if (tmp_value)
783     return svn_cstring_strtoi64(valuep, tmp_value,
784                                 APR_INT64_MIN, APR_INT64_MAX, 10);
785 
786   *valuep = default_value;
787   return SVN_NO_ERROR;
788 }
789 
790 void
svn_config_set_int64(svn_config_t * cfg,const char * section,const char * option,apr_int64_t value)791 svn_config_set_int64(svn_config_t *cfg,
792                      const char *section,
793                      const char *option,
794                      apr_int64_t value)
795 {
796   svn_config_set(cfg, section, option,
797                  apr_psprintf(cfg->pool, "%" APR_INT64_T_FMT, value));
798 }
799 
800 svn_error_t *
svn_config_get_yes_no_ask(svn_config_t * cfg,const char ** valuep,const char * section,const char * option,const char * default_value)801 svn_config_get_yes_no_ask(svn_config_t *cfg, const char **valuep,
802                           const char *section, const char *option,
803                           const char* default_value)
804 {
805   const char *tmp_value;
806 
807   svn_config_get(cfg, &tmp_value, section, option, NULL);
808 
809   if (! tmp_value)
810     tmp_value = default_value;
811 
812   if (tmp_value && (0 == svn_cstring_casecmp(tmp_value, SVN_CONFIG_ASK)))
813     {
814       *valuep = SVN_CONFIG_ASK;
815     }
816   else
817     {
818       svn_boolean_t bool_val;
819       /* We already incorporated default_value into tmp_value if
820          necessary, so the FALSE below will be ignored unless the
821          caller is doing something it shouldn't be doing. */
822       SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
823       *valuep = bool_val ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE;
824     }
825 
826   return SVN_NO_ERROR;
827 }
828 
829 svn_error_t *
svn_config_get_tristate(svn_config_t * cfg,svn_tristate_t * valuep,const char * section,const char * option,const char * unknown_value,svn_tristate_t default_value)830 svn_config_get_tristate(svn_config_t *cfg, svn_tristate_t *valuep,
831                         const char *section, const char *option,
832                         const char *unknown_value,
833                         svn_tristate_t default_value)
834 {
835   const char *tmp_value;
836 
837   svn_config_get(cfg, &tmp_value, section, option, NULL);
838 
839   if (! tmp_value)
840     {
841       *valuep = default_value;
842     }
843   else if (0 == svn_cstring_casecmp(tmp_value, unknown_value))
844     {
845       *valuep = svn_tristate_unknown;
846     }
847   else
848     {
849       svn_boolean_t bool_val;
850       /* We already incorporated default_value into tmp_value if
851          necessary, so the FALSE below will be ignored unless the
852          caller is doing something it shouldn't be doing. */
853       SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
854       *valuep = bool_val ? svn_tristate_true : svn_tristate_false;
855     }
856 
857   return SVN_NO_ERROR;
858 }
859 
860 int
svn_config_enumerate_sections(svn_config_t * cfg,svn_config_section_enumerator_t callback,void * baton)861 svn_config_enumerate_sections(svn_config_t *cfg,
862                               svn_config_section_enumerator_t callback,
863                               void *baton)
864 {
865   apr_hash_index_t *sec_ndx;
866   int count = 0;
867   apr_pool_t *subpool = svn_pool_create(cfg->x_pool);
868 
869   for (sec_ndx = apr_hash_first(subpool, cfg->sections);
870        sec_ndx != NULL;
871        sec_ndx = apr_hash_next(sec_ndx))
872     {
873       void *sec_ptr;
874       cfg_section_t *sec;
875 
876       apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
877       sec = sec_ptr;
878       ++count;
879       if (!callback(sec->name, baton))
880         break;
881     }
882 
883   svn_pool_destroy(subpool);
884   return count;
885 }
886 
887 
888 int
svn_config_enumerate_sections2(svn_config_t * cfg,svn_config_section_enumerator2_t callback,void * baton,apr_pool_t * pool)889 svn_config_enumerate_sections2(svn_config_t *cfg,
890                                svn_config_section_enumerator2_t callback,
891                                void *baton, apr_pool_t *pool)
892 {
893   apr_hash_index_t *sec_ndx;
894   apr_pool_t *iteration_pool;
895   int count = 0;
896 
897   iteration_pool = svn_pool_create(pool);
898   for (sec_ndx = apr_hash_first(pool, cfg->sections);
899        sec_ndx != NULL;
900        sec_ndx = apr_hash_next(sec_ndx))
901     {
902       void *sec_ptr;
903       cfg_section_t *sec;
904 
905       apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
906       sec = sec_ptr;
907       ++count;
908       svn_pool_clear(iteration_pool);
909       if (!callback(sec->name, baton, iteration_pool))
910         break;
911     }
912   svn_pool_destroy(iteration_pool);
913 
914   return count;
915 }
916 
917 
918 
919 int
svn_config_enumerate(svn_config_t * cfg,const char * section,svn_config_enumerator_t callback,void * baton)920 svn_config_enumerate(svn_config_t *cfg, const char *section,
921                      svn_config_enumerator_t callback, void *baton)
922 {
923   cfg_section_t *sec;
924   apr_hash_index_t *opt_ndx;
925   int count;
926   apr_pool_t *subpool;
927 
928   find_option(cfg, section, NULL, &sec);
929   if (sec == NULL)
930     return 0;
931 
932   subpool = svn_pool_create(cfg->x_pool);
933   count = 0;
934   for (opt_ndx = apr_hash_first(subpool, sec->options);
935        opt_ndx != NULL;
936        opt_ndx = apr_hash_next(opt_ndx))
937     {
938       void *opt_ptr;
939       cfg_option_t *opt;
940       const char *temp_value;
941 
942       apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
943       opt = opt_ptr;
944 
945       ++count;
946       make_string_from_option(&temp_value, cfg, sec, opt, NULL);
947       if (!callback(opt->name, temp_value, baton))
948         break;
949     }
950 
951   svn_pool_destroy(subpool);
952   return count;
953 }
954 
955 
956 int
svn_config_enumerate2(svn_config_t * cfg,const char * section,svn_config_enumerator2_t callback,void * baton,apr_pool_t * pool)957 svn_config_enumerate2(svn_config_t *cfg, const char *section,
958                       svn_config_enumerator2_t callback, void *baton,
959                       apr_pool_t *pool)
960 {
961   cfg_section_t *sec;
962   apr_hash_index_t *opt_ndx;
963   apr_pool_t *iteration_pool;
964   int count;
965 
966   find_option(cfg, section, NULL, &sec);
967   if (sec == NULL)
968     return 0;
969 
970   iteration_pool = svn_pool_create(pool);
971   count = 0;
972   for (opt_ndx = apr_hash_first(pool, sec->options);
973        opt_ndx != NULL;
974        opt_ndx = apr_hash_next(opt_ndx))
975     {
976       void *opt_ptr;
977       cfg_option_t *opt;
978       const char *temp_value;
979 
980       apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
981       opt = opt_ptr;
982 
983       ++count;
984       make_string_from_option(&temp_value, cfg, sec, opt, NULL);
985       svn_pool_clear(iteration_pool);
986       if (!callback(opt->name, temp_value, baton, iteration_pool))
987         break;
988     }
989   svn_pool_destroy(iteration_pool);
990 
991   return count;
992 }
993 
994 
995 
996 /* Baton for search_groups() */
997 struct search_groups_baton
998 {
999   const char *key;          /* Provided by caller of svn_config_find_group */
1000   const char *match;        /* Filled in by search_groups */
1001   apr_pool_t *pool;
1002 };
1003 
1004 
1005 /* This is an `svn_config_enumerator_t' function, and BATON is a
1006  * `struct search_groups_baton *'.
1007  */
search_groups(const char * name,const char * value,void * baton,apr_pool_t * pool)1008 static svn_boolean_t search_groups(const char *name,
1009                                    const char *value,
1010                                    void *baton,
1011                                    apr_pool_t *pool)
1012 {
1013   struct search_groups_baton *b = baton;
1014   apr_array_header_t *list;
1015 
1016   list = svn_cstring_split(value, ",", TRUE, pool);
1017   if (svn_cstring_match_glob_list(b->key, list))
1018     {
1019       /* Fill in the match and return false, to stop enumerating. */
1020       b->match = apr_pstrdup(b->pool, name);
1021       return FALSE;
1022     }
1023   else
1024     return TRUE;
1025 }
1026 
1027 
svn_config_find_group(svn_config_t * cfg,const char * key,const char * master_section,apr_pool_t * pool)1028 const char *svn_config_find_group(svn_config_t *cfg, const char *key,
1029                                   const char *master_section,
1030                                   apr_pool_t *pool)
1031 {
1032   struct search_groups_baton gb;
1033 
1034   gb.key = key;
1035   gb.match = NULL;
1036   gb.pool = pool;
1037   (void) svn_config_enumerate2(cfg, master_section, search_groups, &gb, pool);
1038   return gb.match;
1039 }
1040 
1041 
1042 const char*
svn_config_get_server_setting(svn_config_t * cfg,const char * server_group,const char * option_name,const char * default_value)1043 svn_config_get_server_setting(svn_config_t *cfg,
1044                               const char* server_group,
1045                               const char* option_name,
1046                               const char* default_value)
1047 {
1048   const char *retval;
1049   svn_config_get(cfg, &retval, SVN_CONFIG_SECTION_GLOBAL,
1050                  option_name, default_value);
1051   if (server_group)
1052     {
1053       svn_config_get(cfg, &retval, server_group, option_name, retval);
1054     }
1055   return retval;
1056 }
1057 
1058 
1059 svn_error_t *
svn_config_dup(svn_config_t ** cfgp,svn_config_t * src,apr_pool_t * pool)1060 svn_config_dup(svn_config_t **cfgp,
1061                svn_config_t *src,
1062                apr_pool_t *pool)
1063 {
1064   apr_hash_index_t *sectidx;
1065   apr_hash_index_t *optidx;
1066 
1067   *cfgp = 0;
1068   SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
1069 
1070   (*cfgp)->x_values = src->x_values;
1071   (*cfgp)->section_names_case_sensitive = src->section_names_case_sensitive;
1072   (*cfgp)->option_names_case_sensitive = src->option_names_case_sensitive;
1073 
1074   for (sectidx = apr_hash_first(pool, src->sections);
1075        sectidx != NULL;
1076        sectidx = apr_hash_next(sectidx))
1077   {
1078     const void *sectkey;
1079     void *sectval;
1080     apr_ssize_t sectkeyLength;
1081     cfg_section_t * srcsect;
1082     cfg_section_t * destsec;
1083 
1084     apr_hash_this(sectidx, &sectkey, &sectkeyLength, &sectval);
1085     srcsect = sectval;
1086 
1087     destsec = svn_config_addsection(*cfgp, srcsect->name);
1088 
1089     for (optidx = apr_hash_first(pool, srcsect->options);
1090          optidx != NULL;
1091          optidx = apr_hash_next(optidx))
1092     {
1093       const void *optkey;
1094       void *optval;
1095       apr_ssize_t optkeyLength;
1096       cfg_option_t *srcopt;
1097       cfg_option_t *destopt;
1098 
1099       apr_hash_this(optidx, &optkey, &optkeyLength, &optval);
1100       srcopt = optval;
1101 
1102       svn_config_create_option(&destopt, srcopt->name, srcopt->value,
1103                                (*cfgp)->option_names_case_sensitive,
1104                                pool);
1105 
1106       destopt->value = apr_pstrdup(pool, srcopt->value);
1107       destopt->x_value = apr_pstrdup(pool, srcopt->x_value);
1108       destopt->expanded = srcopt->expanded;
1109       apr_hash_set(destsec->options,
1110                    apr_pstrdup(pool, (const char*)optkey),
1111                    optkeyLength, destopt);
1112     }
1113   }
1114 
1115   return SVN_NO_ERROR;
1116 }
1117 
1118 svn_error_t *
svn_config_copy_config(apr_hash_t ** cfg_hash,apr_hash_t * src_hash,apr_pool_t * pool)1119 svn_config_copy_config(apr_hash_t **cfg_hash,
1120                        apr_hash_t *src_hash,
1121                        apr_pool_t *pool)
1122 {
1123   apr_hash_index_t *cidx;
1124 
1125   *cfg_hash = apr_hash_make(pool);
1126   for (cidx = apr_hash_first(pool, src_hash);
1127        cidx != NULL;
1128        cidx = apr_hash_next(cidx))
1129   {
1130     const void *ckey;
1131     void *cval;
1132     apr_ssize_t ckeyLength;
1133     svn_config_t * srcconfig;
1134     svn_config_t * destconfig;
1135 
1136     apr_hash_this(cidx, &ckey, &ckeyLength, &cval);
1137     srcconfig = cval;
1138 
1139     SVN_ERR(svn_config_dup(&destconfig, srcconfig, pool));
1140 
1141     apr_hash_set(*cfg_hash,
1142                  apr_pstrdup(pool, (const char*)ckey),
1143                  ckeyLength, destconfig);
1144   }
1145 
1146   return SVN_NO_ERROR;
1147 }
1148 
1149 svn_error_t*
svn_config_get_server_setting_int(svn_config_t * cfg,const char * server_group,const char * option_name,apr_int64_t default_value,apr_int64_t * result_value,apr_pool_t * pool)1150 svn_config_get_server_setting_int(svn_config_t *cfg,
1151                                   const char *server_group,
1152                                   const char *option_name,
1153                                   apr_int64_t default_value,
1154                                   apr_int64_t *result_value,
1155                                   apr_pool_t *pool)
1156 {
1157   const char* tmp_value;
1158   char *end_pos;
1159 
1160   tmp_value = svn_config_get_server_setting(cfg, server_group,
1161                                             option_name, NULL);
1162   if (tmp_value == NULL)
1163     *result_value = default_value;
1164   else
1165     {
1166       /* read tmp_value as an int now */
1167       *result_value = apr_strtoi64(tmp_value, &end_pos, 0);
1168 
1169       if (*end_pos != 0)
1170         {
1171           return svn_error_createf
1172             (SVN_ERR_BAD_CONFIG_VALUE, NULL,
1173              _("Config error: invalid integer value '%s'"),
1174              tmp_value);
1175         }
1176     }
1177 
1178   return SVN_NO_ERROR;
1179 }
1180 
1181 svn_error_t *
svn_config_get_server_setting_bool(svn_config_t * cfg,svn_boolean_t * valuep,const char * server_group,const char * option_name,svn_boolean_t default_value)1182 svn_config_get_server_setting_bool(svn_config_t *cfg,
1183                                    svn_boolean_t *valuep,
1184                                    const char *server_group,
1185                                    const char *option_name,
1186                                    svn_boolean_t default_value)
1187 {
1188   const char* tmp_value;
1189   tmp_value = svn_config_get_server_setting(cfg, server_group,
1190                                             option_name, NULL);
1191   return get_bool(valuep, tmp_value, default_value,
1192                   server_group, option_name);
1193 }
1194 
1195 
1196 svn_boolean_t
svn_config_has_section(svn_config_t * cfg,const char * section)1197 svn_config_has_section(svn_config_t *cfg, const char *section)
1198 {
1199   cfg_section_t *sec;
1200 
1201   /* Canonicalize the hash key */
1202   svn_stringbuf_set(cfg->tmp_key, section);
1203   if (! cfg->section_names_case_sensitive)
1204     make_hash_key(cfg->tmp_key->data);
1205 
1206   sec = svn_hash_gets(cfg->sections, cfg->tmp_key->data);
1207   return sec != NULL;
1208 }
1209