[asterisk-commits] nadi: branch nadi/trunk-cm r43886 - in /team/nadi/trunk-cm: include/asterisk/...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Thu Sep 28 09:01:28 MST 2006


Author: nadi
Date: Thu Sep 28 11:01:26 2006
New Revision: 43886

URL: http://svn.digium.com/view/asterisk?rev=43886&view=rev
Log:
add: hash and configman, still experimental, work in progress

Added:
    team/nadi/trunk-cm/include/asterisk/configman.h   (with props)
    team/nadi/trunk-cm/include/asterisk/hash.h   (with props)
    team/nadi/trunk-cm/main/configman.c   (with props)
    team/nadi/trunk-cm/main/hash.c   (with props)
Modified:
    team/nadi/trunk-cm/include/asterisk/cli.h
    team/nadi/trunk-cm/main/Makefile

Modified: team/nadi/trunk-cm/include/asterisk/cli.h
URL: http://svn.digium.com/view/asterisk/team/nadi/trunk-cm/include/asterisk/cli.h?rev=43886&r1=43885&r2=43886&view=diff
==============================================================================
--- team/nadi/trunk-cm/include/asterisk/cli.h (original)
+++ team/nadi/trunk-cm/include/asterisk/cli.h Thu Sep 28 11:01:26 2006
@@ -46,7 +46,7 @@
 
 /*! \brief A command line entry */
 struct ast_cli_entry {
-	char * const cmda[AST_MAX_CMD_LEN];
+	char * cmda[AST_MAX_CMD_LEN];
 	/*! Handler for the command (fd for output, # of args, argument list).
 	  Returns RESULT_SHOWUSAGE for improper arguments.
 	  argv[] has argc 'useful' entries, and an additional NULL entry
@@ -57,9 +57,9 @@
 	 */
 	int (*handler)(int fd, int argc, char *argv[]);
 	/*! Summary of the command (< 60 characters) */
-	const char *summary;
+	char *summary;
 	/*! Detailed usage information */
-	const char *usage;
+	char *usage;
 	/*! Generate the n-th (starting from 0) possible completion
 	  for a given 'word' following 'line' in position 'pos'.
 	  'line' and 'word' must not be modified.

Added: team/nadi/trunk-cm/include/asterisk/configman.h
URL: http://svn.digium.com/view/asterisk/team/nadi/trunk-cm/include/asterisk/configman.h?rev=43886&view=auto
==============================================================================
--- team/nadi/trunk-cm/include/asterisk/configman.h (added)
+++ team/nadi/trunk-cm/include/asterisk/configman.h Thu Sep 28 11:01:26 2006
@@ -1,0 +1,120 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi at beronet.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file configman.h
+ * \brief Configuration Management
+ * \author Nadi Sarrar <nadi at beronet.com>
+ */
+
+#ifndef _ASTERISK_CONFIGMAN_H
+#define _ASTERISK_CONFIGMAN_H
+
+#include <sys/types.h>
+
+typedef struct cm    cm_t;
+
+typedef struct {
+	char           * name;
+	char           * def;
+	char           * desc;
+} cm_dir_t;
+
+enum ktype {
+	KTYPE_NONE = 0,
+	KTYPE_INTEGER,
+	KTYPE_STRING,
+};
+
+typedef struct {
+	char           * section_name;      /* Name of the section. */
+	char           * default_name;      /* If you want this section to collect config categories with
+										   any name and have a special category for default values,
+										   name it here. */
+	char           * key_field;         /* If you specify a key field, then this will be the key to
+										   access values, specify the key_type below. */
+	enum ktype       key_type;          /* The type of the key field. */
+    int              num_directives;    /* Number of the directives array elements. */
+	const cm_dir_t * directives;        /* The first directive targets the string between the '[ ]'.
+										   If key_type == KEY_NONE, let the first one be empty, i.e.
+										   { 0, 0, 0 } or { } or { 0 } or whatever you like most. */
+} cm_section_t;
+
+/*! \brief Create a new config manager object
+ * \param modname Name to identify the owner, used in log messages and for registering cli commands like
+ *                {modname} show config [...]
+ * \param sections Array of sections, must be static or allocated until after cm_destroy
+ * \param num Number of elemenets in sections
+ * Creates a cm_t structure from given cm_section_t's.
+ *
+ * Returns NULL on error, or a cm_t data structure on success.
+ */
+cm_t *   cm_create (const char *modname, const cm_section_t *sections, int num);
+
+/*! \brief Load and parse a configuration file
+ * \param cm_t which cm_t to use
+ * \param filename path of file to open. If no preceding '/' character, path is considered relative to AST_CONFIG_DIR
+ * Fills the cm_t structure with data from a given configuration file.
+ *
+ * Returns non-zero on error.
+ */
+int      cm_load (cm_t *cm, const char *filename);
+
+/*! \brief Destroy a cm_t
+ * \param cm_t which cm_t to destroy
+ * Destroys and frees a given cm_t data structure.
+ */
+void     cm_destroy (cm_t *cm);
+
+/*! \brief Get a configuration value
+ * \param cm_t which cm_t to use
+ * \param buf where to put the result
+ * \param size size of buf
+ * \param sec_id section id to use
+ * \param elem_id element id to use
+ * \param ... integer or string id. omit this parameter, if the underlying cm_section_t is a single section type
+ * Reads a value from the cm_t data structure and copies it into buf.
+ *
+ * Returns non-zero on error.
+ */
+int      cm_get (cm_t *cm, char *buf, size_t size, int sec_id, int elem_id, ...);
+
+/*! \brief Validate an id
+ * \param cm_t which cm_t to use
+ * \param sec_id section id to use
+ * \param ... integer or string id
+ * Validates a given id.
+ *
+ * Returns true on success, zero if either id or sec_id is not valid.
+ */
+int      cm_id_valid (cm_t *cm, int sec_id, ...);
+
+/*! \brief Get the subsequent id
+ * \param cm_t which cm_t to use
+ * \param sec_id section id to use
+ * \param ... preceding integer or string id
+ * Gets the subsequent id of a given id.
+ *
+ * Returns a const pointer to the subsequent id or NULL on error, i.e.
+ * if the preceding ID was already the last one.
+ */
+const
+void *   cm_get_next_id (cm_t *cm, int sec_id, ...);
+
+int      ast_configman_init (void);
+
+#endif

Propchange: team/nadi/trunk-cm/include/asterisk/configman.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/nadi/trunk-cm/include/asterisk/configman.h
------------------------------------------------------------------------------
    svn:keywords = text/plain

Propchange: team/nadi/trunk-cm/include/asterisk/configman.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/nadi/trunk-cm/include/asterisk/hash.h
URL: http://svn.digium.com/view/asterisk/team/nadi/trunk-cm/include/asterisk/hash.h?rev=43886&view=auto
==============================================================================
--- team/nadi/trunk-cm/include/asterisk/hash.h (added)
+++ team/nadi/trunk-cm/include/asterisk/hash.h Thu Sep 28 11:01:26 2006
@@ -1,0 +1,247 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi at beronet.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file hash.h
+ *
+ * \brief A simple hash implementation.
+ *
+ * \author Nadi Sarrar <nadi at beronet.com>
+ *
+ * A set of macros to provide an easy way of working with hashes.
+ * Works with either integer or character string keys out of the
+ * box. If you want to use another key type, just write a hash
+ * function for it and look at macros beginning with "__".
+ */
+
+
+#ifndef ASTERISK_HASH_H
+#define ASTERISK_HASH_H
+
+#include <sys/types.h>
+#include "asterisk/lock.h"
+
+int modolo_hash	(int key, size_t size);
+int joaat_hash (char *key, size_t size);
+
+int eq_str (char *a, char *b);
+int eq_int (int a, int b);
+
+#define __AST_HASH(name, ktype, vtype)	\
+struct name {							\
+	struct {							\
+		ktype       _key;				\
+		vtype       _val;				\
+		char        _used;				\
+	} *_data;							\
+	size_t          _size;				\
+	size_t          _length;			\
+	ast_mutex_t     _lock;				\
+	int     (*_hash_f)(ktype, size_t);	\
+	int     (*_eq_f)  (ktype, ktype);	\
+}
+
+#define AST_HASH_INT(name, type)		\
+	__AST_HASH(name, int, type)
+
+#define AST_HASH_STR(name, type)		\
+	__AST_HASH(name, char*, type)
+
+
+
+
+#define __AST_HASH_INIT_NOLOCK(hash, size, hash_f, eq_f)	\
+do {														\
+	(hash)->_data = calloc((size), sizeof(*(hash)->_data));	\
+	(hash)->_size = (size);									\
+	(hash)->_hash_f = (hash_f);								\
+	(hash)->_eq_f = (eq_f);									\
+} while (0)
+
+#define __AST_HASH_INIT(hash, size, hash_f, cmp)			\
+do {														\
+	__AST_HASH_INIT_NOLOCK((hash), (size), (hash_f), (cmp));\
+	ast_mutex_init(&(hash)->_lock);							\
+} while (0)
+
+#define AST_HASH_INIT_INT_NOLOCK(hash, size)				\
+	__AST_HASH_INIT_NOLOCK((hash), size, modolo_hash, eq_int)
+
+#define AST_HASH_INIT_INT(hash, size)						\
+	__AST_HASH_INIT((hash), size, modolo_hash, eq_int)
+
+#define AST_HASH_INIT_STR_NOLOCK(hash, size)				\
+	__AST_HASH_INIT_NOLOCK((hash), size, joaat_hash, eq_str)
+
+#define AST_HASH_INIT_STR(hash, size)						\
+	__AST_HASH_INIT((hash), size, joaat_hash, eq_str)
+
+
+
+
+#define AST_HASH_DESTROY_NOLOCK(hash)	\
+do {									\
+	if ((hash)->_data)					\
+		free((hash)->_data);			\
+} while (0)
+
+#define AST_HASH_DESTROY(hash)			\
+do {									\
+	AST_HASH_DESTROY_NOLOCK((hash));	\
+	ast_mutex_destroy(&(hash)->_lock);	\
+} while (0)
+
+
+
+
+#define AST_HASH_LOCK(hash)				\
+	ast_mutex_lock(&(hash)->_lock)
+
+#define AST_HASH_TRYLOCK(hash)			\
+	ast_mutex_trylock(&(hash)->_lock)
+
+#define AST_HASH_UNLOCK(hash)			\
+	ast_mutex_unlock(&(hash)->_lock)
+
+
+
+
+#define AST_HASH_INSERT_NOLOCK(hash, key, val)	\
+({												\
+	int pos = (hash)->_hash_f(key, (hash)->_size);	\
+	int end = pos;								\
+ 	int re = -1;								\
+	do {										\
+		if (!(hash)->_data[pos]._used) {		\
+			(hash)->_data[pos]._key = key;		\
+			(hash)->_data[pos]._val = val;		\
+			(hash)->_data[pos]._used = 1;		\
+			++(hash)->_length;					\
+ 			re = 0;								\
+			break;								\
+		}										\
+		pos = (pos + 1) % (hash)->_size;		\
+	} while (pos != end);						\
+	re;											\
+})
+
+#define AST_HASH_INSERT(hash, key, val)			\
+({												\
+ 	int re;										\
+	AST_HASH_LOCK((hash));						\
+	re = AST_HASH_INSERT_NOLOCK(hash, key, val);\
+	AST_HASH_UNLOCK((hash));					\
+	re;											\
+})
+
+#define AST_HASH_LOOKUP_NOLOCK(hash, key, val)	\
+({												\
+	int pos = (hash)->_hash_f(key, (hash)->_size);	\
+	int end = pos;								\
+ 	int re = -1;								\
+	do {										\
+		if ((hash)->_data[pos]._used &&			\
+			(hash)->_eq_f((hash)->_data[pos]._key, key)) {	\
+			val = (hash)->_data[pos]._val;		\
+			re = 0;								\
+ 		}										\
+		pos = (pos + 1) % (hash)->_size;		\
+	} while (pos != end);						\
+	re;											\
+})
+
+#define AST_HASH_LOOKUP(hash, key, val)			\
+({												\
+ 	int re;										\
+	AST_HASH_LOCK((hash));						\
+	re = AST_HASH_LOOKUP_NOLOCK(hash, key, val);\
+	AST_HASH_UNLOCK((hash));					\
+	re;											\
+})
+
+#define AST_HASH_REMOVE_NOLOCK(hash, key)		\
+do {											\
+	int pos = (hash)->_hash_f((key), (hash)->_size);	\
+	int end = pos;								\
+	do {										\
+		if ((hash)->_data[pos]._used &&			\
+			(hash)->_eq_f((hash)->_data[pos]._key, (key))) {	\
+			(hash)->_data[pos]._used = 0;		\
+			--(hash)->_length;					\
+ 			break;								\
+ 		}										\
+		pos = (pos + 1) % (hash)->_size;		\
+	} while (pos != end);						\
+} while (0)										\
+
+#define AST_HASH_REMOVE(hash, key)				\
+do {											\
+	AST_HASH_LOCK((hash));						\
+	AST_HASH_REMOVE_NOLOCK((hash), (key));		\
+	AST_HASH_UNLOCK((hash));					\
+} while (0)
+
+
+
+
+#define __AST_HASH_NEXT(hash, i)				\
+({												\
+	for (++i;									\
+		 i < (hash)->_size &&					\
+		 !(hash)->_data[i]._used;				\
+		 ++i);									\
+})
+
+#define AST_HASH_TRAVERSE_NOLOCK(hash, key, val, i)	\
+	for (i = -1,									\
+		 __AST_HASH_NEXT((hash), i);				\
+		 i < (hash)->_size &&						\
+		 (((key = (hash)->_data[i]._key)) || 1) &&	\
+		 (((val = (hash)->_data[i]._val)) || 1);	\
+		 __AST_HASH_NEXT((hash), i))
+
+
+
+
+#define AST_HASH_LENGTH_NOLOCK(hash)			\
+	(hash)->_length
+
+#define AST_HASH_LENGTH(hash)					\
+({												\
+ 	size_t length;								\
+	AST_HASH_LOCK((hash));						\
+ 	length = AST_HASH_LENGTH_NOLOCK((hash));	\
+ 	AST_HASH_UNLOCK((hash));					\
+ 	length;										\
+})
+
+
+
+
+#define AST_HASH_SIZE_NOLOCK(hash)				\
+	(hash)->_size
+
+#define AST_HASH_SIZE(hash)						\
+({												\
+ 	size_t size;								\
+	AST_HASH_LOCK((hash));						\
+ 	size = AST_HASH_SIZE_NOLOCK((hash));		\
+ 	AST_HASH_UNLOCK((hash));					\
+ 	size;										\
+})
+
+#endif

Propchange: team/nadi/trunk-cm/include/asterisk/hash.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/nadi/trunk-cm/include/asterisk/hash.h
------------------------------------------------------------------------------
    svn:keywords = text/plain

Propchange: team/nadi/trunk-cm/include/asterisk/hash.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/nadi/trunk-cm/main/Makefile
URL: http://svn.digium.com/view/asterisk/team/nadi/trunk-cm/main/Makefile?rev=43886&r1=43885&r2=43886&view=diff
==============================================================================
--- team/nadi/trunk-cm/main/Makefile (original)
+++ team/nadi/trunk-cm/main/Makefile Thu Sep 28 11:01:26 2006
@@ -26,7 +26,7 @@
 	utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \
 	netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
 	cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
-	strcompat.o
+	strcompat.o hash.o configman.o
 
 # we need to link in the objects statically, not as a library, because
 # otherwise modules will not have them available if none of the static

Added: team/nadi/trunk-cm/main/configman.c
URL: http://svn.digium.com/view/asterisk/team/nadi/trunk-cm/main/configman.c?rev=43886&view=auto
==============================================================================
--- team/nadi/trunk-cm/main/configman.c (added)
+++ team/nadi/trunk-cm/main/configman.c Thu Sep 28 11:01:26 2006
@@ -1,0 +1,1289 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi at beronet.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file configman.c
+ *
+ * \brief Configuration Management
+ *
+ * \author Nadi Sarrar <nadi at beronet.com>
+ *
+ * Configuration Management written to be used by
+ * Asterisk channel modules, but may fit fine for
+ * other cases too.
+ */
+
+/* TODO:
+ *  - get rid of the hash code by implementing ast_hash or similar.
+ *  - write macros for easier variable access (cm_get_bool, cm_get_int, cm_get_strlist, cm_get_intlist, ...)
+ *  - handle return values for *alloc, strdup, ast_cli_register
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "asterisk/configman.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/logger.h"
+#include "asterisk/lock.h"
+#include "asterisk/strings.h"
+#include "asterisk/utils.h"
+#include "asterisk/term.h"
+#include "asterisk/hash.h"
+
+static inline char * notdir (char *str)
+{
+	char *p = strrchr(str, '/');
+	return (p && *(p + 1)) ? p + 1 : str;
+}
+
+#define WARNING_PARSE_NV(cm, name, value, section) \
+	ast_log(LOG_WARNING, "%s [%s]: invalid expression in section \"%s\": \"%s = %s\". " \
+			"Please edit your %s and then reload.\n", \
+			(cm)->modname, notdir((cm)->filename), (section), (name), (value), notdir((cm)->filename))
+
+#define WARNING_PARSE_SEC(cm, section) \
+	ast_log(LOG_WARNING, "%s [%s]: invalid section \"%s\". " \
+			"Please edit your %s and then reload.\n", \
+			(cm)->modname, notdir((cm)->filename), (section), notdir((cm)->filename))
+
+#define WARNING(fmt,...) \
+	ast_log(LOG_WARNING, "confman: " fmt, ##__VA_ARGS__)
+#define WARNING_CM(cm,fmt,...) \
+	ast_log(LOG_WARNING, "%s: " fmt, (cm)->modname, ##__VA_ARGS__)
+#define WARNING_CMF(cm,fmt,...) \
+	ast_log(LOG_WARNING, "%s [%s]: " fmt, (cm)->modname, (cm)->filename, ##__VA_ARGS__)
+
+#define ERROR(fmt,...) \
+	ast_log(LOG_ERROR, "confman: " fmt, ##__VA_ARGS__)
+#define ERROR_CM(cm,fmt,...) \
+	ast_log(LOG_ERROR, "%s: " fmt, (cm)->modname, ##__VA_ARGS__)
+#define ERROR_CMF(cm,fmt,...) \
+	ast_log(LOG_ERROR, "%s [%s]: " fmt, (cm)->modname, (cm)->filename, ##__VA_ARGS__)
+
+#define LOCK(cm)    ast_mutex_lock(&((cm)->lock))
+#define UNLOCK(cm)  ast_mutex_unlock(&((cm)->lock))
+
+#define HASHSIZE         512
+
+static char              SHOW[] = "show";
+static char              CONFIG[] = "config";
+static char              DESCRIPTION[] = "description";
+static char              DESCRIPTIONS[] = "descriptions";
+static char              VALUES[] = "values";
+
+typedef union {
+	char               * c;
+	int                  i;
+} cm_hash_key_t;
+
+typedef union {
+	int                  i;
+	void               * p;
+} cm_hash_value_t;
+
+typedef struct {
+	int                  used;
+	cm_hash_key_t        key;
+	cm_hash_value_t      val;
+} cm_hash_entry_t;
+
+typedef struct {
+	cm_hash_entry_t      data[HASHSIZE];
+	size_t               length;
+} cm_hash_t;
+
+enum cm_state {
+	CM_NULL = 0,
+	CM_CREATED,
+	CM_LOADED,
+};
+
+typedef char           * cm_val_t;
+typedef cm_val_t       * cm_row_t;
+typedef struct {
+	int                  num_rows;
+	cm_row_t           * rows;
+} cm_matrix_t;
+
+struct cm {
+	ast_mutex_t          lock;
+	enum cm_state        state;
+	char               * modname;
+	char               * filename;
+	unsigned int         num_secs;
+	const cm_section_t * secs;
+	cm_matrix_t        * vals;
+	cm_hash_t         ** hashes;
+	cm_hash_key_t     ** key_lists;
+	struct ast_cli_entry clis[3];
+};
+
+static AST_HASH_STR(cm_obj_hash_t, cm_t *) cm_obj_hash;
+
+static inline int __joaat_hash (char *key, size_t len)
+{
+	int hash = 0;
+	size_t i;
+
+	for (i = 0; i < len; ++i) {
+		hash += key[i];
+		hash += (hash << 10);
+		hash ^= (hash >> 6);
+	}
+	hash += (hash << 3);
+	hash ^= (hash >> 11);
+	hash += (hash << 15);
+	hash %= HASHSIZE;
+
+	if (hash < 0)
+		hash *= -1;
+
+	return hash;
+}
+
+static inline int __hash_insert (cm_hash_t *hash, int pos, cm_hash_key_t key, cm_hash_value_t val)
+{
+	int end = pos;
+
+	do {
+		if (!hash->data[pos].used) {
+			hash->data[pos].key = key;
+			hash->data[pos].val = val;
+			hash->data[pos].used = 1;
+			++hash->length;
+			return 0;
+		}
+		pos = (pos + 1) % HASHSIZE;
+	} while (pos != end);
+
+	return -1;
+}
+
+static int __hash_insert_int (cm_hash_t *hash, int key, cm_hash_value_t val)
+{
+	int pos = __joaat_hash((char *)&key, sizeof(int));
+
+	return __hash_insert(hash, pos, (cm_hash_key_t)key, val);
+}
+
+static int __hash_insert_string (cm_hash_t *hash, char *key, cm_hash_value_t val)
+{
+	int pos = __joaat_hash(key, strlen(key));
+
+	return __hash_insert(hash, pos, (cm_hash_key_t)key, val);
+}
+
+static int __hash_lookup_int (cm_hash_t *hash, int key, cm_hash_value_t *val)
+{
+	int pos = __joaat_hash((char *)&key, sizeof(int));
+	int end = pos;
+
+	do {
+		if (hash->data[pos].used && hash->data[pos].key.i == key) {
+			*val = hash->data[pos].val;
+			return 0;
+		}
+		pos = (pos + 1) % HASHSIZE;
+	} while (pos != end);
+
+	return -1;
+}
+
+static int __hash_lookup_string (cm_hash_t *hash, char *key, cm_hash_value_t *val)
+{
+	int pos = __joaat_hash(key, strlen(key));
+	int end = pos;
+
+	do {
+		if (hash->data[pos].used && !strcmp(hash->data[pos].key.c, key)) {
+			*val = hash->data[pos].val;
+			return 0;
+		}
+		pos = (pos + 1) % HASHSIZE;
+	} while (pos != end);
+
+	return -1;
+}
+
+static int __get_col (const cm_section_t *sec, char *name)
+{
+	int i;
+
+	if (!strcasecmp(sec->section_name, name))
+		return 0;
+
+	for (i = 0; i < sec->num_directives; ++i)
+		if (sec->directives[i].name &&
+			!strcasecmp(sec->directives[i].name, name))
+			return i;
+
+	return -1;
+}
+
+int cm_id_valid (cm_t *cm, int sec_id, ...)
+{
+	va_list           ap;
+	int               id_int;
+	char            * id_string;
+	cm_hash_value_t   val;
+
+	if (!cm)
+		goto err1;
+
+	LOCK(cm);
+
+	if (cm->state != CM_LOADED || sec_id >= cm->num_secs || sec_id < 0)
+		goto err2;
+
+	switch (cm->secs[sec_id].key_type) {
+	case KTYPE_NONE:
+		goto err2;
+	case KTYPE_INTEGER:
+		va_start(ap, sec_id);
+		id_int = va_arg(ap, int);
+		va_end(ap);
+		if (!cm->hashes[sec_id])
+			goto err2;
+		if (__hash_lookup_int(cm->hashes[sec_id], id_int, &val))
+			goto err2;
+		break;
+	case KTYPE_STRING:
+		va_start(ap, sec_id);
+		id_string = va_arg(ap, char *);
+		va_end(ap);
+		if (!cm->hashes[sec_id])
+			goto err2;
+		if (__hash_lookup_string(cm->hashes[sec_id], id_string, &val))
+			goto err2;
+		break;
+	}
+
+	UNLOCK(cm);
+
+	return 1;
+
+err2:
+	UNLOCK(cm);
+err1:
+	return 0;
+}
+
+int cm_get (cm_t *cm, char *buf, size_t size, int sec_id, int elem_id, ...)
+{
+	va_list           ap;
+	int               id_int;
+	char            * val,
+		            * id_string;
+	cm_hash_value_t   row;
+
+	if (!cm)
+		goto err1;
+
+	LOCK(cm);
+
+	if (cm->state != CM_LOADED)
+		goto err2;
+
+	if (!buf || !size || sec_id >= cm->num_secs || sec_id < 0 ||
+		elem_id < 0 || elem_id >= cm->secs[sec_id].num_directives)
+		goto err2;
+
+	switch (cm->secs[sec_id].key_type) {
+	case KTYPE_NONE:
+		val = cm->vals[sec_id].rows[0][elem_id];
+		if (!val || !memccpy(buf, val, 0, size)) {
+			*buf = 0;
+			goto err2;
+		}
+		goto out;
+	case KTYPE_INTEGER:
+		va_start(ap, elem_id);
+		id_int = va_arg(ap, int);
+		va_end(ap);
+		if (!cm->hashes[sec_id])
+			goto err2;
+		if (__hash_lookup_int(cm->hashes[sec_id], id_int, &row))
+			goto err2;
+		break;
+	case KTYPE_STRING:
+		va_start(ap, elem_id);
+		id_string = va_arg(ap, char *);
+		va_end(ap);
+		if (!cm->hashes[sec_id])
+			goto err2;
+		if (__hash_lookup_string(cm->hashes[sec_id], id_string, &row))
+			goto err2;
+		break;
+	}
+	
+	val = cm->vals[sec_id].rows[row.i][elem_id];
+	if (!val)
+		val = cm->vals[sec_id].rows[0][elem_id];
+	if (!val || !memccpy(buf, val, 0, size)) {
+		*buf = 0;
+		goto err2;
+	}
+
+out:
+	UNLOCK(cm);
+
+	return 0;
+
+err2:
+	UNLOCK(cm);
+err1:
+	return -1;
+}
+
+static void __read_row (cm_t *cm, const cm_section_t *sec, cm_row_t *row_p, char *cat, struct ast_variable *v)
+{
+	cm_row_t  row;
+	int       pos;
+
+	if (!*row_p)
+		*row_p = calloc(sec->num_directives, sizeof(cm_val_t));
+	row = *row_p;
+
+	if (!row[0])
+		row[0] = strdup(cat);
+	for (; v; v = v->next) {
+		pos = __get_col(sec, v->name);
+		if (pos >= 0) {
+			if (row[pos]) {
+				row[pos] = realloc(row[pos], strlen(row[pos]) + strlen(v->value) + 2);
+				sprintf(row[pos] + strlen(row[pos]), ",%s", v->value);
+			} else
+				row[pos] = strdup(v->value);
+		} else
+			WARNING_PARSE_NV(cm, v->name, v->value, cat);
+	}
+}
+
+static void __read_category_1 (cm_t *cm, const cm_section_t *sec, cm_matrix_t *matrix, char *cat, struct ast_variable *v)
+{
+	if (!matrix->num_rows) {
+		matrix->num_rows = 1;
+		matrix->rows = calloc(1, sizeof(cm_row_t));
+	}
+	__read_row(cm, sec, &matrix->rows[0], cat, v);
+}
+
+static void __read_category_n (cm_t *cm, const cm_section_t *sec, cm_matrix_t *matrix, char *cat, struct ast_variable *v)
+{
+	int row;
+
+	for (row = 1; row < matrix->num_rows; ++row) {
+		if (!strcasecmp(matrix->rows[row][0], cat)) {
+			__read_row(cm, sec, &matrix->rows[row], cat, v);
+			return;
+		}
+	}
+
+	if (!matrix->num_rows) {
+		matrix->num_rows = 2;
+		matrix->rows = calloc(2, sizeof(cm_row_t));
+	}
+	else {
+		++matrix->num_rows;
+		matrix->rows = realloc(matrix->rows, matrix->num_rows * sizeof(cm_row_t));
+		matrix->rows[matrix->num_rows - 1] = NULL;
+	}
+	__read_row(cm, sec, &matrix->rows[matrix->num_rows - 1], cat, v);
+}
+
+static void __hash_categories (cm_t *cm, const cm_section_t *sec, cm_matrix_t *matrix, cm_hash_t **hash)
+{
+	int    pos,
+		   start,
+		   end,
+		   i;
+	char * token,
+		 * tmp;
+
+	switch (sec->key_type) {
+	case KTYPE_INTEGER:
+		if (!*hash)
+			*hash = calloc(1, sizeof(cm_hash_t));
+		pos = __get_col(sec, sec->key_field);
+		if (pos < 0)
+			ERROR_CM(cm, "missing key field \"%s\" in section structure!\n", sec->key_field);
+		else
+			for (i = 1; i < matrix->num_rows; ++i) {
+				if (!matrix->rows[i][pos])
+					WARNING_CMF(cm, "missing value for key field \"%s\" in section \"%s\"!\n",
+								sec->key_field,
+								matrix->rows[i][0] ? matrix->rows[i][0] : sec->section_name);
+				else {
+					tmp = strdup(matrix->rows[i][pos]);
+					for (token = strsep(&tmp, ","); token; token = strsep(&tmp, ",")) { 
+						if (!*token)
+							continue;
+						if (sscanf(token, "%d-%d", &start, &end) >= 2) {
+							for (; start <= end; start++) {
+								if (__hash_insert_int(*hash, start, (cm_hash_value_t)i))
+									WARNING_CMF(cm, "failed to hash integer key: %d\n", start);
+							}
+						} else if (sscanf(token, "%d", &start)) {
+							if (__hash_insert_int(*hash, start, (cm_hash_value_t)i))
+								WARNING_CMF(cm, "failed to hash integer key: %d\n", start);
+						} else
+							WARNING_PARSE_NV(cm, sec->directives[pos].name, matrix->rows[i][pos], sec->section_name);
+					}
+					free(tmp);
+				}
+			}
+		break;
+	case KTYPE_STRING:
+		if (!*hash)
+			*hash = calloc(1, sizeof(cm_hash_t));
+		pos = __get_col(sec, sec->key_field);
+		if (pos < 0)
+			ERROR_CM(cm, "missing key field \"%s\" in section structure!\n", sec->key_field);
+		else
+			for (i = 1; i < matrix->num_rows; ++i) {
+				if (!matrix->rows[i][pos])
+					WARNING_CMF(cm, "missing value for key field \"%s\" in section \"%s\"!\n",
+								sec->key_field,
+								matrix->rows[i][0] ? matrix->rows[i][0] : sec->section_name);
+				else {
+					if (__hash_insert_string(*hash, matrix->rows[i][pos], (cm_hash_value_t)i))
+						WARNING_CMF(cm, "failed to hash string key: %s\n", matrix->rows[matrix->num_rows - 1][pos]);
+				}
+			}
+		break;
+	case KTYPE_NONE:
+		break;
+	}
+}
+
+static void __fill_defaults (cm_t *cm, const cm_section_t *sec, cm_matrix_t *matrix)
+{
+	int i;
+
+	if (!matrix->num_rows) {
+		matrix->num_rows = 1;
+		matrix->rows = calloc(1,  sizeof(cm_row_t));
+	}
+
+	if (!matrix->rows[0])
+		matrix->rows[0] = calloc(sec->num_directives, sizeof(cm_val_t));
+
+	if (!matrix->rows[0][0])
+		matrix->rows[0][0] = sec->default_name ? strdup(sec->default_name) : strdup(sec->section_name);
+	
+	for (i = 1; i < sec->num_directives; ++i)
+		if (!matrix->rows[0][i] && sec->directives[i].def && *sec->directives[i].def)
+			matrix->rows[0][i] = strdup(sec->directives[i].def);
+}
+
+static inline void __key_list_insert_sorted_int (cm_hash_key_t *key_list, cm_hash_key_t key)
+{
+	int i,
+		a,
+		b;
+
+	for (i = 0; key_list[i].i != -1 && key_list[i].i <= key.i; ++i);
+
+	b = key_list[i].i;
+	key_list[i].i = key.i;
+
+	for (++i; b != -1; ++i) {
+		a = key_list[i].i;
+		key_list[i].i = b;
+		b = a;
+	}
+}
+
+static inline void __key_list_insert_sorted_string (cm_hash_key_t *key_list, cm_hash_key_t key)
+{
+	int    i;
+	char * a,
+		 * b;
+
+	for (i = 0; key_list[i].c && strcasecmp(key_list[i].c, key.c) <= 0; ++i);
+
+	b = key_list[i].c;
+	key_list[i].c = key.c;
+
+	for (++i; b; ++i) {
+		a = key_list[i].c;
+		key_list[i].c = b;
+		b = a;
+	}
+}
+
+static void __init_key_list (cm_t *cm, cm_hash_t *hash, cm_hash_key_t **key_list, enum ktype type)
+{
+	int i;
+
+	*key_list = calloc(hash->length, sizeof(key_list));
+
+	if (type == KTYPE_INTEGER)
+		for (i = 0; i < hash->length; ++i)
+			(*key_list)[i].i = -1;
+
+	for (i = 0; i < HASHSIZE; ++i) {
+		if (hash->data[i].used) {
+			if (type == KTYPE_INTEGER)
+				__key_list_insert_sorted_int(*key_list, hash->data[i].key);
+			else
+				__key_list_insert_sorted_string(*key_list, hash->data[i].key);
+		}
+	}
+}
+
+const void * cm_get_next_id (cm_t *cm, int sec_id, ...)
+{
+	va_list         ap;
+	int             id_int,
+			        i;
+	char          * id_string;
+	void          * retval = NULL;
+	cm_hash_key_t * key_list;
+	cm_hash_t     * hash;
+
+	if (!cm)
+		goto err1;
+
+	LOCK(cm);
+
+	if (cm->state != CM_LOADED || sec_id >= cm->num_secs || sec_id < 0)
+		goto err2;
+
+	hash = cm->hashes[sec_id];
+	key_list = cm->key_lists[sec_id];
+	
+	if (!hash->length)
+		goto err2;
+
+	switch (cm->secs[sec_id].key_type) {
+	case KTYPE_NONE:
+		goto err2;
+	case KTYPE_INTEGER:
+		va_start(ap, sec_id);
+		id_int = va_arg(ap, int);
+		va_end(ap);
+		if (id_int == -1) {
+			retval = &key_list[0].i;
+		} else {
+			for (i = 0; i < hash->length && key_list[i].i != id_int; ++i);
+			if (i >= (hash->length - 1))
+				goto err2;
+			retval = &key_list[i + 1].i;
+		}
+		break;
+	case KTYPE_STRING:
+		va_start(ap, sec_id);
+		id_string = va_arg(ap, char *);
+		va_end(ap);
+		if (!id_string) {
+			retval = key_list[0].c;
+		} else {
+			for (i = 0; i < hash->length && strcasecmp(key_list[i].c, id_string); ++i);
+			if (i >= (hash->length - 1))
+				goto err2;
+			retval = key_list[i + 1].c;
+		}
+		break;
+	}
+
+	UNLOCK(cm);
+
+	return retval;
+
+err2:
+	UNLOCK(cm);
+err1:
+	return NULL;
+}
+
+/* cli commands for modules */
+static void __show_config_description (int fd, const cm_section_t *sec, int col)
+{
+	char name[128];
+	char section[128];
+
+	term_color(name, sec->directives[col].name, COLOR_BRWHITE, 0, sizeof(name));
+	term_color(section, sec->section_name, COLOR_YELLOW, 0, sizeof(section));
+
+	if (sec->directives[col].def && *sec->directives[col].def)
+		ast_cli(fd, "[%s] %s   (Default: %s)\n\t%s\n",
+				section,
+				name,
+				sec->directives[col].def,
+				sec->directives[col].desc);
+	else
+		ast_cli(fd, "[%s] %s\n\t%s\n",
+				section,
+				name,
+				sec->directives[col].desc);
+}
+
+static char * __complete_section (cm_t *cm, const char *word, int state)
+{
+	int which = 0,
+		wordlen = strlen(word),
+		i;
+
+	for (i = 0; i < cm->num_secs; ++i) {
+		if (!wordlen || !strncmp(word, cm->secs[i].section_name, wordlen)) {
+			if (++which > state) {
+				return strdup(cm->secs[i].section_name);
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static char * __complete_directive (cm_t *cm, const char *word, int state)
+{
+	int which = 0,
+		wordlen = strlen(word),
+		i,
+		j;
+
+	for (i = 0; i < cm->num_secs; ++i) {
+		for (j = 1; j < cm->secs[i].num_directives; ++j) {
+			if (!wordlen || !strncmp(word, cm->secs[i].directives[j].name, wordlen)) {
+				if (++which > state) {
+					return strdup(cm->secs[i].directives[j].name);
+				}
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static char * __complete_keys (cm_t *cm, const cm_section_t *sec, const char *word, int state)
+{
+	cm_hash_key_t  * key_list;
+	int              which = 0,
+					 wordlen = strlen(word),
+					 len,
+					 i;
+	char             buf[32];
+
+	for (i = 0; i < cm->num_secs; ++i)
+		if (&cm->secs[i] == sec)
+			break;
+	if (i == cm->num_secs)
+		return NULL;
+
+	len = cm->hashes[i]->length;
+	key_list = cm->key_lists[i];
+
+	if (sec->key_type == KTYPE_STRING) {
+		for (i = 0; i < len; ++i) {
+			if (!wordlen || !strncmp(word, key_list[i].c, wordlen)) {
+				if (++which > state) {
+					return strdup(key_list[i].c);
+				}
+			}
+		}
+	} else if (sec->key_type == KTYPE_INTEGER) {
+		if (!wordlen) {
+			if (state >= len)
+				return NULL;
+			snprintf(buf, sizeof(buf), "%d", key_list[state].i);
+			return strdup(buf);
+		}
+		for (i = 0; i < len; ++i) {
+			snprintf(buf, sizeof(buf), "%d", key_list[i].i);
+			if (!strncmp(word, buf, wordlen)) {
+				if (++which > state) {
+					return strdup(buf);
+				}
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static inline cm_t * __get_cm_from_hash (char *modname)
+{
+	cm_t * cm = NULL;
+	AST_HASH_LOOKUP(&cm_obj_hash, modname, cm);
+	return cm;
+}
+
+static cm_t * __get_cm_from_line (const char *line)
+{
+	char modname[128];
+
+	if (sscanf(line, "%128s", modname) == 1)
+		return __get_cm_from_hash(modname);
+	else
+		return NULL;
+}
+
+static const cm_section_t * __get_sec_from_line (cm_t *cm, const char *line)
+{
+	char section_name[128],
+		 buf1[128],
+		 buf2[128];
+	int  i;
+
+	if (sscanf(line, "%127s show config %128s %128s", buf1, buf2, section_name) != 3)
+		return NULL;
+
+	for (i = 0; i < cm->num_secs; ++i)
+		if (!strcasecmp(cm->secs[i].section_name, section_name))
+			return &cm->secs[i];
+
+	return NULL;
+}
+
+static char * cli_show_config_description_completer (const char *line, const char *word, int pos, int state)
+{
+	cm_t * cm;
+	char * retval = NULL;
+
+	cm = __get_cm_from_line(line);
+	if (!cm)
+		return NULL;
+
+	LOCK(cm);
+	retval = __complete_directive(cm, word, state);
+	UNLOCK(cm);
+
+	return retval;
+}
+
+static int cli_show_config_description (int fd, int argc, char *argv[])
+{
+	cm_t * cm;
+	int    err = RESULT_SHOWUSAGE,
+		   i,
+		   col;
+
+	if (argc != 5)
+		return err;
+
+	cm = __get_cm_from_hash(argv[0]);
+	if (!cm)
+		return err;
+
+	LOCK(cm);
+	for (i = 0; i < cm->num_secs; ++i) {
+		col = __get_col(&cm->secs[i], argv[4]);
+		if (col > 0) {
+			__show_config_description(fd, &cm->secs[i], col);
+			err = 0;
+		}
+	}
+	UNLOCK(cm);
+
+	if (err)
+		ast_cli(fd, "No such directive: %s\n", argv[4]);
+
+	return err;
+}
+
+static char * cli_show_config_descriptions_completer (const char *line, const char *word, int pos, int state)
+{
+	cm_t * cm;
+	char * retval = NULL;
+
+	cm = __get_cm_from_line(line);
+	if (!cm)
+		return NULL;
+
+	LOCK(cm);
+	retval = __complete_section(cm, word, state);
+	UNLOCK(cm);
+
+	return retval;
+}
+
+static int cli_show_config_descriptions (int fd, int argc, char *argv[])
+{
+	cm_t            * cm;
+	int               err = RESULT_SHOWUSAGE,
+					  i,
+					  j;
+
+	if (argc - 4 && argc - 5)
+		return err;
+
+	cm = __get_cm_from_hash(argv[0]);
+	if (!cm)
+		return err;
+
+	LOCK(cm);
+	for (i = 0; i < cm->num_secs; ++i) {
+		if (argc == 4 || (argc == 5 && !strcasecmp(cm->secs[i].section_name, argv[4]))) {
+			for (j = 1; j < cm->secs[i].num_directives; ++j)
+				__show_config_description(fd, &cm->secs[i], j);
+			err = 0;
+			if (argc == 5)
+				break;
+		}
+	}
+	UNLOCK(cm);
+
+	if (err && argc == 5)
+		ast_cli(fd, "No such section: %s\n", argv[4]);
+
+	return err;
+}
+
+static char * cli_show_config_values_completer (const char *line, const char *word, int pos, int state)
+{
+	cm_t               * cm;
+	const cm_section_t * sec;
+	char               * retval = NULL;
+
+	cm = __get_cm_from_line(line);
+	if (!cm)
+		return NULL;
+
+	LOCK(cm);
+	if (pos == 4) {
+		retval = __complete_section(cm, word, state);
+	} else if (pos == 5) {
+		sec = __get_sec_from_line(cm, line);
+		if (sec && sec->key_type != KTYPE_NONE)
+			retval = __complete_keys(cm, sec, word, state);
+	}
+	UNLOCK(cm);
+
+	return retval;
+}
+
+static inline void __show_config_values_header (int fd, char *name)
+{
+	char yname[128];
+
+	term_color(yname, name, COLOR_YELLOW, 0, sizeof(yname));
+	ast_cli(fd, "[%s]\n", yname);
+}
+
+static void __show_config_values (int fd, cm_t *cm, int sec_id, int row)
+{
+	char   wval[128],
+		 * val;
+	int    i,
+		   col;
+
+	col = cm->secs[sec_id].key_type == KTYPE_NONE ? 0 : 
+		__get_col(&cm->secs[sec_id], cm->secs[sec_id].key_field);
+
+	for (i = 0; i < cm->secs[sec_id].num_directives; ++i) {
+		if (i == col)
+			continue;
+		val = cm->vals[sec_id].rows[row][i];
+		if (!val)
+			val = cm->vals[sec_id].rows[0][i];
+		term_color(wval, val, COLOR_BRWHITE, 0, sizeof(wval));
+		ast_cli(fd, " -> %s: %s\n", cm->secs[sec_id].directives[i].name, wval);
+	}
+}
+
+static void __show_config_values_list (int fd, cm_t *cm, int sec_id)
+{
+	cm_hash_key_t * key_list;
+	cm_hash_value_t row;
+	int             len,
+					i;
+	char            id[128];
+
+	switch (cm->secs[sec_id].key_type) {
+	case KTYPE_NONE:
+		__show_config_values_header(fd, cm->secs[sec_id].section_name);
+		__show_config_values(fd, cm, sec_id, 0);
+		break;
+	case KTYPE_INTEGER:
+		len = cm->hashes[sec_id]->length;
+		key_list = cm->key_lists[sec_id];
+		for (i = 0; i < len; ++i) {
+			if (!__hash_lookup_int(cm->hashes[sec_id], key_list[i].i, &row)) {
+				snprintf(id, sizeof(id), "%d", key_list[i].i);
+				__show_config_values_header(fd, id);
+				__show_config_values(fd, cm, sec_id, row.i);
+			}
+		}
+		break;
+	case KTYPE_STRING:
+		len = cm->hashes[sec_id]->length;
+		key_list = cm->key_lists[sec_id];
+		for (i = 0; i < len; ++i) {
+			if (!__hash_lookup_string(cm->hashes[sec_id], key_list[i].c, &row)) {
+				__show_config_values_header(fd, key_list[i].c);
+				__show_config_values(fd, cm, sec_id, row.i);
+			}
+		}
+		break;
+	}
+}
+
+static int cli_show_config_values (int fd, int argc, char *argv[])
+{
+	cm_t          * cm;
+	int             err = RESULT_SHOWUSAGE,
+					i,
+					sec_id = -1,
+					id_int;
+	cm_hash_value_t row;
+
+	if (argc < 4 || argc > 6)
+		return err;
+
+	cm = __get_cm_from_hash(argv[0]);
+	if (!cm)
+		return err;
+
+	LOCK(cm);
+
+	if (argc > 4) {
+		for (i = 0; i < cm->num_secs; ++i)
+			if (!strcmp(argv[4], cm->secs[i].section_name)) {
+				sec_id = i;
+				break;
+			}
+		if (sec_id < 0) {
+			ast_cli(fd, "No such section: %s\n", argv[4]);
+			goto out;
+		}
+	}
+
+	if (argc > 5) {
+		switch (cm->secs[sec_id].key_type) {
+		case KTYPE_NONE:
+			ast_cli(fd, "Section \"%s\" has no key field!\n", argv[4]);
+			goto out;
+		case KTYPE_INTEGER:
+			if (sscanf(argv[5], "%d", &id_int) != 1 ||
+				__hash_lookup_int(cm->hashes[sec_id], id_int, &row)) {
+				ast_cli(fd, "Unknown ID: %s\n", argv[5]);
+				goto out;
+			}
+			break;
+		case KTYPE_STRING:
+			if (__hash_lookup_string(cm->hashes[sec_id], argv[5], &row)) {
+				ast_cli(fd, "Unknown ID: %s\n", argv[5]);
+				goto out;
+			}
+			break;
+		}
+		__show_config_values_header(fd, argv[5]);
+		__show_config_values(fd, cm, sec_id, row.i);
+	}
+
+	if (argc == 5)
+		__show_config_values_list(fd, cm, sec_id);
+
+	if (argc == 4)
+		for (i = 0; i < cm->num_secs; ++i)
+			__show_config_values_list(fd, cm, i);
+	
+	err = 0;
+
+out:
+	UNLOCK(cm);
+
+	return err;
+}
+
+int cm_load (cm_t *cm, const char *filename)
+{
+	struct ast_config    * cfg;
+	struct ast_variable  * v;
+	char                 * cat = NULL,
+						   buf[128];
+	int                    freesec = -1,
+						   i;
+	const cm_section_t   * sec;
+
+	if (!cm) {
+		WARNING("cm_load called with NULL parameter!\n");
+		goto err1;
+	}
+
+	LOCK(cm);
+
+	if (cm->state != CM_CREATED) {
+		WARNING("cm_load called with %s cm!\n",
+				cm->state == CM_LOADED ? "already loaded" : "non-created");
+		goto err2;
+	}
+
+	cm->filename = strdup(filename);
+	if (!cm->filename) {
+		ERROR_CM(cm, "not enough memory!\n");
+		goto err2;
+	}
+
+	cfg = ast_config_load(filename);
+	if (!cfg) {
+		ERROR_CM(cm, "failed to load file: %s\n", filename);
+		goto err3;
+	}
+
+	for (i = 0; i < cm->num_secs; ++i) {
+		if (cm->secs[i].key_type != KTYPE_NONE) {
+			freesec = i;
+			break;
+		}
+	}
+
+	while ((cat = ast_category_browse(cfg, cat))) {

[... 335 lines stripped ...]


More information about the asterisk-commits mailing list