[zaptel-commits] branch group/menuselect r1124 - in
/team/group/menuselect: ./ build_tools/ mxml/
zaptel-commits at lists.digium.com
zaptel-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 zaptel-commits
mailing list