| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /*************************************************************************** |
| * Description: General purpose map object * |
| * Author: Gal Shachor <shachor@il.ibm.com> * |
| * Author: Mladen Turk <mturk@apache.org> * |
| * Version: $Revision$ * |
| ***************************************************************************/ |
| #if defined(AS400) && !defined(AS400_UTF8) |
| #include "apr_xlate.h" |
| #endif |
| |
| #include "jk_global.h" |
| #include "jk_pool.h" |
| #include "jk_util.h" |
| #include "jk_map.h" |
| |
| #define CAPACITY_INC_SIZE (50) |
| #define LENGTH_OF_LINE (8192) |
| #define JK_MAP_RECURSION (20) |
| #define JK_MAP_REFERENCE (".reference") |
| #define JK_MAP_REFERENCE_SZ (strlen(JK_MAP_REFERENCE)) |
| |
| /* Taken from APR tables/apr_hash.c */ |
| /* |
| * This is the popular `times 33' hash algorithm which is used by |
| * perl and also appears in Berkeley DB. This is one of the best |
| * known hash functions for strings because it is both computed |
| * very fast and distributes very well. |
| * |
| * The originator may be Dan Bernstein but the code in Berkeley DB |
| * cites Chris Torek as the source. The best citation I have found |
| * is "Chris Torek, Hash function for text in C, Usenet message |
| * <27038@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich |
| * Salz's USENIX 1992 paper about INN which can be found at |
| * <http://citeseer.nj.nec.com/salz92internetnews.html>. |
| * |
| * The magic of number 33, i.e. why it works better than many other |
| * constants, prime or not, has never been adequately explained by |
| * anyone. So I try an explanation: if one experimentally tests all |
| * multipliers between 1 and 256 (as I did while writing a low-level |
| * data structure library some time ago) one detects that even |
| * numbers are not useable at all. The remaining 128 odd numbers |
| * (except for the number 1) work more or less all equally well. |
| * They all distribute in an acceptable way and this way fill a hash |
| * table with an average percent of approx. 86%. |
| * |
| * If one compares the chi^2 values of the variants (see |
| * Bob Jenkins ``Hashing Frequently Asked Questions'' at |
| * http://burtleburtle.net/bob/hash/hashfaq.html for a description |
| * of chi^2), the number 33 not even has the best value. But the |
| * number 33 and a few other equally good numbers like 17, 31, 63, |
| * 127 and 129 have nevertheless a great advantage to the remaining |
| * numbers in the large set of possible multipliers: their multiply |
| * operation can be replaced by a faster operation based on just one |
| * shift plus either a single addition or subtraction operation. And |
| * because a hash function has to both distribute good _and_ has to |
| * be very fast to compute, those few numbers should be preferred. |
| * |
| * -- Ralf S. Engelschall <rse@engelschall.com> |
| */ |
| #define COMPUTE_KEY_HASH(key, hash) \ |
| { \ |
| const unsigned char *p; \ |
| hash = 0; \ |
| for (p = (const unsigned char *)key; *p; p++) { \ |
| hash = hash * 33 + *p; \ |
| } \ |
| } |
| |
| static volatile int global_map_id = 0; |
| static void trim_prp_comment(char *prp); |
| static size_t trim(char *s); |
| static int map_realloc(jk_map_t *m); |
| static char *jk_map_replace_properties(jk_map_t *m, jk_map_t *env, char *value); |
| |
| int jk_map_alloc(jk_map_t **m) |
| { |
| if (m) { |
| *m = (jk_map_t *)calloc(1, sizeof(jk_map_t)); |
| if (*m) |
| return jk_map_open(*m); |
| } |
| |
| return JK_FALSE; |
| } |
| |
| int jk_map_free(jk_map_t **m) |
| { |
| int rc = JK_FALSE; |
| |
| if (m && *m) { |
| jk_map_close(*m); |
| free(*m); |
| *m = NULL; |
| } |
| |
| return rc; |
| } |
| |
| int jk_map_open(jk_map_t *m) |
| { |
| int rc = JK_FALSE; |
| |
| if (m) { |
| jk_open_pool(&m->p, m->buf, sizeof(jk_pool_atom_t) * SMALL_POOL_SIZE); |
| m->id = ++global_map_id; |
| m->capacity = 0; |
| m->size = 0; |
| m->keys = NULL; |
| m->names = NULL; |
| m->values = NULL; |
| rc = JK_TRUE; |
| } |
| |
| return rc; |
| } |
| |
| int jk_map_close(jk_map_t *m) |
| { |
| int rc = JK_FALSE; |
| |
| if (m) { |
| jk_close_pool(&m->p); |
| rc = JK_TRUE; |
| } |
| |
| return rc; |
| } |
| |
| void *jk_map_get(jk_map_t *m, const char *name, const void *def) |
| { |
| const void *rc = (void *)def; |
| |
| if (m && name) { |
| unsigned int i; |
| unsigned int hash; |
| COMPUTE_KEY_HASH(name, hash) |
| for (i = 0; i < m->size; i++) { |
| if (m->keys[i] == hash && strcmp(m->names[i], name) == 0) { |
| rc = m->values[i]; |
| break; |
| } |
| } |
| } |
| |
| return (void *)rc; /* DIRTY */ |
| } |
| |
| int jk_map_get_id(jk_map_t *m, const char *name) |
| { |
| int rc = -1; |
| if (m && name) { |
| unsigned int i; |
| unsigned int hash; |
| COMPUTE_KEY_HASH(name, hash) |
| for (i = 0; i < m->size; i++) { |
| if (m->keys[i] == hash && strcmp(m->names[i], name) == 0) { |
| rc = i; |
| break; |
| } |
| } |
| } |
| |
| return rc; |
| } |
| |
| const char *jk_map_get_string(jk_map_t *m, const char *name, const char *def) |
| { |
| const char *rc = def; |
| |
| if (m && name) { |
| unsigned int i; |
| unsigned int hash; |
| COMPUTE_KEY_HASH(name, hash) |
| for (i = 0; i < m->size; i++) { |
| if (m->keys[i] == hash && strcmp(m->names[i], name) == 0) { |
| rc = m->values[i]; |
| break; |
| } |
| } |
| } |
| |
| return rc; |
| } |
| |
| |
| int jk_map_get_int(jk_map_t *m, const char *name, int def) |
| { |
| char buf[100]; |
| const char *rc; |
| size_t len; |
| int int_res; |
| |
| sprintf(buf, "%d", def); |
| rc = jk_map_get_string(m, name, buf); |
| |
| len = strlen(rc); |
| if (len) { |
| const char *lastchar = &rc[0] + len - 1; |
| int multit = 1; |
| if ('m' == *lastchar || 'M' == *lastchar) { |
| multit = 1024 * 1024; |
| } |
| else if ('k' == *lastchar || 'K' == *lastchar) { |
| multit = 1024; |
| } |
| /* Safe because atoi() will stop at any non-numeric lastchar */ |
| int_res = atoi(rc) * multit; |
| } |
| else |
| int_res = def; |
| |
| return int_res; |
| } |
| |
| double jk_map_get_double(jk_map_t *m, const char *name, double def) |
| { |
| char buf[100]; |
| const char *rc; |
| |
| sprintf(buf, "%f", def); |
| rc = jk_map_get_string(m, name, buf); |
| |
| return atof(rc); |
| } |
| |
| int jk_map_get_bool(jk_map_t *m, const char *name, int def) |
| { |
| char buf[100]; |
| const char *rc; |
| |
| sprintf(buf, "%d", def); |
| rc = jk_map_get_string(m, name, buf); |
| |
| return jk_get_bool_code(rc, def); |
| } |
| |
| char **jk_map_get_string_list(jk_map_t *m, |
| const char *name, |
| unsigned int *list_len, const char *def) |
| { |
| const char *l = jk_map_get_string(m, name, def); |
| char **ar = NULL; |
| |
| #ifdef _MT_CODE_PTHREAD |
| char *lasts; |
| #endif |
| |
| *list_len = 0; |
| |
| if (l) { |
| unsigned capacity = 0; |
| unsigned idex = 0; |
| char *p; |
| char *v = jk_pool_strdup(&m->p, l); |
| |
| if (!v) { |
| return NULL; |
| } |
| |
| /* |
| * GS, in addition to VG's patch, we now need to |
| * strtok also by a "*" |
| */ |
| #ifdef _MT_CODE_PTHREAD |
| for (p = strtok_r(v, " \t,", &lasts); |
| p; p = strtok_r(NULL, " \t,", &lasts)) |
| #else |
| for (p = strtok(v, " \t,"); p; p = strtok(NULL, " \t,")) |
| #endif |
| |
| { |
| if (idex == capacity) { |
| ar = jk_pool_realloc(&m->p, |
| sizeof(char *) * (capacity + 5), |
| ar, sizeof(char *) * capacity); |
| if (!ar) { |
| return NULL; |
| } |
| capacity += 5; |
| } |
| ar[idex] = jk_pool_strdup(&m->p, p); |
| idex++; |
| } |
| |
| *list_len = idex; |
| } |
| |
| return ar; |
| } |
| |
| int *jk_map_get_int_list(jk_map_t *m, |
| const char *name, |
| unsigned int *list_len, |
| const char *def) |
| { |
| const char *l = jk_map_get_string(m, name, def); |
| int *ar = NULL; |
| |
| #ifdef _MT_CODE_PTHREAD |
| char *lasts; |
| #endif |
| |
| if (l) { |
| unsigned int capacity = 0; |
| unsigned int idex = 0; |
| char *p; |
| char *v = jk_pool_strdup(&m->p, l); |
| |
| if (!v) { |
| return NULL; |
| } |
| |
| /* |
| * GS, in addition to VG's patch, we now need to |
| * strtok also by a "*" |
| */ |
| #ifdef _MT_CODE_PTHREAD |
| for (p = strtok_r(v, " \t,", &lasts); |
| p; p = strtok_r(NULL, " \t,", &lasts)) |
| #else |
| for (p = strtok(v, " \t,"); p; p = strtok(NULL, " \t,")) |
| #endif |
| |
| { |
| if (idex == capacity) { |
| ar = jk_pool_realloc(&m->p, |
| sizeof(int) * (capacity + 5), |
| ar, sizeof(int) * capacity); |
| if (!ar) { |
| return NULL; |
| } |
| capacity += 5; |
| } |
| ar[idex] = atoi(p); |
| idex++; |
| } |
| |
| *list_len = idex; |
| } |
| return ar; |
| } |
| |
| int jk_map_add(jk_map_t *m, const char *name, const void *value) |
| { |
| int rc = JK_FALSE; |
| |
| if (m && name) { |
| unsigned int hash; |
| COMPUTE_KEY_HASH(name, hash) |
| map_realloc(m); |
| |
| if (m->size < m->capacity) { |
| m->values[m->size] = value; |
| m->names[m->size] = jk_pool_strdup(&m->p, name); |
| m->keys[m->size] = hash; |
| m->size++; |
| rc = JK_TRUE; |
| } |
| } |
| |
| return rc; |
| } |
| |
| int jk_map_put(jk_map_t *m, const char *name, const void *value, void **old) |
| { |
| int rc = JK_FALSE; |
| |
| if (m && name) { |
| unsigned int i; |
| unsigned int hash; |
| COMPUTE_KEY_HASH(name, hash) |
| for (i = 0; i < m->size; i++) { |
| if (m->keys[i] == hash && strcmp(m->names[i], name) == 0) { |
| break; |
| } |
| } |
| |
| if (i < m->size) { |
| if (old) |
| *old = (void *)m->values[i]; /* DIRTY */ |
| m->values[i] = value; |
| rc = JK_TRUE; |
| } |
| else { |
| rc = jk_map_add(m, name, value); |
| } |
| } |
| |
| return rc; |
| } |
| |
| |
| static int jk_map_validate_property(char *prp, jk_logger_t *l) |
| { |
| /* check the worker properties */ |
| if (!jk_is_valid_property(prp)) { |
| jk_log(l, JK_LOG_ERROR, |
| "The attribute '%s' is not supported - please check" |
| " the documentation for the supported attributes.", |
| prp); |
| return JK_FALSE; |
| } |
| if (jk_is_deprecated_property(prp)) { |
| jk_log(l, JK_LOG_WARNING, |
| "The attribute '%s' is deprecated - please check" |
| " the documentation for the correct replacement.", |
| prp); |
| } |
| return JK_TRUE; |
| } |
| |
| static int jk_map_handle_duplicates(jk_map_t *m, const char *prp, char **v, |
| int treatment, jk_logger_t *l) |
| { |
| const char *oldv = jk_map_get_string(m, prp, NULL); |
| if (oldv) { |
| if ((treatment == JK_MAP_HANDLE_DUPLICATES) |
| && jk_is_unique_property(prp) == JK_FALSE) { |
| char *tmpv = jk_pool_alloc(&m->p, |
| strlen(*v) + strlen(oldv) + 3); |
| if (tmpv) { |
| char sep = '*'; |
| if (jk_is_path_property(prp)) |
| sep = PATH_SEPERATOR; |
| else if (jk_is_cmd_line_property(prp)) |
| sep = ' '; |
| else if (jk_is_list_property(prp)) |
| sep = ','; |
| sprintf(tmpv, "%s%c%s", oldv, sep, *v); |
| } |
| *v = tmpv; |
| if (JK_IS_DEBUG_LEVEL(l)) |
| jk_log(l, JK_LOG_DEBUG, |
| "Concatenated value is: %s -> %s", |
| prp, *v); |
| return JK_FALSE; |
| } |
| else { |
| jk_log(l, JK_LOG_WARNING, |
| "Duplicate key '%s' detected - previous value '%s'" |
| " will be overwritten with '%s'.", |
| prp, oldv ? oldv : "(null)", v ? *v : "(null)"); |
| return JK_TRUE; |
| } |
| } |
| else { |
| return JK_TRUE; |
| } |
| } |
| |
| int jk_map_read_property(jk_map_t *m, jk_map_t *env, const char *str, |
| int treatment, jk_logger_t *l) |
| { |
| int rc = JK_TRUE; |
| char buf[LENGTH_OF_LINE + 1]; |
| char *prp = &buf[0]; |
| |
| if (strlen(str) > LENGTH_OF_LINE) { |
| jk_log(l, JK_LOG_ERROR, |
| "Line too long (%d > %d), ignoring entry", |
| strlen(str), LENGTH_OF_LINE); |
| return JK_FALSE; |
| } |
| |
| strcpy(prp, str); |
| if (trim(prp)) { |
| char *v = strchr(prp, '='); |
| if (v) { |
| *v = '\0'; |
| v++; |
| if (trim(v) && trim(prp)) { |
| if (treatment == JK_MAP_HANDLE_RAW) { |
| v = jk_pool_strdup(&m->p, v); |
| } |
| else { |
| if (jk_map_validate_property(prp, l) == JK_FALSE) |
| return JK_FALSE; |
| v = jk_map_replace_properties(m, env, v); |
| if (jk_map_handle_duplicates(m, prp, &v, treatment, l) == JK_TRUE) |
| v = jk_pool_strdup(&m->p, v); |
| } |
| if (v) { |
| if (JK_IS_DEBUG_LEVEL(l)) |
| jk_log(l, JK_LOG_DEBUG, |
| "Adding property '%s' with value '%s' to map.", |
| prp, v); |
| jk_map_put(m, prp, v, NULL); |
| } |
| else { |
| JK_LOG_NULL_PARAMS(l); |
| rc = JK_FALSE; |
| } |
| } |
| } |
| } |
| return rc; |
| } |
| |
| |
| int jk_map_read_properties(jk_map_t *m, jk_map_t *env, const char *f, time_t *modified, |
| int treatment, jk_logger_t *l) |
| { |
| int rc = JK_FALSE; |
| |
| if (m && f) { |
| struct stat statbuf; |
| FILE *fp; |
| if (jk_stat(f, &statbuf) == -1) |
| return JK_FALSE; |
| #if defined(AS400) && !defined(AS400_UTF8) |
| fp = fopen(f, "r, o_ccsid=0"); |
| #else |
| fp = fopen(f, "r"); |
| #endif |
| |
| if (fp) { |
| char buf[LENGTH_OF_LINE + 1]; |
| char *prp; |
| |
| rc = JK_TRUE; |
| |
| while (NULL != (prp = fgets(buf, LENGTH_OF_LINE, fp))) { |
| trim_prp_comment(prp); |
| if (*prp) { |
| if ((rc = jk_map_read_property(m, env, prp, treatment, l)) == JK_FALSE) |
| break; |
| } |
| } |
| fclose(fp); |
| if (modified) |
| *modified = statbuf.st_mtime; |
| } |
| } |
| |
| return rc; |
| } |
| |
| |
| int jk_map_size(jk_map_t *m) |
| { |
| if (m) { |
| return m->size; |
| } |
| |
| return -1; |
| } |
| |
| const char *jk_map_name_at(jk_map_t *m, int idex) |
| { |
| if (m && idex >= 0) { |
| return m->names[idex]; /* DIRTY */ |
| } |
| |
| return NULL; |
| } |
| |
| void *jk_map_value_at(jk_map_t *m, int idex) |
| { |
| if (m && idex >= 0) { |
| return (void *)m->values[idex]; /* DIRTY */ |
| } |
| |
| return NULL; |
| } |
| |
| void jk_map_dump(jk_map_t *m, jk_logger_t *l) |
| { |
| if (m) { |
| int s = jk_map_size(m); |
| int i; |
| for (i=0;i<s;i++) { |
| if (!jk_map_name_at(m, i)) { |
| jk_log(l, JK_LOG_WARNING, |
| "Map contains empty name at index %d\n", i); |
| } |
| if (!jk_map_value_at(m, i)) { |
| jk_log(l, JK_LOG_WARNING, |
| "Map contains empty value for name '%s' at index %d\n", |
| jk_map_name_at(m, i), i); |
| } |
| if (JK_IS_DEBUG_LEVEL(l)) { |
| jk_log(l, JK_LOG_DEBUG, |
| "Dump of map %d: '%s' -> '%s'", m->id, |
| jk_map_name_at(m, i) ? jk_map_name_at(m, i) : "(null)", |
| jk_map_value_at(m, i) ? jk_map_value_at(m, i) : "(null)"); |
| } |
| } |
| } |
| } |
| |
| int jk_map_copy(jk_map_t *src, jk_map_t *dst) |
| { |
| int sz = jk_map_size(src); |
| int i; |
| for (i = 0; i < sz; i++) { |
| const char *name = jk_map_name_at(src, i); |
| if (jk_map_get(dst, name, NULL) == NULL) { |
| if (!jk_map_put(dst, name, |
| jk_pool_strdup(&dst->p, jk_map_get_string(src, name, NULL)), |
| NULL)) { |
| return JK_FALSE; |
| } |
| } |
| } |
| return JK_TRUE; |
| } |
| |
| |
| static void trim_prp_comment(char *prp) |
| { |
| #if defined(AS400) && !defined(AS400_UTF8) |
| char *comment; |
| /* lots of lines that translate a '#' realtime deleted */ |
| comment = strchr(prp, *APR_NUMBERSIGN); |
| #else |
| char *comment = strchr(prp, '#'); |
| #endif |
| if (comment) { |
| *comment = '\0'; |
| } |
| } |
| |
| static size_t trim(char *s) |
| { |
| size_t first; |
| size_t len; |
| |
| /* check for empty strings */ |
| if (!(len = strlen(s))) |
| return 0; |
| for (len = len - 1; (len > 0) && |
| jk_isspace(s[len]); len--); |
| if ((len > 0) || !jk_isspace(s[len])) { |
| len++; |
| } |
| |
| s[len] = '\0'; |
| len++; |
| |
| for (first = 0; (s[first] != '\0') && |
| jk_isspace(s[first]); first++); |
| |
| if (first > 0) { |
| memmove(s, s + first, len - first); |
| } |
| |
| return len; |
| } |
| |
| static int map_realloc(jk_map_t *m) |
| { |
| if (m->size == m->capacity) { |
| const char **names; |
| const void **values; |
| unsigned int *keys; |
| int capacity = m->capacity + CAPACITY_INC_SIZE; |
| size_t old_sz = m->capacity * sizeof(void *); |
| size_t new_sz = capacity * sizeof(void *); |
| |
| names = (const char **)jk_pool_realloc(&m->p, new_sz, m->names, old_sz); |
| values = (const void **)jk_pool_realloc(&m->p, new_sz, m->values, old_sz); |
| keys = (unsigned int *)jk_pool_realloc(&m->p, new_sz, m->keys, old_sz); |
| |
| if (values && names && keys) { |
| |
| m->names = names; |
| m->values = values; |
| m->keys = keys; |
| m->capacity = capacity; |
| |
| return JK_TRUE; |
| } |
| } |
| |
| return JK_FALSE; |
| } |
| |
| /** |
| * Replace $(property) in value. |
| * |
| */ |
| static char *jk_map_replace_properties(jk_map_t *m, jk_map_t *env, char *value) |
| { |
| char *rc = value; |
| char *env_start = rc; |
| int rec = 0; |
| |
| while ((env_start = strstr(env_start, "$(")) != NULL) { |
| char *env_end = strstr(env_start, ")"); |
| if (rec++ > 20) |
| return rc; |
| if (env_end) { |
| char env_name[LENGTH_OF_LINE + 1] = ""; |
| const char *env_value; |
| #if defined(WIN32) |
| char env_buf[LENGTH_OF_LINE + 1]; |
| #endif |
| *env_end = '\0'; |
| strcpy(env_name, env_start + 2); |
| *env_end = ')'; |
| |
| env_value = jk_map_get_string(m, env_name, NULL); |
| if (!env_value) { |
| env_value = getenv(env_name); |
| } |
| if (!env_value && env) { |
| /* Search inside local environment table */ |
| env_value = jk_map_get_string(env, env_name, NULL); |
| } |
| |
| #if defined(WIN32) |
| if (!env_value) { |
| /* Try the env block from calling process */ |
| if (GetEnvironmentVariable(env_name, env_buf, |
| sizeof(env_buf))) |
| env_value = &env_buf[0]; |
| } |
| #endif |
| if (env_value) { |
| size_t offset = 0; |
| char *new_value = jk_pool_alloc(&m->p, |
| (sizeof(char) * |
| (strlen(rc) + |
| strlen(env_value)))); |
| if (!new_value) { |
| break; |
| } |
| *env_start = '\0'; |
| strcpy(new_value, rc); |
| strcat(new_value, env_value); |
| strcat(new_value, env_end + 1); |
| offset = env_start - rc + strlen(env_value); |
| rc = new_value; |
| /* Avoid recursive subst */ |
| env_start = rc + offset; |
| } |
| else { |
| env_start = env_end; |
| } |
| } |
| else { |
| break; |
| } |
| } |
| |
| return rc; |
| } |
| |
| /** |
| * Resolve references |
| * |
| */ |
| int jk_map_resolve_references(jk_map_t *m, const char *prefix, |
| int wildcard, int depth, jk_logger_t *l) |
| { |
| int rc = JK_FALSE; |
| |
| JK_TRACE_ENTER(l); |
| |
| if (m && prefix) { |
| if (depth <= JK_MAP_RECURSION) { |
| size_t prelen = strlen(prefix); |
| unsigned int i; |
| rc = JK_TRUE; |
| if (JK_IS_DEBUG_LEVEL(l)) |
| jk_log(l, JK_LOG_DEBUG, |
| "Checking for references with prefix %s with%s wildcard (recursion %d)", |
| prefix, wildcard? "" : "out", depth); |
| for (i = 0; i < m->size; i++) { |
| char *v = (char *)m->values[i]; |
| if (v && *v && |
| !strncmp(m->names[i], prefix, prelen)) { |
| size_t remain = strlen(m->names[i]) - prelen; |
| if ((remain == JK_MAP_REFERENCE_SZ ) || (wildcard && remain > JK_MAP_REFERENCE_SZ)) { |
| remain = strlen(m->names[i]) - JK_MAP_REFERENCE_SZ; |
| if (!strncmp(m->names[i] + remain, JK_MAP_REFERENCE, JK_MAP_REFERENCE_SZ)) { |
| char *from = jk_pool_alloc(&m->p, |
| (sizeof(char) * |
| (strlen(v) + 2))); |
| char *to = jk_pool_alloc(&m->p, |
| (sizeof(char) * |
| (remain + 2))); |
| if (!from || !to) { |
| jk_log(l, JK_LOG_ERROR, |
| "Error in string allocation"); |
| rc = JK_FALSE; |
| break; |
| } |
| strcpy(from, v); |
| *(from+strlen(v)) = '.'; |
| *(from+strlen(v)+1) = '\0'; |
| strncpy(to, m->names[i], remain); |
| *(to+remain) = '.'; |
| *(to+remain+1) = '\0'; |
| |
| rc = jk_map_resolve_references(m, v, 0, depth+1, l); |
| if (rc == JK_FALSE) { |
| break; |
| } |
| if (JK_IS_DEBUG_LEVEL(l)) |
| jk_log(l, JK_LOG_DEBUG, |
| "Copying values from %s to %s", |
| from, to); |
| rc = jk_map_inherit_properties(m, from, to, l); |
| if (rc == JK_FALSE) { |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| else { |
| jk_log(l, JK_LOG_ERROR, |
| "Recursion limit %d for worker references with prefix '%s' reached", |
| JK_MAP_RECURSION, prefix); |
| } |
| } |
| else { |
| JK_LOG_NULL_PARAMS(l); |
| } |
| JK_TRACE_EXIT(l); |
| return rc; |
| } |
| |
| /** |
| * Inherit properties |
| * |
| */ |
| int jk_map_inherit_properties(jk_map_t *m, const char *from, const char *to, jk_logger_t *l) |
| { |
| int rc = JK_FALSE; |
| const char *prp; |
| char *to_prp; |
| |
| if (m && from && to) { |
| unsigned int i; |
| for (i = 0; i < m->size; i++) { |
| if (!strncmp(m->names[i], from, strlen(from))) { |
| rc = JK_TRUE; |
| prp = m->names[i] + strlen(from); |
| to_prp = jk_pool_alloc(&m->p, |
| (sizeof(char) * |
| (strlen(to) + |
| strlen(prp) + 1))); |
| if (!to_prp) { |
| jk_log(l, JK_LOG_ERROR, |
| "Error in string allocation for attribute '%s.%s'", |
| to, prp); |
| rc = JK_FALSE; |
| break; |
| } |
| strcpy(to_prp, to); |
| strcat(to_prp, prp); |
| if (jk_map_get_id(m, to_prp) < 0 ) { |
| rc = jk_map_add(m, to_prp, m->values[i]); |
| if (rc == JK_FALSE) { |
| jk_log(l, JK_LOG_ERROR, |
| "Error when adding attribute '%s'", |
| to_prp); |
| break; |
| } |
| } |
| } |
| } |
| if ( rc == JK_FALSE) { |
| jk_log(l, JK_LOG_ERROR, |
| "Reference '%s' not found", |
| from); |
| } |
| } |
| else { |
| JK_LOG_NULL_PARAMS(l); |
| } |
| return rc; |
| } |