blob: 2a5e5af9b4663fe72f5f8231dd5d5276bbdea85c [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed 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$ *
***************************************************************************/
#ifdef AS400
#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))
#ifdef AS400
#define CASE_MASK 0xbfbfbfbf
#else
#define CASE_MASK 0xdfdfdfdf
#endif
/* Compute the "checksum" for a key, consisting of the first
* 4 bytes, normalized for case-insensitivity and packed into
* an int...this checksum allows us to do a single integer
* comparison as a fast check to determine whether we can
* skip a strcasecmp
*/
#define COMPUTE_KEY_CHECKSUM(key, checksum) \
{ \
const char *k = (key); \
unsigned int c = (unsigned int)*k; \
(checksum) = c; \
(checksum) <<= 8; \
if (c) { \
c = (unsigned int)*++k; \
checksum |= c; \
} \
(checksum) <<= 8; \
if (c) { \
c = (unsigned int)*++k; \
checksum |= c; \
} \
(checksum) <<= 8; \
if (c) { \
c = (unsigned int)*++k; \
checksum |= c; \
} \
checksum &= CASE_MASK; \
}
struct jk_map
{
jk_pool_t p;
jk_pool_atom_t buf[SMALL_POOL_SIZE];
const char **names;
const void **values;
unsigned int *keys;
unsigned int capacity;
unsigned int size;
};
static void trim_prp_comment(char *prp);
static size_t trim(char *s);
static int map_realloc(jk_map_t *m);
int jk_map_alloc(jk_map_t **m)
{
if (m) {
return jk_map_open(*m = (jk_map_t *)malloc(sizeof(jk_map_t)));
}
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->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 key;
COMPUTE_KEY_CHECKSUM(name, key)
for (i = 0; i < m->size; i++) {
if (m->keys[i] == key && 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 key;
COMPUTE_KEY_CHECKSUM(name, key)
for (i = 0; i < m->size; i++) {
if (m->keys[i] == key && 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 key;
COMPUTE_KEY_CHECKSUM(name, key)
for (i = 0; i < m->size; i++) {
if (m->keys[i] == key && 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;
int multit = 1;
sprintf(buf, "%d", def);
rc = jk_map_get_string(m, name, buf);
len = strlen(rc);
if (len) {
char *lastchar = &buf[0] + len - 1;
strcpy(buf, rc);
if ('m' == *lastchar || 'M' == *lastchar) {
*lastchar = '\0';
multit = 1024 * 1024;
}
else if ('k' == *lastchar || 'K' == *lastchar) {
*lastchar = '\0';
multit = 1024;
}
int_res = atoi(buf);
}
else
int_res = def;
return int_res * multit;
}
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];
size_t len;
const char *rc;
int rv = 0;
sprintf(buf, "%d", def);
rc = jk_map_get_string(m, name, buf);
len = strlen(rc);
if (len) {
if (strcasecmp(rc, "true") == 0 ||
*rc == 'Y' || *rc == 'y' || *rc == '1') {
rv = 1;
}
}
return rv;
}
char **jk_map_get_string_list(jk_map_t *m,
const char *name,
unsigned *list_len, const char *def)
{
const char *l = jk_map_get_string(m, name, def);
char **ar = NULL;
#if defined(AS400) || defined(_REENTRANT)
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 "*"
*/
#if defined(AS400) || defined(_REENTRANT)
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 JK_FALSE;
}
capacity += 5;
}
ar[idex] = jk_pool_strdup(&m->p, 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 key;
COMPUTE_KEY_CHECKSUM(name, key)
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] = key;
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 key;
COMPUTE_KEY_CHECKSUM(name, key)
for (i = 0; i < m->size; i++) {
if (m->keys[i] == key && strcasecmp(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;
}
int jk_map_read_property(jk_map_t *m, const char *str, jk_logger_t *l)
{
int rc = JK_TRUE;
char buf[LENGTH_OF_LINE + 1];
char *prp = &buf[0];
if (strlen(str) > LENGTH_OF_LINE)
return JK_FALSE;
strcpy(prp, str);
if (trim(prp)) {
char *v = strchr(prp, '=');
if (v) {
*v = '\0';
v++;
trim(prp);
trim(v);
if (strlen(v) && strlen(prp)) {
const char *oldv = jk_map_get_string(m, prp, NULL);
v = jk_map_replace_properties(m, v);
if (oldv && 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;
}
else {
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);
}
v = jk_pool_strdup(&m->p, v);
}
if (v) {
jk_map_put(m, prp, v, NULL);
}
else {
rc = JK_FALSE;
}
}
}
}
return rc;
}
int jk_map_read_properties(jk_map_t *m, const char *f, time_t *modified, jk_logger_t *l)
{
int rc = JK_FALSE;
if (m && f) {
struct stat statbuf;
FILE *fp;
if ((rc = stat(f, &statbuf)) == -1)
return JK_FALSE;
#ifdef AS400
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, prp, 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;
}
static void trim_prp_comment(char *prp)
{
#ifdef AS400
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 i;
/* check for empty strings */
if (!(i = strlen(s)))
return 0;
for (i = i - 1; (i >= 0) &&
isspace((int)((unsigned char)s[i])); i--);
s[i + 1] = '\0';
for (i = 0; ('\0' != s[i]) &&
isspace((int)((unsigned char)s[i])); i++);
if (i > 0) {
strcpy(s, &s[i]);
}
return strlen(s);
}
static int map_realloc(jk_map_t *m)
{
if (m->size == m->capacity) {
char **names;
void **values;
unsigned int *keys;
int capacity = m->capacity + CAPACITY_INC_SIZE;
names = (char **)jk_pool_alloc(&m->p, sizeof(char *) * capacity);
values = (void **)jk_pool_alloc(&m->p, sizeof(void *) * capacity);
keys = (unsigned int *)jk_pool_alloc(&m->p, sizeof(unsigned int) * capacity);
if (values && names) {
if (m->capacity && m->names)
memcpy(names, m->names, sizeof(char *) * m->capacity);
if (m->capacity && m->values)
memcpy(values, m->values, sizeof(void *) * m->capacity);
if (m->capacity && m->keys)
memcpy(keys, m->keys, sizeof(unsigned int) * m->capacity);
m->names = (const char **)names;
m->values = (const void **)values;
m->keys = keys;
m->capacity = capacity;
return JK_TRUE;
}
}
return JK_FALSE;
}
/**
* Replace $(property) in value.
*
*/
char *jk_map_replace_properties(jk_map_t *m, const char *value)
{
char *rc = (char *)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 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 && 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++) {
if (m->values[i] && !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(m->values[i]) + 2)));
char *to = jk_pool_alloc(&m->p,
(sizeof(char) *
(remain + 2)));
if (!from || !to) {
rc = JK_FALSE;
break;
}
strcpy(from, m->values[i]);
*(from+strlen(m->values[i])) = '.';
*(from+strlen(m->values[i])+1) = '\0';
strncpy(to, m->names[i], remain);
*(to+remain) = '.';
*(to+remain+1) = '\0';
rc = jk_map_resolve_references(m, m->values[i], 0, ++depth, 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);
if (rc == JK_FALSE) {
break;
}
m->values[i] = NULL;
}
}
}
}
}
JK_TRACE_EXIT(l);
return rc;
}
/**
* Inherit properties
*
*/
int jk_map_inherit_properties(jk_map_t *m, const char *from, const char *to)
{
int rc = JK_FALSE;
if (m && from && to) {
unsigned int i;
rc = JK_TRUE;
for (i = 0; i < m->size; i++) {
if (!strncmp(m->names[i], from, strlen(from))) {
const char *prp = m->names[i] + strlen(from);
char *to_prp = jk_pool_alloc(&m->p,
(sizeof(char) *
(strlen(to) +
strlen(prp) + 1)));
if (!to_prp) {
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) {
break;
}
}
}
}
}
return rc;
}