[svn-commits] branch group/menuselect r1124 - in /team/group/menuselect: ./ build_tools/ mxml/

svn-commits at lists.digium.com svn-commits at lists.digium.com
Mon Jun 19 20:01:11 MST 2006


Author: russell
Date: Mon Jun 19 22:01:11 2006
New Revision: 1124

URL: http://svn.digium.com/view/zaptel?rev=1124&view=rev
Log:
import a bunch of stuff, allow menuselect to build

Added:
    team/group/menuselect/build_tools/Makefile   (with props)
    team/group/menuselect/build_tools/linkedlists.h   (with props)
    team/group/menuselect/build_tools/menuselect.c   (with props)
    team/group/menuselect/build_tools/menuselect.h   (with props)
    team/group/menuselect/build_tools/menuselect_curses.c   (with props)
    team/group/menuselect/makeopts.xml   (with props)
    team/group/menuselect/mxml/
    team/group/menuselect/mxml/ANNOUNCEMENT   (with props)
    team/group/menuselect/mxml/CHANGES   (with props)
    team/group/menuselect/mxml/COPYING   (with props)
    team/group/menuselect/mxml/Makefile   (with props)
    team/group/menuselect/mxml/Makefile.in   (with props)
    team/group/menuselect/mxml/README   (with props)
    team/group/menuselect/mxml/config.h   (with props)
    team/group/menuselect/mxml/config.h.in   (with props)
    team/group/menuselect/mxml/configure   (with props)
    team/group/menuselect/mxml/install-sh   (with props)
    team/group/menuselect/mxml/mxml-attr.c   (with props)
    team/group/menuselect/mxml/mxml-entity.c   (with props)
    team/group/menuselect/mxml/mxml-file.c   (with props)
    team/group/menuselect/mxml/mxml-index.c   (with props)
    team/group/menuselect/mxml/mxml-node.c   (with props)
    team/group/menuselect/mxml/mxml-private.c   (with props)
    team/group/menuselect/mxml/mxml-search.c   (with props)
    team/group/menuselect/mxml/mxml-set.c   (with props)
    team/group/menuselect/mxml/mxml-string.c   (with props)
    team/group/menuselect/mxml/mxml.h   (with props)
    team/group/menuselect/mxml/mxml.list   (with props)
    team/group/menuselect/mxml/mxml.list.in   (with props)
    team/group/menuselect/mxml/mxml.pc   (with props)
    team/group/menuselect/mxml/mxml.pc.in   (with props)
Modified:
    team/group/menuselect/Makefile

Modified: team/group/menuselect/Makefile
URL: http://svn.digium.com/view/zaptel/team/group/menuselect/Makefile?rev=1124&r1=1123&r2=1124&view=diff
==============================================================================
--- team/group/menuselect/Makefile (original)
+++ team/group/menuselect/Makefile Mon Jun 19 22:01:11 2006
@@ -8,6 +8,7 @@
 .EXPORT_ALL_VARIABLES:
 
 HOSTCC=gcc
+CC=gcc
 ifeq ($(PWD),)
 PWD=$(shell pwd)
 endif
@@ -452,3 +453,13 @@
 	rm -f fw2h vpm450m_fw.h
 
 FORCE:
+
+menuselect: build_tools/menuselect
+	- at build_tools/menuselect $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts && echo "menuselect changes saved!" || echo "menuselect changes NOT saved!"
+
+build_tools/menuselect: build_tools/menuselect.c build_tools/menuselect_curses.c build_tools/menuselect.h mxml/libmxml.a
+	$(MAKE) -C build_tools menuselect
+
+mxml/libmxml.a:
+	@cd mxml && unset CFLAGS LIBS && test -f config.h || ./configure
+	$(MAKE) -C mxml libmxml.a

Added: team/group/menuselect/build_tools/Makefile
URL: http://svn.digium.com/view/zaptel/team/group/menuselect/build_tools/Makefile?rev=1124&view=auto
==============================================================================
--- team/group/menuselect/build_tools/Makefile (added)
+++ team/group/menuselect/build_tools/Makefile Mon Jun 19 22:01:11 2006
@@ -1,0 +1,18 @@
+MENUSELECT_OBJS=menuselect.o menuselect_curses.o
+MENUSELECT_CFLAGS=-g -c -D_GNU_SOURCE -DMENUSELECT -I..
+MENUSELECT_LIBS=../mxml/libmxml.a -lncurses
+
+menuselect: $(MENUSELECT_OBJS)
+	$(CC) -g -Wall -o $@ $(MENUSELECT_OBJS) $(MENUSELECT_LIBS)
+
+menuselect.o: menuselect.c menuselect.h
+	$(CC) -Wall -o $@ $(MENUSELECT_CFLAGS) $<
+
+menuselect_curses.o: menuselect_curses.c menuselect.h
+	$(CC) -Wall -o $@ $(MENUSELECT_CFLAGS) $<
+
+clean:
+	rm -f menuselect *.o
+
+dist-clean: clean
+	rm -f menuselect-deps

Propchange: team/group/menuselect/build_tools/Makefile
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/menuselect/build_tools/Makefile
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/menuselect/build_tools/Makefile
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/group/menuselect/build_tools/linkedlists.h
URL: http://svn.digium.com/view/zaptel/team/group/menuselect/build_tools/linkedlists.h?rev=1124&view=auto
==============================================================================
--- team/group/menuselect/build_tools/linkedlists.h (added)
+++ team/group/menuselect/build_tools/linkedlists.h Mon Jun 19 22:01:11 2006
@@ -1,0 +1,499 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster at digium.com>
+ * Kevin P. Fleming <kpfleming at digium.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.
+ */
+
+#ifndef ASTERISK_LINKEDLISTS_H
+#define ASTERISK_LINKEDLISTS_H
+
+#include "asterisk/lock.h"
+
+/*!
+  \file linkedlists.h
+  \brief A set of macros to manage forward-linked lists.
+*/
+
+/*!
+  \brief Attempts to lock a list.
+  \param head This is a pointer to the list head structure
+
+  This macro attempts to place an exclusive lock in the
+  list head structure pointed to by head.
+  Returns non-zero on success, 0 on failure
+*/
+#define AST_LIST_LOCK(head)						\
+	ast_mutex_lock(&(head)->lock) 
+	
+/*!
+  \brief Attempts to unlock a list.
+  \param head This is a pointer to the list head structure
+
+  This macro attempts to remove an exclusive lock from the
+  list head structure pointed to by head. If the list
+  was not locked by this thread, this macro has no effect.
+*/
+#define AST_LIST_UNLOCK(head) 						\
+	ast_mutex_unlock(&(head)->lock)
+
+/*!
+  \brief Defines a structure to be used to hold a list of specified type.
+  \param name This will be the name of the defined structure.
+  \param type This is the type of each list entry.
+
+  This macro creates a structure definition that can be used
+  to hold a list of the entries of type \a type. It does not actually
+  declare (allocate) a structure; to do that, either follow this
+  macro with the desired name of the instance you wish to declare,
+  or use the specified \a name to declare instances elsewhere.
+
+  Example usage:
+  \code
+  static AST_LIST_HEAD(entry_list, entry) entries;
+  \endcode
+
+  This would define \c struct \c entry_list, and declare an instance of it named
+  \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_LIST_HEAD(name, type)					\
+struct name {								\
+	struct type *first;						\
+	struct type *last;						\
+	ast_mutex_t lock;						\
+}
+
+/*!
+  \brief Defines a structure to be used to hold a list of specified type (with no lock).
+  \param name This will be the name of the defined structure.
+  \param type This is the type of each list entry.
+
+  This macro creates a structure definition that can be used
+  to hold a list of the entries of type \a type. It does not actually
+  declare (allocate) a structure; to do that, either follow this
+  macro with the desired name of the instance you wish to declare,
+  or use the specified \a name to declare instances elsewhere.
+
+  Example usage:
+  \code
+  static AST_LIST_HEAD_NOLOCK(entry_list, entry) entries;
+  \endcode
+
+  This would define \c struct \c entry_list, and declare an instance of it named
+  \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_LIST_HEAD_NOLOCK(name, type)				\
+struct name {								\
+	struct type *first;						\
+	struct type *last;						\
+}
+
+/*!
+  \brief Defines initial values for a declaration of AST_LIST_HEAD
+*/
+#define AST_LIST_HEAD_INIT_VALUE	{		\
+	.first = NULL,					\
+	.last = NULL,					\
+	.lock = AST_MUTEX_INIT_VALUE,			\
+	}
+
+/*!
+  \brief Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK
+*/
+#define AST_LIST_HEAD_NOLOCK_INIT_VALUE	{	\
+	.first = NULL,					\
+	.last = NULL,					\
+	}
+
+/*!
+  \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+  \param name This will be the name of the defined structure.
+  \param type This is the type of each list entry.
+
+  This macro creates a structure definition that can be used
+  to hold a list of the entries of type \a type, and allocates an instance
+  of it, initialized to be empty.
+
+  Example usage:
+  \code
+  static AST_LIST_HEAD_STATIC(entry_list, entry);
+  \endcode
+
+  This would define \c struct \c entry_list, intended to hold a list of
+  type \c struct \c entry.
+*/
+#define AST_LIST_HEAD_STATIC(name, type)				\
+struct name {								\
+	struct type *first;						\
+	struct type *last;						\
+	ast_mutex_t lock;						\
+} name = AST_LIST_HEAD_INIT_VALUE
+
+/*!
+  \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+
+  This is the same as AST_LIST_HEAD_STATIC, except without the lock included.
+*/
+#define AST_LIST_HEAD_NOLOCK_STATIC(name, type)				\
+struct name {								\
+	struct type *first;						\
+	struct type *last;						\
+} name = AST_LIST_HEAD_NOLOCK_INIT_VALUE
+
+/*!
+  \brief Initializes a list head structure with a specified first entry.
+  \param head This is a pointer to the list head structure
+  \param entry pointer to the list entry that will become the head of the list
+
+  This macro initializes a list head structure by setting the head
+  entry to the supplied value and recreating the embedded lock.
+*/
+#define AST_LIST_HEAD_SET(head, entry) do {				\
+	(head)->first = (entry);					\
+	(head)->last = (entry);						\
+	ast_mutex_init(&(head)->lock);					\
+} while (0)
+
+/*!
+  \brief Initializes a list head structure with a specified first entry.
+  \param head This is a pointer to the list head structure
+  \param entry pointer to the list entry that will become the head of the list
+
+  This macro initializes a list head structure by setting the head
+  entry to the supplied value.
+*/
+#define AST_LIST_HEAD_SET_NOLOCK(head, entry) do {			\
+	(head)->first = (entry);					\
+	(head)->last = (entry);						\
+} while (0)
+
+/*!
+  \brief Declare a forward link structure inside a list entry.
+  \param type This is the type of each list entry.
+
+  This macro declares a structure to be used to link list entries together.
+  It must be used inside the definition of the structure named in
+  \a type, as follows:
+
+  \code
+  struct list_entry {
+  	...
+  	AST_LIST_ENTRY(list_entry) list;
+  }
+  \endcode
+
+  The field name \a list here is arbitrary, and can be anything you wish.
+*/
+#define AST_LIST_ENTRY(type)						\
+struct {								\
+	struct type *next;						\
+}
+ 
+/*!
+  \brief Returns the first entry contained in a list.
+  \param head This is a pointer to the list head structure
+ */
+#define	AST_LIST_FIRST(head)	((head)->first)
+
+/*!
+  \brief Returns the last entry contained in a list.
+  \param head This is a pointer to the list tail structure
+ */
+#define	AST_LIST_LAST(head)	((head)->last)
+
+/*!
+  \brief Returns the next entry in the list after the given entry.
+  \param elm This is a pointer to the current entry.
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+*/
+#define AST_LIST_NEXT(elm, field)	((elm)->field.next)
+
+/*!
+  \brief Checks whether the specified list contains any entries.
+  \param head This is a pointer to the list head structure
+
+  Returns non-zero if the list has entries, zero if not.
+ */
+#define	AST_LIST_EMPTY(head)	(AST_LIST_FIRST(head) == NULL)
+
+/*!
+  \brief Loops over (traverses) the entries in a list.
+  \param head This is a pointer to the list head structure
+  \param var This is the name of the variable that will hold a pointer to the
+  current list entry on each iteration. It must be declared before calling
+  this macro.
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+
+  This macro is use to loop over (traverse) the entries in a list. It uses a
+  \a for loop, and supplies the enclosed code with a pointer to each list
+  entry as it loops. It is typically used as follows:
+  \code
+  static AST_LIST_HEAD(entry_list, list_entry) entries;
+  ...
+  struct list_entry {
+  	...
+  	AST_LIST_ENTRY(list_entry) list;
+  }
+  ...
+  struct list_entry *current;
+  ...
+  AST_LIST_TRAVERSE(&entries, current, list) {
+     (do something with current here)
+  }
+  \endcode
+  \warning If you modify the forward-link pointer contained in the \a current entry while
+  inside the loop, the behavior will be unpredictable. At a minimum, the following
+  macros will modify the forward-link pointer, and should not be used inside
+  AST_LIST_TRAVERSE() against the entry pointed to by the \a current pointer without
+  careful consideration of their consequences:
+  \li AST_LIST_NEXT() (when used as an lvalue)
+  \li AST_LIST_INSERT_AFTER()
+  \li AST_LIST_INSERT_HEAD()
+  \li AST_LIST_INSERT_TAIL()
+*/
+#define AST_LIST_TRAVERSE(head,var,field) 				\
+	for((var) = (head)->first; (var); (var) = (var)->field.next)
+
+/*!
+  \brief Loops safely over (traverses) the entries in a list.
+  \param head This is a pointer to the list head structure
+  \param var This is the name of the variable that will hold a pointer to the
+  current list entry on each iteration. It must be declared before calling
+  this macro.
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+
+  This macro is used to safely loop over (traverse) the entries in a list. It
+  uses a \a for loop, and supplies the enclosed code with a pointer to each list
+  entry as it loops. It is typically used as follows:
+
+  \code
+  static AST_LIST_HEAD(entry_list, list_entry) entries;
+  ...
+  struct list_entry {
+  	...
+  	AST_LIST_ENTRY(list_entry) list;
+  }
+  ...
+  struct list_entry *current;
+  ...
+  AST_LIST_TRAVERSE_SAFE_BEGIN(&entries, current, list) {
+     (do something with current here)
+  }
+  AST_LIST_TRAVERSE_SAFE_END;
+  \endcode
+
+  It differs from AST_LIST_TRAVERSE() in that the code inside the loop can modify
+  (or even free, after calling AST_LIST_REMOVE_CURRENT()) the entry pointed to by
+  the \a current pointer without affecting the loop traversal.
+*/
+#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field) {				\
+	typeof((head)->first) __list_next;						\
+	typeof((head)->first) __list_prev = NULL;					\
+	typeof((head)->first) __new_prev = NULL;					\
+	for ((var) = (head)->first, __new_prev = (var),					\
+	      __list_next = (var) ? (var)->field.next : NULL;				\
+	     (var);									\
+	     __list_prev = __new_prev, (var) = __list_next,				\
+	     __new_prev = (var),							\
+	     __list_next = (var) ? (var)->field.next : NULL				\
+	    )
+
+/*!
+  \brief Removes the \a current entry from a list during a traversal.
+  \param head This is a pointer to the list head structure
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+
+  \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN()
+  block; it is used to unlink the current entry from the list without affecting
+  the list traversal (and without having to re-traverse the list to modify the
+  previous entry, if any).
+ */
+#define AST_LIST_REMOVE_CURRENT(head, field)						\
+	__new_prev = __list_prev;							\
+	if (__list_prev)								\
+		__list_prev->field.next = __list_next;					\
+	else										\
+		(head)->first = __list_next;						\
+	if (!__list_next)								\
+		(head)->last = __list_prev;
+
+/*!
+  \brief Inserts a list entry before the current entry during a traversal.
+  \param head This is a pointer to the list head structure
+  \param elm This is a pointer to the entry to be inserted.
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+
+  \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN()
+  block.
+ */
+#define AST_LIST_INSERT_BEFORE_CURRENT(head, elm, field) do {		\
+	if (__list_prev) {						\
+		(elm)->field.next = __list_prev->field.next;		\
+		__list_prev->field.next = elm;				\
+	} else {							\
+		(elm)->field.next = (head)->first;			\
+		(head)->first = (elm);					\
+	}								\
+	__new_prev = (elm);						\
+} while (0)
+
+/*!
+  \brief Closes a safe loop traversal block.
+ */
+#define AST_LIST_TRAVERSE_SAFE_END  }
+
+/*!
+  \brief Initializes a list head structure.
+  \param head This is a pointer to the list head structure
+
+  This macro initializes a list head structure by setting the head
+  entry to \a NULL (empty list) and recreating the embedded lock.
+*/
+#define AST_LIST_HEAD_INIT(head) {					\
+	(head)->first = NULL;						\
+	(head)->last = NULL;						\
+	ast_mutex_init(&(head)->lock);					\
+}
+
+/*!
+  \brief Destroys a list head structure.
+  \param head This is a pointer to the list head structure
+
+  This macro destroys a list head structure by setting the head
+  entry to \a NULL (empty list) and destroying the embedded lock.
+  It does not free the structure from memory.
+*/
+#define AST_LIST_HEAD_DESTROY(head) {					\
+	(head)->first = NULL;						\
+	(head)->last = NULL;						\
+	ast_mutex_destroy(&(head)->lock);				\
+}
+
+/*!
+  \brief Initializes a list head structure.
+  \param head This is a pointer to the list head structure
+
+  This macro initializes a list head structure by setting the head
+  entry to \a NULL (empty list). There is no embedded lock handling
+  with this macro.
+*/
+#define AST_LIST_HEAD_INIT_NOLOCK(head) {				\
+	(head)->first = NULL;						\
+	(head)->last = NULL;						\
+}
+
+/*!
+  \brief Inserts a list entry after a given entry.
+  \param head This is a pointer to the list head structure
+  \param listelm This is a pointer to the entry after which the new entry should
+  be inserted.
+  \param elm This is a pointer to the entry to be inserted.
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+ */
+#define AST_LIST_INSERT_AFTER(head, listelm, elm, field) do {		\
+	(elm)->field.next = (listelm)->field.next;			\
+	(listelm)->field.next = (elm);					\
+	if ((head)->last == (listelm))					\
+		(head)->last = (elm);					\
+} while (0)
+
+/*!
+  \brief Inserts a list entry at the head of a list.
+  \param head This is a pointer to the list head structure
+  \param elm This is a pointer to the entry to be inserted.
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+ */
+#define AST_LIST_INSERT_HEAD(head, elm, field) do {			\
+		(elm)->field.next = (head)->first;			\
+		(head)->first = (elm);					\
+		if (!(head)->last)					\
+			(head)->last = (elm);				\
+} while (0)
+
+/*!
+  \brief Appends a list entry to the tail of a list.
+  \param head This is a pointer to the list head structure
+  \param elm This is a pointer to the entry to be appended.
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+
+  Note: The link field in the appended entry is \b not modified, so if it is
+  actually the head of a list itself, the entire list will be appended
+  temporarily (until the next AST_LIST_INSERT_TAIL is performed).
+ */
+#define AST_LIST_INSERT_TAIL(head, elm, field) do {			\
+      if (!(head)->first) {						\
+		(head)->first = (elm);					\
+		(head)->last = (elm);					\
+      } else {								\
+		(head)->last->field.next = (elm);			\
+		(head)->last = (elm);					\
+      }									\
+} while (0)
+
+/*!
+  \brief Removes and returns the head entry from a list.
+  \param head This is a pointer to the list head structure
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+
+  Removes the head entry from the list, and returns a pointer to it.
+  This macro is safe to call on an empty list.
+ */
+#define AST_LIST_REMOVE_HEAD(head, field) ({				\
+		typeof((head)->first) cur = (head)->first;		\
+		if (cur) {						\
+			(head)->first = cur->field.next;		\
+			cur->field.next = NULL;				\
+			if ((head)->last == cur)			\
+				(head)->last = NULL;			\
+		}							\
+		cur;							\
+	})
+
+/*!
+  \brief Removes a specific entry from a list.
+  \param head This is a pointer to the list head structure
+  \param elm This is a pointer to the entry to be removed.
+  \param field This is the name of the field (declared using AST_LIST_ENTRY())
+  used to link entries of this list together.
+  \warning The removed entry is \b not freed nor modified in any way.
+ */
+#define AST_LIST_REMOVE(head, elm, field) do {			        \
+	if ((head)->first == (elm)) {					\
+		(head)->first = (elm)->field.next;			\
+		if ((head)->last == (elm))			\
+			(head)->last = NULL;			\
+	} else {								\
+		typeof(elm) curelm = (head)->first;			\
+		while (curelm && (curelm->field.next != (elm)))			\
+			curelm = curelm->field.next;			\
+		if (curelm) { \
+			curelm->field.next = (elm)->field.next;			\
+			if ((head)->last == (elm))				\
+				(head)->last = curelm;				\
+		} \
+	}								\
+        (elm)->field.next = NULL;                                       \
+} while (0)
+
+#endif /* _ASTERISK_LINKEDLISTS_H */

Propchange: team/group/menuselect/build_tools/linkedlists.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/menuselect/build_tools/linkedlists.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/menuselect/build_tools/linkedlists.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/group/menuselect/build_tools/menuselect.c
URL: http://svn.digium.com/view/zaptel/team/group/menuselect/build_tools/menuselect.c?rev=1124&view=auto
==============================================================================
--- team/group/menuselect/build_tools/menuselect.c (added)
+++ team/group/menuselect/build_tools/menuselect.c Mon Jun 19 22:01:11 2006
@@ -1,0 +1,740 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005 - 2006, Russell Bryant
+ *
+ * Russell Bryant <russell at digium.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
+ *
+ * \author Russell Bryant <russell at digium.com>
+ * 
+ * \brief A menu-driven system for Asterisk module selection
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mxml/mxml.h"
+#include "menuselect.h"
+
+#include "linkedlists.h"
+
+#undef MENUSELECT_DEBUG
+
+/*! The list of categories */
+struct categories categories = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+/*!
+   We have to maintain a pointer to the root of the trees generated from reading
+   the build options XML files so that we can free it when we're done.  We don't
+   copy any of the information over from these trees. Our list is just a 
+   convenient mapping to the information contained in these lists with one
+   additional piece of information - whether the build option is enabled or not.
+*/
+struct tree {
+	/*! the root of the tree */
+	mxml_node_t *root;
+	/*! for linking */
+	AST_LIST_ENTRY(tree) list;
+};
+
+/*! The list of trees from makeopts.xml files */
+static AST_LIST_HEAD_NOLOCK_STATIC(trees, tree);
+
+static const char * const makeopts_files[] = {
+	"makeopts.xml"
+};
+
+static char *output_makeopts = OUTPUT_MAKEOPTS_DEFAULT;
+
+/*! This is set to 1 if menuselect.makeopts pre-existed the execution of this app */
+static int existing_config = 0;
+
+/*! This is set when the --check-deps argument is provided. */
+static int check_deps = 0;
+
+#if !defined(ast_strdupa) && defined(__GNUC__)
+#define ast_strdupa(s)                                                    \
+	(__extension__                                                    \
+	({                                                                \
+		const char *__old = (s);                                  \
+		size_t __len = strlen(__old) + 1;                         \
+		char *__new = __builtin_alloca(__len);                    \
+		memcpy (__new, __old, __len);                             \
+		__new;                                                    \
+	}))
+#endif
+
+/*! \brief return a pointer to the first non-whitespace character */
+static inline char *skip_blanks(char *str)
+{
+	if (!str)
+		return NULL;
+
+	while (*str && *str < 33)
+		str++;
+
+	return str;
+}
+
+/*! \brief Add a category to the category list, ensuring that there are no duplicates */
+static int add_category(struct category *cat)
+{
+	struct category *tmp;
+
+	AST_LIST_TRAVERSE(&categories, tmp, list) {
+		if (!strcmp(tmp->name, cat->name)) {
+			fprintf(stderr, "Category '%s' specified more than once!\n", cat->name);
+			return -1;
+		}
+	}
+	AST_LIST_INSERT_TAIL(&categories, cat, list);
+
+	return 0;
+}
+
+/*! \brief Add a member to the member list of a category, ensuring that there are no duplicates */
+static int add_member(struct member *mem, struct category *cat)
+{
+	struct member *tmp;
+
+	AST_LIST_TRAVERSE(&cat->members, tmp, list) {
+		if (!strcmp(tmp->name, mem->name)) {
+			fprintf(stderr, "Member '%s' already exists in category '%s', ignoring.\n", mem->name, cat->name);
+			return -1;
+		}
+	}
+	AST_LIST_INSERT_TAIL(&cat->members, mem, list);
+
+	return 0;
+}
+
+/*! \brief Free a member structure and all of its members */
+static void free_member(struct member *mem)
+{
+	struct depend *dep;
+	struct conflict *cnf;
+
+	while ((dep = AST_LIST_REMOVE_HEAD(&mem->deps, list)))
+		free(dep);
+	while ((cnf = AST_LIST_REMOVE_HEAD(&mem->conflicts, list)))
+		free(cnf);
+	free(mem);
+}
+
+/*! \brief Parse an input makeopts file */
+static int parse_makeopts_xml(const char *makeopts_xml)
+{
+	FILE *f;
+	struct category *cat;
+	struct tree *tree;
+	struct member *mem;
+	struct depend *dep;
+	struct conflict *cnf;
+	mxml_node_t *cur;
+	mxml_node_t *cur2;
+	mxml_node_t *cur3;
+	mxml_node_t *menu;
+	const char *tmp;
+
+	if (!(f = fopen(makeopts_xml, "r"))) {
+		fprintf(stderr, "Unable to open '%s' for reading!\n", makeopts_xml);
+		return -1;
+	}
+
+	if (!(tree = calloc(1, sizeof(*tree)))) {
+		fclose(f);
+		return -1;
+	}
+
+	if (!(tree->root = mxmlLoadFile(NULL, f, MXML_OPAQUE_CALLBACK))) {
+		fclose(f);
+		free(tree);
+		return -1;
+	}
+
+	AST_LIST_INSERT_HEAD(&trees, tree, list);
+
+	menu = mxmlFindElement(tree->root, tree->root, "menu", NULL, NULL, MXML_DESCEND);
+	for (cur = mxmlFindElement(menu, menu, "category", NULL, NULL, MXML_DESCEND);
+	     cur;
+	     cur = mxmlFindElement(cur, menu, "category", NULL, NULL, MXML_DESCEND))
+	{
+		if (!(cat = calloc(1, sizeof(*cat))))
+			return -1;
+
+		cat->name = mxmlElementGetAttr(cur, "name");
+		cat->displayname = mxmlElementGetAttr(cur, "displayname");
+		if ((tmp = mxmlElementGetAttr(cur, "positive_output")))
+			cat->positive_output = !strcasecmp(tmp, "yes");
+		cat->remove_on_change = mxmlElementGetAttr(cur, "remove_on_change");
+
+		if (add_category(cat)) {
+			free(cat);
+			continue;
+		}
+
+		for (cur2 = mxmlFindElement(cur, cur, "member", NULL, NULL, MXML_DESCEND);
+		     cur2;
+		     cur2 = mxmlFindElement(cur2, cur, "member", NULL, NULL, MXML_DESCEND))
+		{
+			if (!(mem = calloc(1, sizeof(*mem))))
+				return -1;
+			
+			mem->name = mxmlElementGetAttr(cur2, "name");
+			mem->displayname = mxmlElementGetAttr(cur2, "displayname");
+		
+			mem->remove_on_change = mxmlElementGetAttr(cur2, "remove_on_change");
+
+			if (!cat->positive_output)
+				mem->was_enabled = mem->enabled = 1;
+	
+			cur3 = mxmlFindElement(cur2, cur2, "defaultenabled", NULL, NULL, MXML_DESCEND);
+			if (cur3 && cur3->child)
+				mem->defaultenabled = cur3->child->value.opaque;
+			
+			for (cur3 = mxmlFindElement(cur2, cur2, "depend", NULL, NULL, MXML_DESCEND);
+			     cur3 && cur3->child;
+			     cur3 = mxmlFindElement(cur3, cur2, "depend", NULL, NULL, MXML_DESCEND))
+			{
+				if (!(dep = calloc(1, sizeof(*dep)))) {
+					free_member(mem);
+					return -1;
+				}
+				if (!strlen_zero(cur3->child->value.opaque)) {
+					dep->name = cur3->child->value.opaque;
+					AST_LIST_INSERT_HEAD(&mem->deps, dep, list);
+				} else
+					free(dep);
+			}
+
+			for (cur3 = mxmlFindElement(cur2, cur2, "conflict", NULL, NULL, MXML_DESCEND);
+			     cur3 && cur3->child;
+			     cur3 = mxmlFindElement(cur3, cur2, "conflict", NULL, NULL, MXML_DESCEND))
+			{
+				if (!(cnf = calloc(1, sizeof(*cnf)))) {
+					free_member(mem);
+					return -1;
+				}
+				if (!strlen_zero(cur3->child->value.opaque)) {
+					cnf->name = cur3->child->value.opaque;
+					AST_LIST_INSERT_HEAD(&mem->conflicts, cnf, list);
+				} else
+					free(cnf);
+			}
+
+			if (add_member(mem, cat))
+				free_member(mem);
+		}
+	}
+
+	fclose(f);
+
+	return 0;
+}
+
+/*! \brief Process dependencies against the input dependencies file */
+static int process_deps(void)
+{
+	struct category *cat;
+	struct member *mem;
+	struct depend *dep;
+	struct conflict *cnf;
+	FILE *f;
+	struct dep_file {
+		char name[32];
+		int met;
+		AST_LIST_ENTRY(dep_file) list;
+	} *dep_file;
+	AST_LIST_HEAD_NOLOCK_STATIC(deps_file, dep_file);
+	char buf[80];
+	char *p;
+	int res = 0;
+
+	if (!(f = fopen(MENUSELECT_DEPS, "r"))) {
+		fprintf(stderr, "Unable to open '%s' for reading!  Did you run ./configure ?\n", MENUSELECT_DEPS);
+		return -1;
+	}
+
+	/* Build a dependency list from the file generated by configure */	
+	while (memset(buf, 0, sizeof(buf)), fgets(buf, sizeof(buf), f)) {
+		p = buf;
+		strsep(&p, "=");
+		if (!p)
+			continue;
+		if (!(dep_file = calloc(1, sizeof(*dep_file))))
+			break;
+		strncpy(dep_file->name, buf, sizeof(dep_file->name) - 1);
+		dep_file->met = atoi(p);
+		AST_LIST_INSERT_TAIL(&deps_file, dep_file, list);
+	}
+
+	fclose(f);
+
+	/* Process dependencies of all modules */
+	AST_LIST_TRAVERSE(&categories, cat, list) {
+		AST_LIST_TRAVERSE(&cat->members, mem, list) {
+			AST_LIST_TRAVERSE(&mem->deps, dep, list) {
+				mem->depsfailed = 1;
+				AST_LIST_TRAVERSE(&deps_file, dep_file, list) {
+					if (!strcasecmp(dep_file->name, dep->name)) {
+						if (dep_file->met)
+							mem->depsfailed = 0;
+						break;
+					}
+				}
+				if (mem->depsfailed)
+					break; /* This dependency is not met, so we can stop now */
+			}
+		}
+	}
+
+	/* Process conflicts of all modules */
+	AST_LIST_TRAVERSE(&categories, cat, list) {
+		AST_LIST_TRAVERSE(&cat->members, mem, list) {
+			AST_LIST_TRAVERSE(&mem->conflicts, cnf, list) {
+				mem->conflictsfailed = 0;
+				AST_LIST_TRAVERSE(&deps_file, dep_file, list) {
+					if (!strcasecmp(dep_file->name, cnf->name)) {
+						if (dep_file->met)
+							mem->conflictsfailed = 1;
+						break;
+					}
+				}
+				if (mem->conflictsfailed)
+					break; /* This conflict was found, so we can stop now */
+			}
+		}
+	}
+
+	/* Free the dependency list we built from the file */
+	while ((dep_file = AST_LIST_REMOVE_HEAD(&deps_file, list)))
+		free(dep_file);
+
+	return res;
+}
+
+/*! \brief Iterate through all of the input makeopts files and call the parse function on them */
+static int build_member_list(void)
+{
+	int i;
+	int res = -1;
+
+	for (i = 0; i < (sizeof(makeopts_files) / sizeof(makeopts_files[0])); i++) {
+		if ((res = parse_makeopts_xml(makeopts_files[i]))) {
+			fprintf(stderr, "Error parsing '%s'!\n", makeopts_files[i]);
+			break;
+		}
+	}
+
+	return res;
+}
+
+/*! \brief Given the string representation of a member and category, mark it as present in a given input file */
+static void mark_as_present(const char *member, const char *category)
+{
+	struct category *cat;
+	struct member *mem;
+
+	AST_LIST_TRAVERSE(&categories, cat, list) {
+		if (strcmp(category, cat->name))
+			continue;
+		AST_LIST_TRAVERSE(&cat->members, mem, list) {
+			if (!strcmp(member, mem->name)) {
+				mem->was_enabled = mem->enabled = cat->positive_output;
+				break;
+			}
+		}
+		if (!mem)
+			fprintf(stderr, "member '%s' in category '%s' not found, ignoring.\n", member, category);
+		break;
+	}
+
+	if (!cat)
+		fprintf(stderr, "category '%s' not found! Can't mark '%s' as disabled.\n", category, member);
+}
+
+/*! \brief Toggle a member of a category at the specified index to enabled/disabled */
+void toggle_enabled(struct category *cat, int index)
+{
+	struct member *mem;
+	int i = 0;
+
+	AST_LIST_TRAVERSE(&cat->members, mem, list) {
+		if (i++ == index)
+			break;
+	}
+
+	if (mem && !(mem->depsfailed || mem->conflictsfailed)) {
+		mem->enabled = !mem->enabled;
+	}
+}
+
+/*! \brief Process a previously failed dependency
+ *
+ * If a module was previously disabled because of a failed dependency
+ * or a conflict, and not because the user selected it to be that way,
+ * then it needs to be re-enabled by default if the problem is no longer present.
+ */
+static void process_prev_failed_deps(char *buf)
+{
+	const char *cat_name, *mem_name;
+	struct category *cat;
+	struct member *mem;
+
+	cat_name = strsep(&buf, "=");
+	mem_name = strsep(&buf, "\n");
+
+	if (!cat_name || !mem_name)
+		return;
+
+	AST_LIST_TRAVERSE(&categories, cat, list) {
+		if (strcasecmp(cat->name, cat_name))
+			continue;
+		AST_LIST_TRAVERSE(&cat->members, mem, list) {
+			if (strcasecmp(mem->name, mem_name))
+				continue;
+
+			if (!mem->depsfailed && !mem->conflictsfailed)
+				mem->enabled = 1;			
+	
+			break;
+		}
+		break;	
+	}
+
+	if (!cat || !mem)
+		fprintf(stderr, "Unable to find '%s' in category '%s'\n", mem_name, cat_name);
+}
+
+/*! \brief Parse an existing output makeopts file and enable members previously selected */
+static int parse_existing_config(const char *infile)
+{
+	FILE *f;
+	char buf[2048];
+	char *category, *parse, *member;
+	int lineno = 0;
+
+	if (!(f = fopen(infile, "r"))) {
+#ifdef MENUSELECT_DEBUG
+		/* This isn't really an error, so only print the message in debug mode */
+		fprintf(stderr, "Unable to open '%s' for reading existing config.\n", infile);
+#endif	
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		lineno++;
+
+		if (strlen_zero(buf))
+			continue;
+
+		/* skip lines that are not for this tool */
+		if (strncasecmp(buf, "MENUSELECT_", strlen("MENUSELECT_")))
+			continue;
+
+		parse = buf;
+		parse = skip_blanks(parse);
+		if (strlen_zero(parse))
+			continue;
+
+		/* Grab the category name */	
+		category = strsep(&parse, "=");
+		if (!parse) {
+			fprintf(stderr, "Invalid string in '%s' at line '%d'!\n", output_makeopts, lineno);
+			continue;
+		}
+		
+		parse = skip_blanks(parse);
+	
+		if (!strcasecmp(category, "MENUSELECT_DEPSFAILED")) {
+			process_prev_failed_deps(parse);
+			continue;
+		}
+	
+		while ((member = strsep(&parse, " \n"))) {
+			member = skip_blanks(member);
+			if (strlen_zero(member))
+				continue;
+			mark_as_present(member, category);
+		}
+	}
+
+	fclose(f);
+
+	return 0;
+}
+
+/*! \brief Create the output makeopts file that results from the user's selections */
+static int generate_makeopts_file(void)
+{
+	FILE *f;
+	struct category *cat;
+	struct member *mem;
+
+	if (!(f = fopen(output_makeopts, "w"))) {
+		fprintf(stderr, "Unable to open build configuration file (%s) for writing!\n", output_makeopts);
+		return -1;
+	}
+
+	/* Traverse all categories and members and output them as var/val pairs */
+	AST_LIST_TRAVERSE(&categories, cat, list) {
+		fprintf(f, "%s=", cat->name);
+		AST_LIST_TRAVERSE(&cat->members, mem, list) {
+			if ((!cat->positive_output && (!mem->enabled || mem->depsfailed || mem->conflictsfailed)) ||
+			    (cat->positive_output && mem->enabled && !mem->depsfailed && !mem->conflictsfailed))
+				fprintf(f, "%s ", mem->name);
+		}
+		fprintf(f, "\n");
+	}
+
+	/* Output which members were disabled because of failed dependencies or conflicts */
+	AST_LIST_TRAVERSE(&categories, cat, list) {
+		AST_LIST_TRAVERSE(&cat->members, mem, list) {
+			if (mem->depsfailed || mem->conflictsfailed)
+				fprintf(f, "MENUSELECT_DEPSFAILED=%s=%s\n", cat->name, mem->name);
+		}
+	}
+
+	fclose(f);
+
+	/* Traverse all categories and members and remove any files that are supposed
+	   to be removed when an item has been changed */
+	AST_LIST_TRAVERSE(&categories, cat, list) {
+		unsigned int had_changes = 0;
+		char *file, *buf;
+
+		AST_LIST_TRAVERSE(&cat->members, mem, list) {
+			if (mem->enabled == mem->was_enabled)
+				continue;
+
+			had_changes = 1;
+
+			if (mem->remove_on_change) {
+				for (buf = ast_strdupa(mem->remove_on_change), file = strsep(&buf, " ");
+				     file;
+				     file = strsep(&buf, " "))
+					unlink(file);
+			}
+		}
+
+		if (cat->remove_on_change && had_changes) {
+			for (buf = ast_strdupa(cat->remove_on_change), file = strsep(&buf, " ");
+			     file;
+			     file = strsep(&buf, " "))
+				unlink(file);
+		}
+	}
+
+	return 0;
+}
+
+#ifdef MENUSELECT_DEBUG
+/*! \brief Print out all of the information contained in our tree */
+static void dump_member_list(void)
+{
+	struct category *cat;
+	struct member *mem;
+	struct depend *dep;
+	struct conflict *cnf;
+
+	AST_LIST_TRAVERSE(&categories, cat, list) {
+		fprintf(stderr, "Category: '%s'\n", cat->name);
+		AST_LIST_TRAVERSE(&cat->members, mem, list) {
+			fprintf(stderr, "   ==>> Member: '%s'  (%s)", mem->name, mem->enabled ? "Enabled" : "Disabled");
+			fprintf(stderr, "        Was %s\n", mem->was_enabled ? "Enabled" : "Disabled");
+			AST_LIST_TRAVERSE(&mem->deps, dep, list)
+				fprintf(stderr, "      --> Depends on: '%s'\n", dep->name);
+			if (!AST_LIST_EMPTY(&mem->deps))
+				fprintf(stderr, "      --> Dependencies Met: %s\n", mem->depsfailed ? "No" : "Yes");	
+			AST_LIST_TRAVERSE(&mem->conflicts, cnf, list)
+				fprintf(stderr, "      --> Conflicts with: '%s'\n", cnf->name);
+			if (!AST_LIST_EMPTY(&mem->conflicts))
+				fprintf(stderr, "      --> Conflicts Found: %s\n", mem->conflictsfailed ? "Yes" : "No");
+		}
+	}
+}
+#endif
+
+/*! \brief Free all categories and their members */
+static void free_member_list(void)
+{
+	struct category *cat;
+	struct member *mem;
+	struct depend *dep;
+	struct conflict *cnf;
+
+	while ((cat = AST_LIST_REMOVE_HEAD(&categories, list))) {
+		while ((mem = AST_LIST_REMOVE_HEAD(&cat->members, list))) {
+			while ((dep = AST_LIST_REMOVE_HEAD(&mem->deps, list)))
+				free(dep);
+			while ((cnf = AST_LIST_REMOVE_HEAD(&mem->conflicts, list)))
+				free(cnf);
+			free(mem);
+		}
+		free(cat);
+	}
+}
+
+/*! \brief Free all of the XML trees */
+static void free_trees(void)
+{
+	struct tree *tree;
+
+	while ((tree = AST_LIST_REMOVE_HEAD(&trees, list))) {
+		mxmlDelete(tree->root);
+		free(tree);
+	}
+}
+
+/*! \brief Enable/Disable all members of a category as long as dependencies have been met and no conflicts are found */
+void set_all(struct category *cat, int val)
+{
+	struct member *mem;
+

[... 14047 lines stripped ...]


More information about the svn-commits mailing list