[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