[asterisk-commits] nadi: branch group/asterisk-1.4-gsm r60066 - in /team/group/asterisk-1.4-gsm:...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Wed Apr 4 05:17:25 MST 2007


Author: nadi
Date: Wed Apr  4 07:17:24 2007
New Revision: 60066

URL: http://svn.digium.com/view/asterisk?view=rev&rev=60066
Log:
adding res_configman and hash, required by chan_gsm.

Added:
    team/group/asterisk-1.4-gsm/include/asterisk/configman.h   (with props)
    team/group/asterisk-1.4-gsm/include/asterisk/hash.h   (with props)
    team/group/asterisk-1.4-gsm/main/hash.c   (with props)
    team/group/asterisk-1.4-gsm/res/res_configman.c   (with props)
Modified:
    team/group/asterisk-1.4-gsm/include/asterisk/cli.h
    team/group/asterisk-1.4-gsm/main/Makefile

Modified: team/group/asterisk-1.4-gsm/include/asterisk/cli.h
URL: http://svn.digium.com/view/asterisk/team/group/asterisk-1.4-gsm/include/asterisk/cli.h?view=diff&rev=60066&r1=60065&r2=60066
==============================================================================
--- team/group/asterisk-1.4-gsm/include/asterisk/cli.h (original)
+++ team/group/asterisk-1.4-gsm/include/asterisk/cli.h Wed Apr  4 07:17:24 2007
@@ -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/group/asterisk-1.4-gsm/include/asterisk/configman.h
URL: http://svn.digium.com/view/asterisk/team/group/asterisk-1.4-gsm/include/asterisk/configman.h?view=auto&rev=60066
==============================================================================
--- team/group/asterisk-1.4-gsm/include/asterisk/configman.h (added)
+++ team/group/asterisk-1.4-gsm/include/asterisk/configman.h Wed Apr  4 07:17:24 2007
@@ -1,0 +1,174 @@
+/*
+ * 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 <stdlib.h>
+#include <sys/types.h>
+
+#include "asterisk/strings.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 the subsequent id
+ * \param cm_t which cm_t to use
+ * \param sec_id section id to use
+ * \param prev pointer to the previous id, if you want to retrieve the first id, give NULL
+ * \param next output pointer to the id after prev
+ * Gets the subsequent id of a given id.
+ *
+ * Returns non-zero if next points to the id after prev, or zero if there
+ * is no subsequent id.
+ * Example for KTYPE_INT:
+ *   int next, *prev = NULL;
+ *   for (; cm_get_next_id(mycm, mysec_id, prev, &next); prev = &next) {
+ *     .. <next> is your traversing id ..
+ *   }
+ */
+int      cm_get_next_id (cm_t *cm, int sec_id, void *prev, void *next);
+
+/*! \brief Get the subsequent id and rewinds if the end was reached
+ * \param cm_t which cm_t to use
+ * \param sec_id section id to use
+ * \param prev pointer to the previous id, if you want to retrieve the first id, give NULL
+ * \param next output pointer to the id after prev
+ * Gets the subsequent id of a given id. This variant succeeds and returns the first
+ * id if the last one was given as prev.
+ *
+ * Returns non-zero if next points to the id after prev, or zero if there
+ * is no subsequent id.
+ */
+int      cm_get_next_id_spin (cm_t *cm, int sec_id, void *prev, void *next);
+
+/*! \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 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, ...);
+
+#define  cm_get_int(cm,dest,sec_id,elem_id,...)	\
+({	\
+	char __buf[32];	\
+	int __retval = 0, __tmp;	\
+	if (cm_get((cm), __buf, sizeof(__buf), (sec_id), (elem_id), ##__VA_ARGS__) ||	\
+		sscanf(__buf, "%d", &__tmp) != 1)	\
+		__retval = -1;	\
+	else 	\
+		dest = __tmp;	\
+	__retval;	\
+})
+
+#define  cm_get_boolint(cm,dest,val_true,val_false,sec_id,elem_id,...)	\
+({	\
+	char __buf[32];	\
+	int __retval = 0;	\
+	if (cm_get((cm), __buf, sizeof(__buf), (sec_id), (elem_id), ##__VA_ARGS__))	\
+		__retval = -1;	\
+	else 	\
+		dest = ast_true(__buf) ? val_true : val_false; \
+	__retval;	\
+})
+
+#define  cm_get_bool(cm,dest,sec_id,elem_id,...)	\
+	cm_get_boolint(cm, dest, 1, 0, sec_id, elem_id, ##__VA_ARGS__)
+
+#define  cm_get_strcasecmp(cm,str,sec_id,elem_id,...)	\
+({	\
+	char __buf[128];	\
+	int __retval = -1;	\
+	if (!cm_get((cm), __buf, sizeof(__buf), (sec_id), (elem_id), ##__VA_ARGS__))	\
+		__retval = strcasecmp((str), __buf);	\
+	__retval;	\
+})
+
+#endif

Propchange: team/group/asterisk-1.4-gsm/include/asterisk/configman.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/asterisk-1.4-gsm/include/asterisk/configman.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/asterisk-1.4-gsm/include/asterisk/configman.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/group/asterisk-1.4-gsm/include/asterisk/hash.h
URL: http://svn.digium.com/view/asterisk/team/group/asterisk-1.4-gsm/include/asterisk/hash.h?view=auto&rev=60066
==============================================================================
--- team/group/asterisk-1.4-gsm/include/asterisk/hash.h (added)
+++ team/group/asterisk-1.4-gsm/include/asterisk/hash.h Wed Apr  4 07:17:24 2007
@@ -1,0 +1,298 @@
+/*
+ * 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"
+
+extern int modolo_hash	(int key, size_t size);
+extern int joaat_hash (char *key, size_t size);
+
+extern int eq_str (char *a, char *b);
+extern int eq_int (int a, int b);
+
+#define __AST_HASH(name, ktype, vtype)	\
+struct name {							\
+	struct {							\
+		char        _used;				\
+		size_t 		_next;				\
+		ktype       _key;				\
+		vtype       _val;				\
+	} *_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_NOLOCK(name, ktype, vtype)	\
+struct name {							\
+	struct {							\
+		char        _used;				\
+		size_t      _next;				\
+		ktype       _key;				\
+		vtype       _val;				\
+	} *_data;							\
+	size_t          _size;				\
+	size_t          _length;			\
+	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_INT_NOLOCK(name, type)	\
+	__AST_HASH_NOLOCK(name, int, type)
+
+#define AST_HASH_STR(name, type)		\
+	__AST_HASH(name, char*, type)
+
+#define AST_HASH_STR_NOLOCK(name, type)	\
+	__AST_HASH_NOLOCK(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);									\
+	for ((hash)->_length = (size); (hash)->_length; --(hash)->_length)	\
+		(hash)->_data[(hash)->_length - 1]._next = -1;		\
+} 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 __TRY_INSERT(hash, key, val, pos, prev, re)	\
+	if (!hash->_data[pos]._used) {					\
+		hash->_data[pos]._key = key;				\
+		hash->_data[pos]._val = val;				\
+		hash->_data[pos]._used = 1;					\
+		if (prev > -1)								\
+			hash->_data[prev]._next = pos;			\
+		++hash->_length;							\
+		re = 0;										\
+		break;										\
+	}
+
+#define AST_HASH_INSERT_NOLOCK(hash, key, val)		\
+({													\
+	int __pos = (hash)->_hash_f(key, (hash)->_size);\
+ 	int __end;										\
+	int __prev = -1;								\
+ 	int __re = -1;									\
+	do {											\
+		__TRY_INSERT((hash), (key), (val), __pos, __prev, __re);\
+		if ((hash)->_eq_f((hash)->_data[__pos]._key, (key)))	\
+ 			break;									\
+ 		__prev = __pos;								\
+		if ((hash)->_data[__pos]._next > -1)		\
+			__pos = (hash)->_data[__pos]._next;		\
+		else {										\
+			__end = __pos;							\
+			__pos = (__pos + 1) % (hash)->_size;	\
+			do {									\
+				__TRY_INSERT((hash), (key), (val), __pos, __prev, __re);	\
+				__pos = (__pos + 1) % (hash)->_size;\
+			} while (__pos != __end);				\
+ 			break;									\
+		}											\
+	} while (1);									\
+	__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 _re = -1;								\
+	do {										\
+		if ((hash)->_data[_pos]._used &&		\
+			(hash)->_eq_f((hash)->_data[_pos]._key, key)) {	\
+			val = (hash)->_data[_pos]._val;		\
+			_re = 0;							\
+ 			break;								\
+ 		}										\
+		_pos = (hash)->_data[_pos]._next;		\
+	} while (_pos > -1);						\
+	_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 _prev = -1;								\
+	do {										\
+		if ((hash)->_data[_pos]._used &&		\
+			(hash)->_eq_f((hash)->_data[_pos]._key, (key))) {	\
+			(hash)->_data[_pos]._used = 0;		\
+			if (_prev > -1)						\
+				(hash)->_data[_prev]._next = (hash)->_data[_pos]._next;	\
+			--(hash)->_length;					\
+ 			break;								\
+ 		}										\
+		_prev = _pos;							\
+		_pos = (hash)->_data[_pos]._next;		\
+	} while (_pos > -1);						\
+} 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/group/asterisk-1.4-gsm/include/asterisk/hash.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/asterisk-1.4-gsm/include/asterisk/hash.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/asterisk-1.4-gsm/include/asterisk/hash.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/group/asterisk-1.4-gsm/main/Makefile
URL: http://svn.digium.com/view/asterisk/team/group/asterisk-1.4-gsm/main/Makefile?view=diff&rev=60066&r1=60065&r2=60066
==============================================================================
--- team/group/asterisk-1.4-gsm/main/Makefile (original)
+++ team/group/asterisk-1.4-gsm/main/Makefile Wed Apr  4 07:17:24 2007
@@ -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 threadstorage.o dial.o
+	strcompat.o threadstorage.o dial.o hash.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/group/asterisk-1.4-gsm/main/hash.c
URL: http://svn.digium.com/view/asterisk/team/group/asterisk-1.4-gsm/main/hash.c?view=auto&rev=60066
==============================================================================
--- team/group/asterisk-1.4-gsm/main/hash.c (added)
+++ team/group/asterisk-1.4-gsm/main/hash.c Wed Apr  4 07:17:24 2007
@@ -1,0 +1,70 @@
+/*
+ * 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.c
+ *
+ * \brief Some default hash functions.
+ *
+ * \author Nadi Sarrar <nadi at beronet.com>
+ *
+ * These are the default functions used by hash.h.
+ */
+
+#include "asterisk.h"
+#include "asterisk/hash.h"
+
+#include <string.h>
+
+int modolo_hash (int key, size_t size)
+{
+	return key % size;
+}
+
+/* Derived from http://en.wikipedia.org/wiki/Hash_table
+ * Original creator: Bob Jenkins */
+int joaat_hash (char *key, size_t size)
+{
+	int hash = 0;
+	int len = strlen(key);
+	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 %= size;
+
+	if (hash < 0)
+		hash *= -1;
+
+	return hash;
+}
+
+int eq_str (char *a, char *b)
+{
+	return !strcasecmp(a, b);
+}
+
+int eq_int (int a, int b)
+{
+	return a == b;
+}

Propchange: team/group/asterisk-1.4-gsm/main/hash.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/asterisk-1.4-gsm/main/hash.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/asterisk-1.4-gsm/main/hash.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/group/asterisk-1.4-gsm/res/res_configman.c
URL: http://svn.digium.com/view/asterisk/team/group/asterisk-1.4-gsm/res/res_configman.c?view=auto&rev=60066
==============================================================================
--- team/group/asterisk-1.4-gsm/res/res_configman.c (added)
+++ team/group/asterisk-1.4-gsm/res/res_configman.c Wed Apr  4 07:17:24 2007
@@ -1,0 +1,1217 @@
+/*
+ * 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 res_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:
+ *  - handle return values for *alloc, strdup, ast_cli_register
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk/configman.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/hash.h"
+#include "asterisk/logger.h"
+#include "asterisk/lock.h"
+#include "asterisk/module.h"
+#include "asterisk/strings.h"
+#include "asterisk/term.h"
+#include "asterisk/utils.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 {
+	AST_HASH_INT_NOLOCK(, int) i;
+	AST_HASH_STR_NOLOCK(, int) c;
+} cm_hash_t;
+
+typedef union {
+	int    i;
+	char * c;
+} cm_key_t;
+
+static AST_HASH_STR(cm_obj_hash_t, cm_t *) cm_obj_hash;
+
+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_key_t          ** key_arrays;
+	struct ast_cli_entry clis[3];
+};
+
+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;
+	int               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 (AST_HASH_LOOKUP_NOLOCK(&cm->hashes[sec_id]->i, 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 (AST_HASH_LOOKUP_NOLOCK(&cm->hashes[sec_id]->c, 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,
+					  row = 0;
+	char            * val,
+		            * id_string;
+
+	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 (AST_HASH_LOOKUP_NOLOCK(&cm->hashes[sec_id]->i, 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 (AST_HASH_LOOKUP_NOLOCK(&cm->hashes[sec_id]->c, id_string, row))
+			goto err2;
+		break;
+	}
+	
+	val = cm->vals[sec_id].rows[row][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));
+			AST_HASH_INIT_INT_NOLOCK(&(*hash)->i, HASHSIZE);
+		}
+		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 (AST_HASH_INSERT_NOLOCK(&(*hash)->i, start, i))
+									WARNING_CMF(cm, "failed to hash integer key: %d\n", start);
+							}
+						} else if (sscanf(token, "%d", &start)) {
+							if (AST_HASH_INSERT_NOLOCK(&(*hash)->i, start, 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));
+			AST_HASH_INIT_STR_NOLOCK(&(*hash)->c, HASHSIZE);
+		}
+		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 (AST_HASH_INSERT_NOLOCK(&(*hash)->c, matrix->rows[i][pos], i))
+						WARNING_CMF(cm, "failed to hash string key: %s\n", matrix->rows[i][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)
+			matrix->rows[0][i] = strdup(sec->directives[i].def);
+}
+
+static inline void __keys_insert_sorted_int (cm_key_t *keys, int key)
+{
+	int i,
+		a,
+		b;
+
+	for (i = 0; keys[i].i != -1 && keys[i].i <= key; ++i);
+
+	b = keys[i].i;
+	keys[i].i = key;
+
+	for (++i; b != -1; ++i) {
+		a = keys[i].i;
+		keys[i].i = b;
+		b = a;
+	}
+}
+
+static inline void __keys_insert_sorted_string (cm_key_t *keys, char *key)
+{
+	int    i;
+	char * a,
+		 * b;
+
+	for (i = 0; keys[i].c && strcasecmp(keys[i].c, key) <= 0; ++i);
+
+	b = keys[i].c;
+	keys[i].c = key;
+
+	for (++i; b; ++i) {
+		a = keys[i].c;
+		keys[i].c = b;
+		b = a;
+	}
+}
+
+static void __init_keys (cm_t *cm, cm_hash_t *hash, cm_key_t **keys, enum ktype type)
+{
+	int    i,
+		   id_int,
+		   val;
+	char * id_string;
+
+	if (type == KTYPE_INTEGER) {
+		*keys = calloc(AST_HASH_LENGTH_NOLOCK(&hash->i), sizeof(cm_key_t));
+		for (i = 0; i < AST_HASH_LENGTH_NOLOCK(&hash->i); ++i)
+			(*keys)[i].i = -1;
+		AST_HASH_TRAVERSE_NOLOCK(&hash->i, id_int, val, i)
+			__keys_insert_sorted_int(*keys, id_int);
+	}
+
+	if (type == KTYPE_STRING) {
+		*keys = calloc(AST_HASH_LENGTH_NOLOCK(&hash->c), sizeof(cm_key_t));
+		AST_HASH_TRAVERSE_NOLOCK(&hash->c, id_string, val, i)
+			__keys_insert_sorted_string(*keys, id_string);
+	}
+}
+
+int cm_get_next_id (cm_t *cm, int sec_id, void *prev, void *next)
+{
+	int             i,
+					retval = 0;
+	cm_key_t      * keys;
+	cm_hash_t     * hash;
+
+	if (!cm)
+		goto err1;
+
+	LOCK(cm);
+
+	if (cm->state != CM_LOADED || sec_id >= cm->num_secs || sec_id < 0 ||
+		cm->secs[sec_id].key_type == KTYPE_NONE)
+		goto err2;
+
+	hash = cm->hashes[sec_id];
+	keys = cm->key_arrays[sec_id];
+	
+	if (!AST_HASH_LENGTH_NOLOCK(&hash->i))
+		goto err2;
+
+	switch (cm->secs[sec_id].key_type) {
+	case KTYPE_INTEGER:
+		if (!prev || *(int *)prev < 0) {
+			*(int *)next = keys[0].i;
+		} else {
+			for (i = 0; i < AST_HASH_LENGTH_NOLOCK(&hash->i) && keys[i].i != *(int *)prev; ++i);
+			if (i >= (AST_HASH_LENGTH_NOLOCK(&hash->i) - 1))
+				goto err2;
+			*(int *)next = keys[i + 1].i;
+		}
+		retval = 1;
+		break;
+	case KTYPE_STRING:
+		if (!prev || !*(char **)prev) {
+			*(char **)next = keys[0].c;
+		} else {
+			for (i = 0; i < AST_HASH_LENGTH_NOLOCK(&hash->c) && strcasecmp(keys[i].c, *(char **)prev); ++i);
+			if (i >= (AST_HASH_LENGTH_NOLOCK(&hash->c) - 1))
+				goto err2;
+			*(char **)next = keys[i + 1].c;
+		}
+		retval = 1;
+		break;
+	case KTYPE_NONE:
+		goto err2;
+	}
+
+err2:
+	UNLOCK(cm);
+err1:
+	return retval;
+}
+
+int cm_get_next_id_spin (cm_t *cm, int sec_id, void *prev, void *next)
+{
+	return cm_get_next_id(cm, sec_id, prev, next) || cm_get_next_id(cm, sec_id, NULL, next);
+}
+
+/* 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_key_t * keys;
+	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 = AST_HASH_LENGTH_NOLOCK(&cm->hashes[i]->i);
+	keys = cm->key_arrays[i];
+
+	if (sec->key_type == KTYPE_STRING) {
+		for (i = 0; i < len; ++i) {
+			if (!wordlen || !strncmp(word, keys[i].c, wordlen)) {
+				if (++which > state) {
+					return strdup(keys[i].c);
+				}
+			}
+		}
+	} else if (sec->key_type == KTYPE_INTEGER) {
+		if (!wordlen) {
+			if (state >= len)
+				return NULL;
+			snprintf(buf, sizeof(buf), "%d", keys[state].i);
+			return strdup(buf);
+		}
+		for (i = 0; i < len; ++i) {
+			snprintf(buf, sizeof(buf), "%d", keys[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;
+
+	if (pos != 4)
+		return 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;
+
+	if (pos != 4)
+		return 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;
+
+	if (pos == 4) {
+		LOCK(cm);
+		retval = __complete_section(cm, word, state);
+		UNLOCK(cm);
+	} else if (pos == 5) {
+		LOCK(cm);
+		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];
+

[... 437 lines stripped ...]


More information about the asterisk-commits mailing list