2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 /***************************************************************************
19 * Description: General purpose map object *
20 * Author: Gal Shachor <shachor@il.ibm.com> *
21 * Author: Mladen Turk <mturk@apache.org> *
22 * Version: $Revision: 1042364 $ *
23 ***************************************************************************/
24 #if defined(AS400) && !defined(AS400_UTF8)
25 #include "apr_xlate.h"
28 #include "jk_global.h"
33 #define CAPACITY_INC_SIZE (50)
34 #define LENGTH_OF_LINE (8192)
35 #define JK_MAP_RECURSION (20)
36 #define JK_MAP_REFERENCE (".reference")
37 #define JK_MAP_REFERENCE_SZ (strlen(JK_MAP_REFERENCE))
39 /* Compute the "checksum" for a key, consisting of the first
40 * 4 bytes, packed into an int.
41 * This checksum allows us to do a single integer
42 * comparison as a fast check to determine whether we can
45 #define COMPUTE_KEY_CHECKSUM(key, checksum) \
47 const char *k = (key); \
48 unsigned int c = (unsigned int)*k; \
52 c = (unsigned int)*++k; \
57 c = (unsigned int)*++k; \
62 c = (unsigned int)*++k; \
70 jk_pool_atom_t buf[SMALL_POOL_SIZE];
76 unsigned int capacity;
80 static void trim_prp_comment(char *prp);
81 static size_t trim(char *s);
82 static int map_realloc(jk_map_t *m);
83 static char *jk_map_replace_properties(jk_map_t *m, jk_map_t *env, char *value);
85 int jk_map_alloc(jk_map_t **m)
88 return jk_map_open(*m = (jk_map_t *)malloc(sizeof(jk_map_t)));
94 int jk_map_free(jk_map_t **m)
107 int jk_map_open(jk_map_t *m)
112 jk_open_pool(&m->p, m->buf, sizeof(jk_pool_atom_t) * SMALL_POOL_SIZE);
124 int jk_map_close(jk_map_t *m)
129 jk_close_pool(&m->p);
136 void *jk_map_get(jk_map_t *m, const char *name, const void *def)
138 const void *rc = (void *)def;
143 COMPUTE_KEY_CHECKSUM(name, key)
144 for (i = 0; i < m->size; i++) {
145 if (m->keys[i] == key && strcmp(m->names[i], name) == 0) {
152 return (void *)rc; /* DIRTY */
155 int jk_map_get_id(jk_map_t *m, const char *name)
161 COMPUTE_KEY_CHECKSUM(name, key)
162 for (i = 0; i < m->size; i++) {
163 if (m->keys[i] == key && strcmp(m->names[i], name) == 0) {
173 const char *jk_map_get_string(jk_map_t *m, const char *name, const char *def)
175 const char *rc = def;
180 COMPUTE_KEY_CHECKSUM(name, key)
181 for (i = 0; i < m->size; i++) {
182 if (m->keys[i] == key && strcmp(m->names[i], name) == 0) {
193 int jk_map_get_int(jk_map_t *m, const char *name, int def)
201 sprintf(buf, "%d", def);
202 rc = jk_map_get_string(m, name, buf);
206 char *lastchar = &buf[0] + len - 1;
208 if ('m' == *lastchar || 'M' == *lastchar) {
210 multit = 1024 * 1024;
212 else if ('k' == *lastchar || 'K' == *lastchar) {
221 return int_res * multit;
224 double jk_map_get_double(jk_map_t *m, const char *name, double def)
229 sprintf(buf, "%f", def);
230 rc = jk_map_get_string(m, name, buf);
235 int jk_map_get_bool(jk_map_t *m, const char *name, int def)
240 sprintf(buf, "%d", def);
241 rc = jk_map_get_string(m, name, buf);
243 return jk_get_bool_code(rc, def);
246 char **jk_map_get_string_list(jk_map_t *m,
248 unsigned int *list_len, const char *def)
250 const char *l = jk_map_get_string(m, name, def);
253 #ifdef _MT_CODE_PTHREAD
260 unsigned capacity = 0;
263 char *v = jk_pool_strdup(&m->p, l);
270 * GS, in addition to VG's patch, we now need to
271 * strtok also by a "*"
273 #ifdef _MT_CODE_PTHREAD
274 for (p = strtok_r(v, " \t,", &lasts);
275 p; p = strtok_r(NULL, " \t,", &lasts))
277 for (p = strtok(v, " \t,"); p; p = strtok(NULL, " \t,"))
282 if (idex == capacity) {
283 ar = jk_pool_realloc(&m->p,
284 sizeof(char *) * (capacity + 5),
285 ar, sizeof(char *) * capacity);
291 ar[idex] = jk_pool_strdup(&m->p, p);
301 int jk_map_get_int_list(jk_map_t *m,
304 unsigned int list_len,
307 const char *l = jk_map_get_string(m, name, def);
309 #ifdef _MT_CODE_PTHREAD
317 unsigned int capacity = list_len;
318 unsigned int index = 0;
320 char *v = jk_pool_strdup(&m->p, l);
327 * GS, in addition to VG's patch, we now need to
328 * strtok also by a "*"
330 #ifdef _MT_CODE_PTHREAD
331 for (p = strtok_r(v, " \t,", &lasts);
332 p; p = strtok_r(NULL, " \t,", &lasts))
334 for (p = strtok(v, " \t,"); p; p = strtok(NULL, " \t,"))
338 if (index < capacity) {
339 list[index] = atoi(p);
350 int jk_map_add(jk_map_t *m, const char *name, const void *value)
356 COMPUTE_KEY_CHECKSUM(name, key)
359 if (m->size < m->capacity) {
360 m->values[m->size] = value;
361 m->names[m->size] = jk_pool_strdup(&m->p, name);
362 m->keys[m->size] = key;
371 int jk_map_put(jk_map_t *m, const char *name, const void *value, void **old)
378 COMPUTE_KEY_CHECKSUM(name, key)
379 for (i = 0; i < m->size; i++) {
380 if (m->keys[i] == key && strcmp(m->names[i], name) == 0) {
387 *old = (void *)m->values[i]; /* DIRTY */
388 m->values[i] = value;
392 rc = jk_map_add(m, name, value);
400 static int jk_map_validate_property(char *prp, jk_logger_t *l)
402 /* check the worker properties */
403 if (!jk_is_valid_property(prp)) {
404 jk_log(l, JK_LOG_ERROR,
405 "The attribute '%s' is not supported - please check"
406 " the documentation for the supported attributes.",
410 if (jk_is_deprecated_property(prp)) {
411 jk_log(l, JK_LOG_WARNING,
412 "The attribute '%s' is deprecated - please check"
413 " the documentation for the correct replacement.",
419 static int jk_map_handle_duplicates(jk_map_t *m, const char *prp, char **v,
420 int treatment, jk_logger_t *l)
422 const char *oldv = jk_map_get_string(m, prp, NULL);
424 if ((treatment == JK_MAP_HANDLE_DUPLICATES)
425 && jk_is_unique_property(prp) == JK_FALSE) {
426 char *tmpv = jk_pool_alloc(&m->p,
427 strlen(*v) + strlen(oldv) + 3);
430 if (jk_is_path_property(prp))
431 sep = PATH_SEPERATOR;
432 else if (jk_is_cmd_line_property(prp))
434 else if (jk_is_list_property(prp))
436 sprintf(tmpv, "%s%c%s", oldv, sep, *v);
439 if (JK_IS_DEBUG_LEVEL(l))
440 jk_log(l, JK_LOG_DEBUG,
441 "Concatenated value is: %s -> %s",
446 jk_log(l, JK_LOG_WARNING,
447 "Duplicate key '%s' detected - previous value '%s'"
448 " will be overwritten with '%s'.",
449 prp, oldv ? oldv : "(null)", v ? *v : "(null)");
458 int jk_map_read_property(jk_map_t *m, jk_map_t *env, const char *str,
459 int treatment, jk_logger_t *l)
462 char buf[LENGTH_OF_LINE + 1];
465 if (strlen(str) > LENGTH_OF_LINE) {
466 jk_log(l, JK_LOG_WARNING,
467 "Line to long (%d > %d), ignoring entry",
468 strlen(str), LENGTH_OF_LINE);
474 char *v = strchr(prp, '=');
478 if (trim(v) && trim(prp)) {
479 if (treatment == JK_MAP_HANDLE_RAW) {
480 v = jk_pool_strdup(&m->p, v);
483 if (jk_map_validate_property(prp, l) == JK_FALSE)
485 v = jk_map_replace_properties(m, env, v);
486 if (jk_map_handle_duplicates(m, prp, &v, treatment, l) == JK_TRUE)
487 v = jk_pool_strdup(&m->p, v);
490 if (JK_IS_DEBUG_LEVEL(l))
491 jk_log(l, JK_LOG_DEBUG,
492 "Adding property '%s' with value '%s' to map.",
494 jk_map_put(m, prp, v, NULL);
497 JK_LOG_NULL_PARAMS(l);
507 int jk_map_read_properties(jk_map_t *m, jk_map_t *env, const char *f, time_t *modified,
508 int treatment, jk_logger_t *l)
515 if (jk_stat(f, &statbuf) == -1)
517 #if defined(AS400) && !defined(AS400_UTF8)
518 fp = fopen(f, "r, o_ccsid=0");
524 char buf[LENGTH_OF_LINE + 1];
529 while (NULL != (prp = fgets(buf, LENGTH_OF_LINE, fp))) {
530 trim_prp_comment(prp);
532 if ((rc = jk_map_read_property(m, env, prp, treatment, l)) == JK_FALSE)
538 *modified = statbuf.st_mtime;
546 int jk_map_size(jk_map_t *m)
555 const char *jk_map_name_at(jk_map_t *m, int idex)
557 if (m && idex >= 0) {
558 return m->names[idex]; /* DIRTY */
564 void *jk_map_value_at(jk_map_t *m, int idex)
566 if (m && idex >= 0) {
567 return (void *)m->values[idex]; /* DIRTY */
573 void jk_map_dump(jk_map_t *m, jk_logger_t *l)
576 int s = jk_map_size(m);
579 if (!jk_map_name_at(m, i)) {
580 jk_log(l, JK_LOG_WARNING,
581 "Map contains empty name at index %d\n", i);
583 if (!jk_map_value_at(m, i)) {
584 jk_log(l, JK_LOG_WARNING,
585 "Map contains empty value for name '%s' at index %d\n",
586 jk_map_name_at(m, i), i);
588 if (JK_IS_DEBUG_LEVEL(l)) {
589 jk_log(l, JK_LOG_DEBUG,
590 "Dump of map: '%s' -> '%s'",
591 jk_map_name_at(m, i) ? jk_map_name_at(m, i) : "(null)",
592 jk_map_value_at(m, i) ? jk_map_value_at(m, i) : "(null)");
598 int jk_map_copy(jk_map_t *src, jk_map_t *dst)
600 int sz = jk_map_size(src);
602 for (i = 0; i < sz; i++) {
603 const char *name = jk_map_name_at(src, i);
604 if (jk_map_get(dst, name, NULL) == NULL) {
605 if (!jk_map_put(dst, name,
606 jk_pool_strdup(&dst->p, jk_map_get_string(src, name, NULL)),
616 static void trim_prp_comment(char *prp)
618 #if defined(AS400) && !defined(AS400_UTF8)
620 /* lots of lines that translate a '#' realtime deleted */
621 comment = strchr(prp, *APR_NUMBERSIGN);
623 char *comment = strchr(prp, '#');
630 static size_t trim(char *s)
635 /* check for empty strings */
636 if (!(len = strlen(s)))
638 for (len = len - 1; (len > 0) &&
639 isspace((int)((unsigned char)s[len])); len--);
640 if ((len > 0) || !isspace((int)((unsigned char)s[len]))) {
647 for (first = 0; (s[first] != '\0') &&
648 isspace((int)((unsigned char)s[first])); first++);
651 memmove(s, s + first, len - first);
657 static int map_realloc(jk_map_t *m)
659 if (m->size == m->capacity) {
663 int capacity = m->capacity + CAPACITY_INC_SIZE;
665 names = (char **)jk_pool_alloc(&m->p, sizeof(char *) * capacity);
666 values = (void **)jk_pool_alloc(&m->p, sizeof(void *) * capacity);
667 keys = (unsigned int *)jk_pool_alloc(&m->p, sizeof(unsigned int) * capacity);
669 if (values && names) {
670 if (m->capacity && m->names)
671 memcpy(names, m->names, sizeof(char *) * m->capacity);
673 if (m->capacity && m->values)
674 memcpy(values, m->values, sizeof(void *) * m->capacity);
676 if (m->capacity && m->keys)
677 memcpy(keys, m->keys, sizeof(unsigned int) * m->capacity);
679 m->names = (const char **)names;
680 m->values = (const void **)values;
682 m->capacity = capacity;
692 * Replace $(property) in value.
695 static char *jk_map_replace_properties(jk_map_t *m, jk_map_t *env, char *value)
698 char *env_start = rc;
701 while ((env_start = strstr(env_start, "$(")) != NULL) {
702 char *env_end = strstr(env_start, ")");
706 char env_name[LENGTH_OF_LINE + 1] = "";
707 const char *env_value;
709 char env_buf[LENGTH_OF_LINE + 1];
712 strcpy(env_name, env_start + 2);
715 env_value = jk_map_get_string(m, env_name, NULL);
717 env_value = getenv(env_name);
719 if (!env_value && env) {
720 /* Search inside local environment table */
721 env_value = jk_map_get_string(env, env_name, NULL);
726 /* Try the env block from calling process */
727 if (GetEnvironmentVariable(env_name, env_buf,
729 env_value = &env_buf[0];
734 char *new_value = jk_pool_alloc(&m->p,
737 strlen(env_value))));
742 strcpy(new_value, rc);
743 strcat(new_value, env_value);
744 strcat(new_value, env_end + 1);
745 offset = env_start - rc + strlen(env_value);
747 /* Avoid recursive subst */
748 env_start = rc + offset;
766 int jk_map_resolve_references(jk_map_t *m, const char *prefix,
767 int wildcard, int depth, jk_logger_t *l)
774 if (depth <= JK_MAP_RECURSION) {
775 size_t prelen = strlen(prefix);
778 if (JK_IS_DEBUG_LEVEL(l))
779 jk_log(l, JK_LOG_DEBUG,
780 "Checking for references with prefix %s with%s wildcard (recursion %d)",
781 prefix, wildcard? "" : "out", depth);
782 for (i = 0; i < m->size; i++) {
783 char *v = (char *)m->values[i];
785 !strncmp(m->names[i], prefix, prelen)) {
786 size_t remain = strlen(m->names[i]) - prelen;
787 if ((remain == JK_MAP_REFERENCE_SZ ) || (wildcard && remain > JK_MAP_REFERENCE_SZ)) {
788 remain = strlen(m->names[i]) - JK_MAP_REFERENCE_SZ;
789 if (!strncmp(m->names[i] + remain, JK_MAP_REFERENCE, JK_MAP_REFERENCE_SZ)) {
790 char *from = jk_pool_alloc(&m->p,
793 char *to = jk_pool_alloc(&m->p,
797 jk_log(l, JK_LOG_ERROR,
798 "Error in string allocation");
803 *(from+strlen(v)) = '.';
804 *(from+strlen(v)+1) = '\0';
805 strncpy(to, m->names[i], remain);
807 *(to+remain+1) = '\0';
809 rc = jk_map_resolve_references(m, v, 0, depth+1, l);
810 if (rc == JK_FALSE) {
813 if (JK_IS_DEBUG_LEVEL(l))
814 jk_log(l, JK_LOG_DEBUG,
815 "Copying values from %s to %s",
817 rc = jk_map_inherit_properties(m, from, to, l);
818 if (rc == JK_FALSE) {
827 jk_log(l, JK_LOG_ERROR,
828 "Recursion limit %d for worker references with prefix '%s' reached",
829 JK_MAP_RECURSION, prefix);
833 JK_LOG_NULL_PARAMS(l);
843 int jk_map_inherit_properties(jk_map_t *m, const char *from, const char *to, jk_logger_t *l)
849 if (m && from && to) {
851 for (i = 0; i < m->size; i++) {
852 if (!strncmp(m->names[i], from, strlen(from))) {
854 prp = m->names[i] + strlen(from);
855 to_prp = jk_pool_alloc(&m->p,
860 jk_log(l, JK_LOG_ERROR,
861 "Error in string allocation for attribute '%s.%s'",
868 if (jk_map_get_id(m, to_prp) < 0 ) {
869 rc = jk_map_add(m, to_prp, m->values[i]);
870 if (rc == JK_FALSE) {
871 jk_log(l, JK_LOG_ERROR,
872 "Error when adding attribute '%s'",
879 if ( rc == JK_FALSE) {
880 jk_log(l, JK_LOG_ERROR,
881 "Reference '%s' not found",
886 JK_LOG_NULL_PARAMS(l);