bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / tomcat-connectors-1.2.32-src / native / common / jk_map.c
1 /*
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
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"
26 #endif
27
28 #include "jk_global.h"
29 #include "jk_pool.h"
30 #include "jk_util.h"
31 #include "jk_map.h"
32
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))
38
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
43  * skip a strcmp
44  */
45 #define COMPUTE_KEY_CHECKSUM(key, checksum)    \
46 {                                              \
47     const char *k = (key);                     \
48     unsigned int c = (unsigned int)*k;         \
49     (checksum) = c;                            \
50     (checksum) <<= 8;                          \
51     if (c) {                                   \
52         c = (unsigned int)*++k;                \
53         checksum |= c;                         \
54     }                                          \
55     (checksum) <<= 8;                          \
56     if (c) {                                   \
57         c = (unsigned int)*++k;                \
58         checksum |= c;                         \
59     }                                          \
60     (checksum) <<= 8;                          \
61     if (c) {                                   \
62         c = (unsigned int)*++k;                \
63         checksum |= c;                         \
64     }                                          \
65 }
66
67 struct jk_map
68 {
69     jk_pool_t p;
70     jk_pool_atom_t buf[SMALL_POOL_SIZE];
71
72     const char **names;
73     const void **values;
74     unsigned int *keys;
75
76     unsigned int capacity;
77     unsigned int size;
78 };
79
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);
84
85 int jk_map_alloc(jk_map_t **m)
86 {
87     if (m) {
88         return jk_map_open(*m = (jk_map_t *)malloc(sizeof(jk_map_t)));
89     }
90
91     return JK_FALSE;
92 }
93
94 int jk_map_free(jk_map_t **m)
95 {
96     int rc = JK_FALSE;
97
98     if (m && *m) {
99         jk_map_close(*m);
100         free(*m);
101         *m = NULL;
102     }
103
104     return rc;
105 }
106
107 int jk_map_open(jk_map_t *m)
108 {
109     int rc = JK_FALSE;
110
111     if (m) {
112         jk_open_pool(&m->p, m->buf, sizeof(jk_pool_atom_t) * SMALL_POOL_SIZE);
113         m->capacity = 0;
114         m->size = 0;
115         m->keys  = NULL;
116         m->names = NULL;
117         m->values = NULL;
118         rc = JK_TRUE;
119     }
120
121     return rc;
122 }
123
124 int jk_map_close(jk_map_t *m)
125 {
126     int rc = JK_FALSE;
127
128     if (m) {
129         jk_close_pool(&m->p);
130         rc = JK_TRUE;
131     }
132
133     return rc;
134 }
135
136 void *jk_map_get(jk_map_t *m, const char *name, const void *def)
137 {
138     const void *rc = (void *)def;
139
140     if (m && name) {
141         unsigned int i;
142         unsigned int key;
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) {
146                 rc = m->values[i];
147                 break;
148             }
149         }
150     }
151
152     return (void *)rc;          /* DIRTY */
153 }
154
155 int jk_map_get_id(jk_map_t *m, const char *name)
156 {
157     int rc = -1;
158     if (m && name) {
159         unsigned int i;
160         unsigned int key;
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) {
164                 rc = i;
165                 break;
166             }
167         }
168     }
169
170     return rc;
171 }
172
173 const char *jk_map_get_string(jk_map_t *m, const char *name, const char *def)
174 {
175     const char *rc = def;
176
177     if (m && name) {
178         unsigned int i;
179         unsigned int key;
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) {
183                 rc = m->values[i];
184                 break;
185             }
186         }
187     }
188
189     return rc;
190 }
191
192
193 int jk_map_get_int(jk_map_t *m, const char *name, int def)
194 {
195     char buf[100];
196     const char *rc;
197     size_t len;
198     int int_res;
199     int multit = 1;
200
201     sprintf(buf, "%d", def);
202     rc = jk_map_get_string(m, name, buf);
203
204     len = strlen(rc);
205     if (len) {
206         char *lastchar = &buf[0] + len - 1;
207         strcpy(buf, rc);
208         if ('m' == *lastchar || 'M' == *lastchar) {
209             *lastchar = '\0';
210             multit = 1024 * 1024;
211         }
212         else if ('k' == *lastchar || 'K' == *lastchar) {
213             *lastchar = '\0';
214             multit = 1024;
215         }
216         int_res = atoi(buf);
217     }
218     else
219         int_res = def;
220
221     return int_res * multit;
222 }
223
224 double jk_map_get_double(jk_map_t *m, const char *name, double def)
225 {
226     char buf[100];
227     const char *rc;
228
229     sprintf(buf, "%f", def);
230     rc = jk_map_get_string(m, name, buf);
231
232     return atof(rc);
233 }
234
235 int jk_map_get_bool(jk_map_t *m, const char *name, int def)
236 {
237     char buf[100];
238     const char *rc;
239
240     sprintf(buf, "%d", def);
241     rc = jk_map_get_string(m, name, buf);
242
243     return jk_get_bool_code(rc, def);
244 }
245
246 char **jk_map_get_string_list(jk_map_t *m,
247                               const char *name,
248                               unsigned int *list_len, const char *def)
249 {
250     const char *l = jk_map_get_string(m, name, def);
251     char **ar = NULL;
252
253 #ifdef _MT_CODE_PTHREAD
254     char *lasts;
255 #endif
256
257     *list_len = 0;
258
259     if (l) {
260         unsigned capacity = 0;
261         unsigned idex = 0;
262         char *p;
263         char *v = jk_pool_strdup(&m->p, l);
264
265         if (!v) {
266             return NULL;
267         }
268
269         /*
270          * GS, in addition to VG's patch, we now need to
271          * strtok also by a "*"
272          */
273 #ifdef _MT_CODE_PTHREAD
274         for (p = strtok_r(v, " \t,", &lasts);
275              p; p = strtok_r(NULL, " \t,", &lasts))
276 #else
277         for (p = strtok(v, " \t,"); p; p = strtok(NULL, " \t,"))
278 #endif
279
280         {
281
282             if (idex == capacity) {
283                 ar = jk_pool_realloc(&m->p,
284                                      sizeof(char *) * (capacity + 5),
285                                      ar, sizeof(char *) * capacity);
286                 if (!ar) {
287                     return JK_FALSE;
288                 }
289                 capacity += 5;
290             }
291             ar[idex] = jk_pool_strdup(&m->p, p);
292             idex++;
293         }
294
295         *list_len = idex;
296     }
297
298     return ar;
299 }
300
301 int jk_map_get_int_list(jk_map_t *m,
302                         const char *name,
303                         int *list,
304                         unsigned int list_len,
305                         const char *def)
306 {
307     const char *l = jk_map_get_string(m, name, def);
308
309 #ifdef _MT_CODE_PTHREAD
310     char *lasts;
311 #endif
312
313     if (!list_len)
314         return 0;
315
316     if (l) {
317         unsigned int capacity = list_len;
318         unsigned int index = 0;
319         char *p;
320         char *v = jk_pool_strdup(&m->p, l);
321
322         if (!v) {
323             return 0;
324         }
325
326         /*
327          * GS, in addition to VG's patch, we now need to
328          * strtok also by a "*"
329          */
330 #ifdef _MT_CODE_PTHREAD
331         for (p = strtok_r(v, " \t,", &lasts);
332              p; p = strtok_r(NULL, " \t,", &lasts))
333 #else
334         for (p = strtok(v, " \t,"); p; p = strtok(NULL, " \t,"))
335 #endif
336
337         {
338             if (index < capacity) {
339                 list[index] = atoi(p);
340                 index++;
341             }
342             else
343                 break;
344         }
345         return index;
346     }
347     return 0;
348 }
349
350 int jk_map_add(jk_map_t *m, const char *name, const void *value)
351 {
352     int rc = JK_FALSE;
353
354     if (m && name) {
355         unsigned int key;
356         COMPUTE_KEY_CHECKSUM(name, key)
357         map_realloc(m);
358
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;
363             m->size++;
364             rc = JK_TRUE;
365         }
366     }
367
368     return rc;
369 }
370
371 int jk_map_put(jk_map_t *m, const char *name, const void *value, void **old)
372 {
373     int rc = JK_FALSE;
374
375     if (m && name) {
376         unsigned int i;
377         unsigned int key;
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) {
381                 break;
382             }
383         }
384
385         if (i < m->size) {
386             if (old)
387                 *old = (void *)m->values[i];        /* DIRTY */
388             m->values[i] = value;
389             rc = JK_TRUE;
390         }
391         else {
392             rc = jk_map_add(m, name, value);
393         }
394     }
395
396     return rc;
397 }
398
399
400 static int jk_map_validate_property(char *prp, jk_logger_t *l)
401 {
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.",
407                prp);
408         return JK_FALSE;
409     }
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.",
414                prp);
415     }
416     return JK_TRUE;
417 }
418
419 static int jk_map_handle_duplicates(jk_map_t *m, const char *prp, char **v,
420                                     int treatment, jk_logger_t *l)
421 {
422     const char *oldv = jk_map_get_string(m, prp, NULL);
423     if (oldv) {
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);
428             if (tmpv) {
429                 char sep = '*';
430                 if (jk_is_path_property(prp))
431                     sep = PATH_SEPERATOR;
432                 else if (jk_is_cmd_line_property(prp))
433                     sep = ' ';
434                 else if (jk_is_list_property(prp))
435                     sep = ',';
436                 sprintf(tmpv, "%s%c%s", oldv, sep, *v);
437             }
438             *v = tmpv;
439             if (JK_IS_DEBUG_LEVEL(l))
440                 jk_log(l, JK_LOG_DEBUG,
441                        "Concatenated value is: %s -> %s",
442                        prp, *v);
443             return JK_FALSE;
444         }
445         else {
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)");
450             return JK_TRUE;
451         }
452     }
453     else {
454         return JK_TRUE;
455     }
456 }
457
458 int jk_map_read_property(jk_map_t *m, jk_map_t *env, const char *str,
459                          int treatment, jk_logger_t *l)
460 {
461     int rc = JK_TRUE;
462     char buf[LENGTH_OF_LINE + 1];
463     char *prp = &buf[0];
464
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);
469         return JK_FALSE;
470     }
471
472     strcpy(prp, str);
473     if (trim(prp)) {
474         char *v = strchr(prp, '=');
475         if (v) {
476             *v = '\0';
477             v++;
478             if (trim(v) && trim(prp)) {
479                 if (treatment == JK_MAP_HANDLE_RAW) {
480                     v = jk_pool_strdup(&m->p, v);
481                 }
482                 else {
483                     if (jk_map_validate_property(prp, l) == JK_FALSE)
484                         return 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);
488                 }
489                 if (v) {
490                     if (JK_IS_DEBUG_LEVEL(l))
491                         jk_log(l, JK_LOG_DEBUG,
492                                "Adding property '%s' with value '%s' to map.",
493                                prp, v);
494                     jk_map_put(m, prp, v, NULL);
495                 }
496                 else {
497                     JK_LOG_NULL_PARAMS(l);
498                     rc = JK_FALSE;
499                 }
500             }
501         }
502     }
503     return rc;
504 }
505
506
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)
509 {
510     int rc = JK_FALSE;
511
512     if (m && f) {
513         struct stat statbuf;
514         FILE *fp;
515         if (jk_stat(f, &statbuf) == -1)
516             return JK_FALSE;
517 #if defined(AS400) && !defined(AS400_UTF8)
518         fp = fopen(f, "r, o_ccsid=0");
519 #else
520         fp = fopen(f, "r");
521 #endif
522
523         if (fp) {
524             char buf[LENGTH_OF_LINE + 1];
525             char *prp;
526
527             rc = JK_TRUE;
528
529             while (NULL != (prp = fgets(buf, LENGTH_OF_LINE, fp))) {
530                 trim_prp_comment(prp);
531                 if (*prp) {
532                     if ((rc = jk_map_read_property(m, env, prp, treatment, l)) == JK_FALSE)
533                         break;
534                 }
535             }
536             fclose(fp);
537             if (modified)
538                 *modified = statbuf.st_mtime;
539         }
540     }
541
542     return rc;
543 }
544
545
546 int jk_map_size(jk_map_t *m)
547 {
548     if (m) {
549         return m->size;
550     }
551
552     return -1;
553 }
554
555 const char *jk_map_name_at(jk_map_t *m, int idex)
556 {
557     if (m && idex >= 0) {
558         return m->names[idex];  /* DIRTY */
559     }
560
561     return NULL;
562 }
563
564 void *jk_map_value_at(jk_map_t *m, int idex)
565 {
566     if (m && idex >= 0) {
567         return (void *)m->values[idex]; /* DIRTY */
568     }
569
570     return NULL;
571 }
572
573 void jk_map_dump(jk_map_t *m, jk_logger_t *l)
574 {
575     if (m) {
576         int s = jk_map_size(m);
577         int i;
578         for (i=0;i<s;i++) {
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);
582             }
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);
587             }
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)");
593             }
594         }
595     }
596 }
597
598 int jk_map_copy(jk_map_t *src, jk_map_t *dst)
599 {
600     int sz = jk_map_size(src);
601     int i;
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)),
607                             NULL)) {
608                 return JK_FALSE;
609             }
610         }
611     }
612     return JK_TRUE;
613 }
614
615
616 static void trim_prp_comment(char *prp)
617 {
618 #if defined(AS400) && !defined(AS400_UTF8)
619     char *comment;
620     /* lots of lines that translate a '#' realtime deleted   */
621     comment = strchr(prp, *APR_NUMBERSIGN);
622 #else
623     char *comment = strchr(prp, '#');
624 #endif
625     if (comment) {
626         *comment = '\0';
627     }
628 }
629
630 static size_t trim(char *s)
631 {
632     size_t first;
633     size_t len;
634
635     /* check for empty strings */
636     if (!(len = strlen(s)))
637         return 0;
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]))) {
641         len++;
642     }
643
644     s[len] = '\0';
645     len++;
646
647     for (first = 0; (s[first] != '\0') &&
648         isspace((int)((unsigned char)s[first])); first++);
649
650     if (first > 0) {
651         memmove(s, s + first, len - first);
652     }
653
654     return len;
655 }
656
657 static int map_realloc(jk_map_t *m)
658 {
659     if (m->size == m->capacity) {
660         char **names;
661         void **values;
662         unsigned int *keys;
663         int capacity = m->capacity + CAPACITY_INC_SIZE;
664
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);
668
669         if (values && names) {
670             if (m->capacity && m->names)
671                 memcpy(names, m->names, sizeof(char *) * m->capacity);
672
673             if (m->capacity && m->values)
674                 memcpy(values, m->values, sizeof(void *) * m->capacity);
675
676             if (m->capacity && m->keys)
677                 memcpy(keys, m->keys, sizeof(unsigned int) * m->capacity);
678
679             m->names = (const char **)names;
680             m->values = (const void **)values;
681             m->keys = keys;
682             m->capacity = capacity;
683
684             return JK_TRUE;
685         }
686     }
687
688     return JK_FALSE;
689 }
690
691 /**
692  *  Replace $(property) in value.
693  *
694  */
695 static char *jk_map_replace_properties(jk_map_t *m, jk_map_t *env, char *value)
696 {
697     char *rc = value;
698     char *env_start = rc;
699     int rec = 0;
700
701     while ((env_start = strstr(env_start, "$(")) != NULL) {
702         char *env_end = strstr(env_start, ")");
703         if (rec++ > 20)
704             return rc;
705         if (env_end) {
706             char env_name[LENGTH_OF_LINE + 1] = "";
707             const char *env_value;
708 #if defined(WIN32)
709             char env_buf[LENGTH_OF_LINE + 1];
710 #endif
711             *env_end = '\0';
712             strcpy(env_name, env_start + 2);
713             *env_end = ')';
714
715             env_value = jk_map_get_string(m, env_name, NULL);
716             if (!env_value) {
717                 env_value = getenv(env_name);
718             }
719             if (!env_value && env) {
720                 /* Search inside local environment table */
721                 env_value = jk_map_get_string(env, env_name, NULL);
722             }
723
724 #if defined(WIN32)
725             if (!env_value) {
726                 /* Try the env block from calling process */
727                 if (GetEnvironmentVariable(env_name, env_buf,
728                                            sizeof(env_buf)))
729                     env_value = &env_buf[0];
730             }
731 #endif
732             if (env_value) {
733                 size_t offset = 0;
734                 char *new_value = jk_pool_alloc(&m->p,
735                                                 (sizeof(char) *
736                                                 (strlen(rc) +
737                                                 strlen(env_value))));
738                 if (!new_value) {
739                     break;
740                 }
741                 *env_start = '\0';
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);
746                 rc = new_value;
747                 /* Avoid recursive subst */
748                 env_start = rc + offset;
749             }
750             else {
751                 env_start = env_end;
752             }
753         }
754         else {
755             break;
756         }
757     }
758
759     return rc;
760 }
761
762 /**
763  *  Resolve references
764  *
765  */
766 int jk_map_resolve_references(jk_map_t *m, const char *prefix,
767                               int wildcard, int depth, jk_logger_t *l)
768 {
769     int rc = JK_FALSE;
770
771     JK_TRACE_ENTER(l);
772
773     if (m && prefix) {
774         if (depth <= JK_MAP_RECURSION) {
775             size_t prelen = strlen(prefix);
776             unsigned int i;
777             rc = JK_TRUE;
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];
784                 if (v && *v &&
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,
791                                                        (sizeof(char) *
792                                                        (strlen(v) + 2)));
793                             char *to = jk_pool_alloc(&m->p,
794                                                      (sizeof(char) *
795                                                      (remain + 2)));
796                             if (!from || !to) {
797                                 jk_log(l, JK_LOG_ERROR,
798                                        "Error in string allocation");
799                                 rc = JK_FALSE;
800                                 break;
801                             }
802                             strcpy(from, v);
803                             *(from+strlen(v))   = '.';
804                             *(from+strlen(v)+1) = '\0';
805                             strncpy(to, m->names[i], remain);
806                             *(to+remain)   = '.';
807                             *(to+remain+1) = '\0';
808
809                             rc = jk_map_resolve_references(m, v, 0, depth+1, l);
810                             if (rc == JK_FALSE) {
811                                 break;
812                             }
813                             if (JK_IS_DEBUG_LEVEL(l))
814                                 jk_log(l, JK_LOG_DEBUG,
815                                        "Copying values from %s to %s",
816                                        from, to);
817                             rc = jk_map_inherit_properties(m, from, to, l);
818                             if (rc == JK_FALSE) {
819                                 break;
820                             }
821                         }
822                     }
823                 }
824             }
825         }
826         else {
827             jk_log(l, JK_LOG_ERROR,
828                    "Recursion limit %d for worker references with prefix '%s' reached",
829                    JK_MAP_RECURSION, prefix);
830         }
831     }
832     else {
833         JK_LOG_NULL_PARAMS(l);
834     }
835     JK_TRACE_EXIT(l);
836     return rc;
837 }
838
839 /**
840  *  Inherit properties
841  *
842  */
843 int jk_map_inherit_properties(jk_map_t *m, const char *from, const char *to, jk_logger_t *l)
844 {
845     int rc = JK_FALSE;
846     const char *prp;
847     char *to_prp;
848
849     if (m && from && to) {
850         unsigned int i;
851         for (i = 0; i < m->size; i++) {
852             if (!strncmp(m->names[i], from, strlen(from))) {
853                 rc = JK_TRUE;
854                 prp = m->names[i] + strlen(from);
855                 to_prp = jk_pool_alloc(&m->p,
856                                        (sizeof(char) *
857                                        (strlen(to) +
858                                        strlen(prp) + 1)));
859                 if (!to_prp) {
860                     jk_log(l, JK_LOG_ERROR,
861                            "Error in string allocation for attribute '%s.%s'",
862                            to, prp);
863                     rc = JK_FALSE;
864                     break;
865                 }
866                 strcpy(to_prp, to);
867                 strcat(to_prp, prp);
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'",
873                                to_prp);
874                         break;
875                     }
876                 }
877             }
878         }
879         if ( rc == JK_FALSE) {
880             jk_log(l, JK_LOG_ERROR,
881                    "Reference '%s' not found",
882                    from);
883         }
884     }
885     else {
886         JK_LOG_NULL_PARAMS(l);
887     }
888     return rc;
889 }