aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/epan/mate/mate_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/epan/mate/mate_util.c')
-rw-r--r--plugins/epan/mate/mate_util.c1700
1 files changed, 1700 insertions, 0 deletions
diff --git a/plugins/epan/mate/mate_util.c b/plugins/epan/mate/mate_util.c
new file mode 100644
index 0000000000..8e4c5dd8ad
--- /dev/null
+++ b/plugins/epan/mate/mate_util.c
@@ -0,0 +1,1700 @@
+/* mate_util.c
+* MATE -- Meta Analysis Tracing Engine
+* Utility Library: Single Copy Strings and Attribute Value Pairs
+*
+* Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
+*
+* Wireshark - Network traffic analyzer
+* By Gerald Combs <gerald@wireshark.org>
+* Copyright 1998 Gerald Combs
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "mate.h"
+#include "mate_util.h"
+#include <wsutil/file_util.h>
+#include <wsutil/ws_printf.h> /* ws_g_warning */
+
+
+/***************************************************************************
+* dbg_print
+***************************************************************************
+* This is the debug facility of the thing.
+***************************************************************************/
+
+/* dbg_print:
+ * which: a pointer to the current level of debugging for a feature
+ * how: the level over which this message should be printed out
+ * where: the file on which to print (g_message if null)
+ * fmt, ...: what to print
+ */
+
+void dbg_print(const gint* which, gint how, FILE* where, const gchar* fmt, ... ) {
+ static gchar debug_buffer[DEBUG_BUFFER_SIZE];
+ va_list list;
+
+ if ( ! which || *which < how ) return;
+
+ va_start( list, fmt );
+ g_vsnprintf(debug_buffer,DEBUG_BUFFER_SIZE,fmt,list);
+ va_end( list );
+
+ if (! where) {
+ g_message("%s", debug_buffer);
+ } else {
+ fputs(debug_buffer,where);
+ fputs("\n",where);
+ }
+
+}
+
+
+/***************************************************************************
+ * single copy strings
+ ***************************************************************************
+ * Strings repeat more often than don't. In order to save memory
+ * we'll keep only one copy of each as key to a hash with a count of
+ * subscribers as value.
+ ***************************************************************************/
+
+/**
+ * scs_init:
+ *
+ * Initializes the scs hash.
+ **/
+
+struct _scs_collection {
+ GHashTable* hash; /* key: a string value: guint number of subscribers */
+};
+
+/* ToDo? free any string,ctr entries pointed to by the hash table ??
+ * XXX: AFAIKT destroy_scs_collection() might be called only when reading a
+ * mate config file. Since reading a new config file can apparently currently
+ * only be done once after starting Wireshark, in theory this fcn
+ * currently should never be called since there will never be an existing
+ * scs_collection to be destroyed.
+ */
+static void destroy_scs_collection(SCS_collection* c) {
+ if (c->hash) g_hash_table_destroy(c->hash);
+}
+
+static SCS_collection* scs_init(void) {
+ SCS_collection* c = (SCS_collection *)g_malloc(sizeof(SCS_collection));
+
+ c->hash = g_hash_table_new(g_str_hash,g_str_equal);
+
+ return c;
+}
+
+
+/**
+ * subscribe:
+ * @param c the scs hash
+ * @param s a string
+ *
+ * Checks if the given string exists already and if so it increases the count of
+ * subsscribers and returns a pointer to the stored string. If not It will copy
+ * the given string store it in the hash and return the pointer to the copy.
+ * Remember, containment is handled internally, take care of your own strings.
+ *
+ * Return value: a pointer to the subscribed string.
+ **/
+gchar* scs_subscribe(SCS_collection* c, const gchar* s) {
+ gchar* orig = NULL;
+ guint* ip = NULL;
+ size_t len = 0;
+
+ g_hash_table_lookup_extended(c->hash,(gconstpointer)s,(gpointer *)&orig,(gpointer *)&ip);
+
+ if (ip) {
+ (*ip)++;
+ } else {
+ ip = g_slice_new(guint);
+ *ip = 0;
+
+ len = strlen(s) + 1;
+
+ if (len <= SCS_SMALL_SIZE) {
+ len = SCS_SMALL_SIZE;
+ } else if (len <= SCS_MEDIUM_SIZE) {
+ len = SCS_MEDIUM_SIZE;
+ } else if (len <= SCS_LARGE_SIZE) {
+ len = SCS_LARGE_SIZE;
+ } else if (len < SCS_HUGE_SIZE) {
+ len = SCS_HUGE_SIZE;
+ } else {
+ len = SCS_HUGE_SIZE;
+ ws_g_warning("mate SCS: string truncated due to huge size");
+ }
+
+ orig = (gchar *)g_slice_alloc(len);
+ g_strlcpy(orig,s,len);
+
+ g_hash_table_insert(c->hash,orig,ip);
+ }
+
+ return orig;
+}
+
+/**
+ * unsubscribe:
+ * @param c the scs hash
+ * @param s a string.
+ *
+ * decreases the count of subscribers, if zero frees the internal copy of
+ * the string.
+ **/
+void scs_unsubscribe(SCS_collection* c, gchar* s) {
+ gchar* orig = NULL;
+ guint* ip = NULL;
+ size_t len = 0xffff;
+
+ g_hash_table_lookup_extended(c->hash,(gconstpointer)s,(gpointer *)&orig,(gpointer *)&ip);
+
+ if (ip) {
+ if (*ip == 0) {
+ g_hash_table_remove(c->hash,orig);
+
+ len = strlen(orig);
+
+ if (len < SCS_SMALL_SIZE) {
+ len = SCS_SMALL_SIZE;
+ } else if (len < SCS_MEDIUM_SIZE) {
+ len = SCS_MEDIUM_SIZE;
+ } else if (len < SCS_LARGE_SIZE) {
+ len = SCS_LARGE_SIZE;
+ } else {
+ len = SCS_HUGE_SIZE;
+ }
+
+ g_slice_free1(len, orig);
+ g_slice_free(guint,ip);
+ }
+ else {
+ (*ip)--;
+ }
+ } else {
+ ws_g_warning("unsubscribe: not subscribed");
+ }
+}
+
+/**
+ * scs_subscribe_printf:
+ * @param fmt a format string ...
+ *
+ * Formats the input and subscribes it.
+ *
+ * Return value: the stored copy of the formated string.
+ *
+ **/
+gchar* scs_subscribe_printf(SCS_collection* c, gchar* fmt, ...) {
+ va_list list;
+ static gchar buf[SCS_HUGE_SIZE];
+
+ va_start( list, fmt );
+ g_vsnprintf(buf, SCS_HUGE_SIZE, fmt, list);
+ va_end( list );
+
+ return scs_subscribe(c,buf);
+}
+
+/***************************************************************************
+* AVPs & Co.
+***************************************************************************
+* The Thing operates mainly on avps, avpls and loals
+* - attribute value pairs (two strings: the name and the value and an operator)
+* - avp lists a somehow sorted list of avps
+* - loal (list of avp lists) an arbitrarily sorted list of avpls
+*
+*
+***************************************************************************/
+
+
+typedef union _any_avp_type {
+ AVP avp;
+ AVPN avpn;
+ AVPL avpl;
+ LoAL loal;
+ LoALnode loaln;
+} any_avp_type;
+
+
+static SCS_collection* avp_strings = NULL;
+
+#ifdef _AVP_DEBUGGING
+static FILE* dbg_fp = NULL;
+
+static int dbg_level = 0;
+static int* dbg = &dbg_level;
+
+static int dbg_avp_level = 0;
+static int* dbg_avp = &dbg_avp_level;
+
+static int dbg_avp_op_level = 0;
+static int* dbg_avp_op = &dbg_avp_op_level;
+
+static int dbg_avpl_level = 0;
+static int* dbg_avpl = &dbg_avpl_level;
+
+static int dbg_avpl_op_level = 0;
+static int* dbg_avpl_op = &dbg_avpl_op_level;
+
+/**
+ * setup_avp_debug:
+ * @param fp the file in which to send debugging output.
+ * @param general a pointer to the level of debugging of facility "general"
+ * @param avp a pointer to the level of debugging of facility "avp"
+ * @param avp_op a pointer to the level of debugging of facility "avp_op"
+ * @param avpl a pointer to the level of debugging of facility "avpl"
+ * @param avpl_op a pointer to the level of debugging of facility "avpl_op"
+ *
+ * If enabled sets up the debug facilities for the avp library.
+ *
+ **/
+extern void setup_avp_debug(FILE* fp, int* general, int* avp, int* avp_op, int* avpl, int* avpl_op) {
+ dbg_fp = fp;
+ dbg = general;
+ dbg_avp = avp;
+ dbg_avp_op = avp_op;
+ dbg_avpl = avpl;
+ dbg_avpl_op = avpl_op;
+}
+
+#endif /* _AVP_DEBUGGING */
+
+/**
+ * avp_init:
+ *
+ * (Re)Initializes the avp library.
+ *
+ **/
+extern void avp_init(void) {
+
+ if (avp_strings) destroy_scs_collection(avp_strings);
+ avp_strings = scs_init();
+
+}
+
+/**
+ * new_avp_from_finfo:
+ * @param name the name the avp will have.
+ * @param finfo the field_info from which to fetch the data.
+ *
+ * Creates an avp from a field_info record.
+ *
+ * Return value: a pointer to the newly created avp.
+ *
+ **/
+extern AVP* new_avp_from_finfo(const gchar* name, field_info* finfo) {
+ AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
+ gchar* value;
+ gchar* repr;
+
+ new_avp_val->n = scs_subscribe(avp_strings, name);
+
+ repr = fvalue_to_string_repr(NULL, &finfo->value,FTREPR_DISPLAY,finfo->hfinfo->display);
+
+ if (repr) {
+ value = scs_subscribe(avp_strings, repr);
+ wmem_free(NULL, repr);
+#ifdef _AVP_DEBUGGING
+ dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: from string: %s",value);
+#endif
+ } else {
+#ifdef _AVP_DEBUGGING
+ dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: a proto: %s",finfo->hfinfo->abbrev);
+#endif
+ value = scs_subscribe(avp_strings, "");
+ }
+
+ new_avp_val->v = value;
+
+ new_avp_val->o = '=';
+
+#ifdef _AVP_DEBUGGING
+ dbg_print (dbg_avp,1,dbg_fp,"new_avp_from_finfo: %X %s%c%s;",(guint32) new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
+#endif
+
+ return new_avp_val;
+}
+
+
+/**
+ * new_avp:
+ * @param name the name the avp will have.
+ * @param value the value the avp will have.
+ * @param o the operator of this avp.
+ *
+ * Creates an avp given every parameter.
+ *
+ * Return value: a pointer to the newly created avp.
+ *
+ **/
+extern AVP* new_avp(const gchar* name, const gchar* value, gchar o) {
+ AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
+
+ new_avp_val->n = scs_subscribe(avp_strings, name);
+ new_avp_val->v = scs_subscribe(avp_strings, value);
+ new_avp_val->o = o;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avp,1,dbg_fp,"new_avp_val: %X %s%c%s;",(guint32) new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
+#endif
+ return new_avp_val;
+}
+
+
+/**
+* delete_avp:
+ * @param avp the avp to delete.
+ *
+ * Destroys an avp and releases the resources it uses.
+ *
+ **/
+extern void delete_avp(AVP* avp) {
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avp,1,dbg_fp,"delete_avp: %X %s%c%s;",(guint32) avp,avp->n,avp->o,avp->v);
+#endif
+
+ scs_unsubscribe(avp_strings, avp->n);
+ scs_unsubscribe(avp_strings, avp->v);
+ g_slice_free(any_avp_type,(any_avp_type*)avp);
+}
+
+
+/**
+* avp_copy:
+ * @param from the avp to be copied.
+ *
+ * Creates an avp whose name op and value are copies of the given one.
+ *
+ * Return value: a pointer to the newly created avp.
+ *
+ **/
+extern AVP* avp_copy(AVP* from) {
+ AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
+
+ new_avp_val->n = scs_subscribe(avp_strings, from->n);
+ new_avp_val->v = scs_subscribe(avp_strings, from->v);
+ new_avp_val->o = from->o;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avp,1,dbg_fp,"copy_avp: %X %s%c%s;",(guint32) new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
+#endif
+
+ return new_avp_val;
+}
+
+/**
+ * new_avpl:
+ * @param name the name the avpl will have.
+ *
+ * Creates an empty avpl.
+ *
+ * Return value: a pointer to the newly created avpl.
+ *
+ **/
+extern AVPL* new_avpl(const gchar* name) {
+ AVPL* new_avpl_p = (AVPL*)g_slice_new(any_avp_type);
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpl_p: %X name=%s",new_avpl_p,name);
+#endif
+
+ new_avpl_p->name = name ? scs_subscribe(avp_strings, name) : scs_subscribe(avp_strings, "");
+ new_avpl_p->len = 0;
+ new_avpl_p->null.avp = NULL;
+ new_avpl_p->null.next = &new_avpl_p->null;
+ new_avpl_p->null.prev = &new_avpl_p->null;
+
+
+ return new_avpl_p;
+}
+
+extern void rename_avpl(AVPL* avpl, gchar* name) {
+ scs_unsubscribe(avp_strings,avpl->name);
+ avpl->name = scs_subscribe(avp_strings,name);
+}
+
+/**
+ * insert_avp_before_node:
+ * @param avpl the avpl in which to insert.
+ * @param next_node the next node before which the new avpn has to be inserted.
+ * @param avp the avp to be inserted.
+ * @param copy_avp whether the original AVP or a copy thereof must be inserted.
+ *
+ * Pre-condition: the avp is sorted before before_avp and does not already exist
+ * in the avpl.
+ */
+static void insert_avp_before_node(AVPL* avpl, AVPN* next_node, AVP *avp, gboolean copy_avp) {
+ AVPN* new_avp_val = (AVPN*)g_slice_new(any_avp_type);
+
+ new_avp_val->avp = copy_avp ? avp_copy(avp) : avp;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %p",new_avp_val);
+ dbg_print(dbg_avpl,5,dbg_fp,"insert_avp: inserting %p in %p before %p;",avp,avpl,next_node);
+#endif
+
+ new_avp_val->next = next_node;
+ new_avp_val->prev = next_node->prev;
+ next_node->prev->next = new_avp_val;
+ next_node->prev = new_avp_val;
+
+ avpl->len++;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
+#endif
+}
+
+/**
+ * insert_avp:
+ * @param avpl the avpl in which to insert.
+ * @param avp the avp to be inserted.
+ *
+ * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
+ *
+ * Return value: whether it was inserted or not.
+ *
+ * BEWARE: Check the return value, you might need to delete the avp if
+ * it is not inserted.
+ **/
+extern gboolean insert_avp(AVPL* avpl, AVP* avp) {
+ AVPN* c;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,4,dbg_fp,"insert_avp: %X %X %s%c%s;",avpl,avp,avp->n,avp->o,avp->v);
+#endif
+
+ /* get to the insertion point */
+ for (c=avpl->null.next; c->avp; c = c->next) {
+ int name_diff = strcmp(avp->n, c->avp->n);
+
+ if (name_diff == 0) {
+ int value_diff = strcmp(avp->v, c->avp->v);
+
+ if (value_diff < 0) {
+ break;
+ }
+
+ if (value_diff == 0) {
+ // ignore duplicate values, prevents (a=1, a=1)
+ // note that this is also used to insert
+ // conditions AVPs, so really check if the name,
+ // value and operator are all equal.
+ if (c->avp->o == avp->o && avp->o == AVP_OP_EQUAL) {
+ return FALSE;
+ }
+ }
+ }
+
+ if (name_diff < 0) {
+ break;
+ }
+ }
+
+ insert_avp_before_node(avpl, c, avp, FALSE);
+
+ return TRUE;
+}
+
+/**
+ * get_avp_by_name:
+ * @param avpl the avpl from which to try to get the avp.
+ * @param name the name of the avp we are looking for.
+ * @param cookie variable in which to store the state between calls.
+ *
+ * Gets pointer to the next avp whose name is given; uses cookie to store its
+ * state between calls.
+ *
+ * Return value: a pointer to the next matching avp if there's one, else NULL.
+ *
+ **/
+extern AVP* get_avp_by_name(AVPL* avpl, gchar* name, void** cookie) {
+ AVPN* curr;
+ AVPN* start = (AVPN*) *cookie;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"get_avp_by_name: entering: %X %s %X",avpl,name,*cookie);
+#endif
+
+ name = scs_subscribe(avp_strings, name);
+
+ if (!start) start = avpl->null.next;
+
+ for ( curr = start; curr->avp; curr = curr->next ) {
+ if ( curr->avp->n == name ) {
+ break;
+ }
+ }
+
+ *cookie = curr;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %X",curr);
+#endif
+
+ scs_unsubscribe(avp_strings, name);
+
+ return curr->avp;
+}
+
+/**
+ * extract_avp_by_name:
+ * @param avpl the avpl from which to try to extract the avp.
+ * @param name the name of the avp we are looking for.
+ *
+ * Extracts from the avpl the next avp whose name is given;
+ *
+ * Return value: a pointer to extracted avp if there's one, else NULL.
+ *
+ **/
+extern AVP* extract_avp_by_name(AVPL* avpl, gchar* name) {
+ AVPN* curr;
+ AVP* avp = NULL;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %X %s",avpl,name);
+#endif
+
+ name = scs_subscribe(avp_strings, name);
+
+ for ( curr = avpl->null.next; curr->avp; curr = curr->next ) {
+ if ( curr->avp->n == name ) {
+ break;
+ }
+ }
+
+ scs_unsubscribe(avp_strings, name);
+
+ if( ! curr->avp ) return NULL;
+
+ curr->next->prev = curr->prev;
+ curr->prev->next = curr->next;
+
+ avp = curr->avp;
+
+ g_slice_free(any_avp_type,(any_avp_type*)curr);
+
+ (avpl->len)--;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
+#endif
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %X",avp);
+#endif
+
+ return avp;
+}
+
+
+/**
+ * extract_first_avp:
+ * @param avpl the avpl from which to try to extract the avp.
+ *
+ * Extracts the fisrt avp from the avpl.
+ *
+ * Return value: a pointer to extracted avp if there's one, else NULL.
+ *
+ **/
+extern AVP* extract_first_avp(AVPL* avpl) {
+ AVP* avp;
+ AVPN* node;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %X",avpl);
+#endif
+
+ node = avpl->null.next;
+
+ avpl->null.next->prev = &avpl->null;
+ avpl->null.next = node->next;
+
+ avp = node->avp;
+
+ if (avp) {
+ g_slice_free(any_avp_type,(any_avp_type*)node);
+ (avpl->len)--;
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
+#endif
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %X",avp);
+#endif
+
+ return avp;
+
+}
+
+
+/**
+ * extract_last_avp:
+ * @param avpl the avpl from which to try to extract the avp.
+ *
+ * Extracts the last avp from the avpl.
+ *
+ * Return value: a pointer to extracted avp if there's one, else NULL.
+ *
+ **/
+extern AVP* extract_last_avp(AVPL* avpl) {
+ AVP* avp;
+ AVPN* node;
+
+ node = avpl->null.prev;
+
+ avpl->null.prev->next = &avpl->null;
+ avpl->null.prev = node->prev;
+
+ avp = node->avp;
+
+ if (avp) {
+ g_slice_free(any_avp_type,(any_avp_type*)node);
+ (avpl->len)--;
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
+#endif
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",avp);
+#endif
+
+ return avp;
+
+}
+
+
+/**
+ * delete_avpl:
+ * @param avpl the avpl from which to try to extract the avp.
+ * @param avps_too whether or not it should delete the avps as well.
+ *
+ * Destroys an avpl and releases the resources it uses. If told to do
+ * so releases the avps as well.
+ *
+ **/
+extern void delete_avpl(AVPL* avpl, gboolean avps_too) {
+ AVP* avp;
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %X",avpl);
+#endif
+
+ while(( avp = extract_last_avp(avpl))) {
+ if (avps_too) {
+ delete_avp(avp);
+ }
+ }
+
+ scs_unsubscribe(avp_strings,avpl->name);
+ g_slice_free(any_avp_type,(any_avp_type*)avpl);
+}
+
+
+
+/**
+ * get_next_avp:
+ * @param avpl the avpl from which to try to get the avps.
+ * @param cookie variable in which to store the state between calls.
+ *
+ * Iterates on an avpl to get its avps.
+ *
+ * Return value: a pointer to the next avp if there's one, else NULL.
+ *
+ **/
+extern AVP* get_next_avp(AVPL* avpl, void** cookie) {
+ AVPN* node;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %X avpn: %X",avpl,*cookie);
+#endif
+
+ if (*cookie) {
+ node = (AVPN*) *cookie;
+ } else {
+ node = avpl->null.next;
+ }
+
+ *cookie = node->next;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",node->avp);
+#endif
+
+ return node->avp;
+}
+
+/**
+ * avpl_to_str:
+ * @param avpl the avpl to represent.
+ *
+ * Creates a newly allocated string containing a representation of an avpl.
+ *
+ * Return value: a pointer to the newly allocated string.
+ *
+ **/
+gchar* avpl_to_str(AVPL* avpl) {
+ AVPN* c;
+ GString* s = g_string_new("");
+ gchar* avp_s;
+ gchar* r;
+
+ for(c=avpl->null.next; c->avp; c = c->next) {
+ avp_s = avp_to_str(c->avp);
+ g_string_append_printf(s," %s;",avp_s);
+ g_free(avp_s);
+ }
+
+ r = g_string_free(s,FALSE);
+
+ /* g_strchug(r); ? */
+ return r;
+}
+
+extern gchar* avpl_to_dotstr(AVPL* avpl) {
+ AVPN* c;
+ GString* s = g_string_new("");
+ gchar* avp_s;
+ gchar* r;
+
+ for(c=avpl->null.next; c->avp; c = c->next) {
+ avp_s = avp_to_str(c->avp);
+ g_string_append_printf(s," .%s;",avp_s);
+ g_free(avp_s);
+ }
+
+ r = g_string_free(s,FALSE);
+
+ /* g_strchug(r); ? */
+ return r;
+}
+
+/**
+* merge_avpl:
+ * @param dst the avpl in which to merge the avps.
+ * @param src the avpl from which to get the avps.
+ * @param copy_avps whether avps should be copied instead of referenced.
+ *
+ * Adds the avps of src that are not existent in dst into dst.
+ *
+ **/
+extern void merge_avpl(AVPL* dst, AVPL* src, gboolean copy_avps) {
+ AVPN* cd = NULL;
+ AVPN* cs = NULL;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %X %X",dst,src);
+#endif
+
+ cs = src->null.next;
+ cd = dst->null.next;
+
+ while (cs->avp && cd->avp) {
+
+ int name_diff = strcmp(cd->avp->n, cs->avp->n);
+
+ if (name_diff < 0) {
+ // dest < source, advance dest to find a better place to insert
+ cd = cd->next;
+ } else if (name_diff > 0) {
+ // dest > source, so it can be definitely inserted here.
+ insert_avp_before_node(dst, cd, cs->avp, copy_avps);
+ cs = cs->next;
+ } else {
+ // attribute names are equal. Ignore duplicate values but ensure that other values are sorted.
+ int value_diff = strcmp(cd->avp->v, cs->avp->v);
+
+ if (value_diff < 0) {
+ // dest < source, do not insert it yet
+ cd = cd->next;
+ } else if (value_diff > 0) {
+ // dest > source, insert AVP before the current dest AVP
+ insert_avp_before_node(dst, cd, cs->avp, copy_avps);
+ cs = cs->next;
+ } else {
+ // identical AVPs, do not create a duplicate.
+ cs = cs->next;
+ }
+ }
+ }
+
+ // if there are remaing source AVPs while there are no more destination
+ // AVPs (cd now represents the NULL item, after the last item), append
+ // all remaining source AVPs to the end
+ while (cs->avp) {
+ insert_avp_before_node(dst, cd, cs->avp, copy_avps);
+ cs = cs->next;
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
+#endif
+
+ return;
+}
+
+
+/**
+ * new_avpl_from_avpl:
+ * @param name the name of the new avpl.
+ * @param avpl the avpl from which to get the avps.
+ * @param copy_avps whether avps should be copied instead of referenced.
+ *
+ * Creates a new avpl containing the same avps as the given avpl
+ * It will either reference or copie the avps.
+ *
+ * Return value: a pointer to the newly allocated string.
+ *
+ **/
+extern AVPL* new_avpl_from_avpl(const gchar* name, AVPL* avpl, gboolean copy_avps) {
+ AVPL* newavpl = new_avpl(name);
+ void* cookie = NULL;
+ AVP* avp;
+ AVP* copy;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_from_avpl: %X from=%X name='%s'",newavpl,avpl,name);
+#endif
+
+ while(( avp = get_next_avp(avpl,&cookie) )) {
+ if (copy_avps) {
+ copy = avp_copy(avp);
+ if ( ! insert_avp(newavpl,copy) ) {
+ delete_avp(copy);
+ }
+ } else {
+ insert_avp(newavpl,avp);
+ }
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done");
+#endif
+
+ return newavpl;
+}
+
+/**
+* match_avp:
+ * @param src an src to be compared agains an "op" avp
+ * @param op the "op" avp that will be matched against the src avp
+ *
+ * Checks whether or not two avp's match.
+ *
+ * Return value: a pointer to the src avp if there's a match.
+ *
+ **/
+extern AVP* match_avp(AVP* src, AVP* op) {
+ gchar** splited;
+ int i;
+ gchar* p;
+ guint ls;
+ guint lo;
+ float fs = 0.0f;
+ float fo = 0.0f;
+ gboolean lower = FALSE;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"match_avp: %s%c%s; vs. %s%c%s;",src->n,src->o,src->v,op->n,op->o,op->v);
+#endif
+
+ if ( src->n != op->n ) {
+ return NULL;
+ }
+
+ switch (op->o) {
+ case AVP_OP_EXISTS:
+ return src;
+ case AVP_OP_EQUAL:
+ return src->v == op->v ? src : NULL;
+ case AVP_OP_NOTEQUAL:
+ return !( src->v == op->v) ? src : NULL;
+ case AVP_OP_STARTS:
+ return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL;
+ case AVP_OP_ONEOFF:
+ splited = g_strsplit(op->v,"|",0);
+ if (splited) {
+ for (i=0;splited[i];i++) {
+ if(g_str_equal(splited[i],src->v)) {
+ g_strfreev(splited);
+ return src;
+ }
+ }
+ g_strfreev(splited);
+ }
+ return NULL;
+
+ case AVP_OP_LOWER:
+ lower = TRUE;
+ /* FALLTHRU */
+ case AVP_OP_HIGHER:
+
+ fs = (float) g_ascii_strtod(src->v, NULL);
+ fo = (float) g_ascii_strtod(op->v, NULL);
+
+ if (lower) {
+ if (fs<fo) return src;
+ else return NULL;
+ } else {
+ if (fs>fo) return src;
+ else return NULL;
+ }
+ case AVP_OP_ENDS:
+ /* does this work? */
+ ls = (guint) strlen(src->v);
+ lo = (guint) strlen(op->v);
+
+ if ( ls < lo ) {
+ return NULL;
+ } else {
+ p = src->v + ( ls - lo );
+ return g_str_equal(p,op->v) ? src : NULL;
+ }
+
+ /* case AVP_OP_TRANSF: */
+ /* return do_transform(src,op); */
+ case AVP_OP_CONTAINS:
+ return g_strrstr(src->v, op->v) ? src : NULL;;
+ }
+ /* will never get here */
+ return NULL;
+}
+
+
+
+/**
+ * new_avpl_loose_match:
+ * @param name the name of the resulting avpl
+ * @param src the data AVPL to be matched against a condition AVPL
+ * @param op the conditions AVPL that will be matched against the data AVPL
+ * @param copy_avps whether the avps in the resulting avpl should be copied
+ *
+ * Creates a new AVP list containing all data AVPs that matched any of the
+ * conditions AVPs. If there are no matches, an empty list will be returned.
+ *
+ * Note: Loose will always be considered a successful match, it matches zero or
+ * more conditions.
+ */
+extern AVPL* new_avpl_loose_match(const gchar* name,
+ AVPL* src,
+ AVPL* op,
+ gboolean copy_avps) {
+
+ AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
+ AVPN* co = NULL;
+ AVPN* cs = NULL;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_loose_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
+#endif
+
+
+ cs = src->null.next;
+ co = op->null.next;
+ while (cs->avp && co->avp) {
+ int name_diff = strcmp(co->avp->n, cs->avp->n);
+
+ if (name_diff < 0) {
+ // op < source, op is not matching
+ co = co->next;
+ } else if (name_diff > 0) {
+ // op > source, source is not matching
+ cs = cs->next;
+ } else {
+ // attribute match found, let's see if there is any condition (op) that accepts this data AVP.
+ AVPN *cond = co;
+ do {
+ if (match_avp(cs->avp, cond->avp)) {
+ insert_avp_before_node(newavpl, newavpl->null.prev, cs->avp, copy_avps);
+ break;
+ }
+ cond = cond->next;
+ } while (cond->avp && cond->avp->n == cs->avp->n);
+ cs = cs->next;
+ }
+ }
+
+ // return matches (possible none)
+ return newavpl;
+}
+
+/**
+* new_avpl_pairs_match:
+ * @param name the name of the resulting avpl
+ * @param src the data AVPL to be matched against a condition AVPL
+ * @param op the conditions AVPL that will be matched against the data AVPL
+ * @param strict TRUE if every condition must have a matching data AVP, FALSE if
+ * it is also acceptable that only one of the condition AVPs for the same
+ * attribute is matching.
+ * @param copy_avps whether the avps in the resulting avpl should be copied
+ *
+ * Creates an AVP list by matching pairs of conditions and data AVPs, returning
+ * the data AVPs. If strict is TRUE, then each condition must be paired with a
+ * matching data AVP. If strict is FALSE, then some conditions are allowed to
+ * fail when other conditions for the same attribute do have a match. Note that
+ * if the condition AVPL is empty, the result will be a match (an empty list).
+ *
+ * Return value: a pointer to the newly created avpl containing the
+ * matching avps or NULL if there is no match.
+ */
+extern AVPL* new_avpl_pairs_match(const gchar* name, AVPL* src, AVPL* op, gboolean strict, gboolean copy_avps) {
+ AVPL* newavpl;
+ AVPN* co = NULL;
+ AVPN* cs = NULL;
+ const gchar *last_match = NULL;
+ gboolean matched = TRUE;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"%s: %p src=%p op=%p name='%s'",G_STRFUNC,newavpl,src,op,name);
+#endif
+
+ newavpl = new_avpl(scs_subscribe(avp_strings, name));
+
+ cs = src->null.next;
+ co = op->null.next;
+ while (cs->avp && co->avp) {
+ int name_diff = strcmp(co->avp->n, cs->avp->n);
+ const gchar *failed_match = NULL;
+
+ if (name_diff < 0) {
+ // op < source, op has no data avp with same attribute.
+ failed_match = co->avp->n;
+ co = co->next;
+ } else if (name_diff > 0) {
+ // op > source, the source avp is not matched by any condition
+ cs = cs->next;
+ } else {
+ // Matching attributes found, now try to find a matching data AVP for the condition.
+ if (match_avp(cs->avp, co->avp)) {
+ insert_avp_before_node(newavpl, newavpl->null.prev, cs->avp, copy_avps);
+ last_match = co->avp->n;
+ cs = cs->next;
+ } else {
+ failed_match = co->avp->n;
+ }
+ co = co->next;
+ }
+
+ // condition did not match, check if we can continue matching.
+ if (failed_match) {
+ if (strict) {
+ matched = FALSE;
+ break;
+ } else if (last_match != failed_match) {
+ // None of the conditions so far matched the attribute, check for other candidates
+ if (!co->avp || co->avp->n != last_match) {
+ matched = FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ // if there are any conditions remaining, then those could not be matched
+ if (matched && strict && co->avp) {
+ matched = FALSE;
+ }
+
+ if (matched) {
+ // there was a match, accept it
+ return newavpl;
+ } else {
+ // no match, only delete AVPs too if they were copied
+ delete_avpl(newavpl, copy_avps);
+ return NULL;
+ }
+}
+
+
+/**
+ * new_avpl_from_match:
+ * @param mode The matching method, one of AVPL_STRICT, AVPL_LOOSE, AVPL_EVERY.
+ * @param name the name of the resulting avpl
+ * @param src the data AVPL to be matched agains a condition AVPL
+ * @param op the conditions AVPL that will be matched against the data AVPL
+ *
+ * Matches the conditions AVPL against the original AVPL according to the mode.
+ * If there is no match, NULL is returned. If there is actually a match, then
+ * the matching AVPs (a subset of the data) are returned.
+ */
+extern AVPL* new_avpl_from_match(avpl_match_mode mode, const gchar* name,AVPL* src, AVPL* op, gboolean copy_avps) {
+ AVPL* avpl = NULL;
+
+ switch (mode) {
+ case AVPL_STRICT:
+ avpl = new_avpl_pairs_match(name, src, op, TRUE, copy_avps);
+ break;
+ case AVPL_LOOSE:
+ avpl = new_avpl_loose_match(name,src,op,copy_avps);
+ break;
+ case AVPL_EVERY:
+ avpl = new_avpl_pairs_match(name, src, op, FALSE, copy_avps);
+ break;
+ case AVPL_NO_MATCH:
+ // XXX this seems unused
+ avpl = new_avpl_from_avpl(name,src,copy_avps);
+ merge_avpl(avpl, op, copy_avps);
+ break;
+ }
+
+ return avpl;
+}
+
+/**
+ * delete_avpl_transform:
+ * @param op a pointer to the avpl transformation object
+ *
+ * Destroys an avpl transformation object and releases all the resources it
+ * uses.
+ *
+ **/
+extern void delete_avpl_transform(AVPL_Transf* op) {
+ AVPL_Transf* next;
+
+ for (; op ; op = next) {
+ next = op->next;
+
+ g_free(op->name);
+
+ if (op->match) {
+ delete_avpl(op->match,TRUE);
+ }
+
+ if (op->replace) {
+ delete_avpl(op->replace,TRUE);
+ }
+
+ g_free(op);
+ }
+
+}
+
+
+/**
+ * avpl_transform:
+ * @param src the source avpl for the transform operation.
+ * @param op a pointer to the avpl transformation object to apply.
+ *
+ * Applies the "op" transformation to an avpl, matches it and eventually
+ * replaces or inserts the transformed avps.
+ *
+ * Return value: whether the transformation was performed or not.
+ **/
+extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
+ AVPL* avpl = NULL;
+ AVPN* cs;
+ AVPN* cm;
+ AVPN* n;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%X op=%X",src,op);
+#endif
+
+ for ( ; op ; op = op->next) {
+
+ avpl = new_avpl_from_match(op->match_mode, src->name,src, op->match, TRUE);
+
+ if (avpl) {
+ switch (op->replace_mode) {
+ case AVPL_NO_REPLACE:
+ delete_avpl(avpl,TRUE);
+ return;
+ case AVPL_INSERT:
+ merge_avpl(src,op->replace,TRUE);
+ delete_avpl(avpl,TRUE);
+ return;
+ case AVPL_REPLACE:
+ cs = src->null.next;
+ cm = avpl->null.next;
+ // Removes AVPs from the source which are in the matched data.
+ // Assume that the matched set is a subset of the source.
+ while (cs->avp && cm->avp) {
+ if (cs->avp->n == cm->avp->n && cs->avp->v == cm->avp->v) {
+ n = cs->next;
+
+ cs->prev->next = cs->next;
+ cs->next->prev = cs->prev;
+ g_slice_free(any_avp_type,(any_avp_type*)cs);
+
+ cs = n;
+ cm = cm->next;
+ } else {
+ // Current matched AVP is not equal to the current
+ // source AVP. Since there must be a source AVP for
+ // each matched AVP, advance current source and not
+ // the match AVP.
+ cs = cs->next;
+ }
+ }
+
+ merge_avpl(src,op->replace,TRUE);
+ delete_avpl(avpl,TRUE);
+ return;
+ }
+ }
+ }
+}
+
+
+/**
+ * new_loal:
+ * @param name the name the loal will take.
+ *
+ * Creates an empty list of avp lists.
+ *
+ * Return value: a pointer to the newly created loal.
+ **/
+extern LoAL* new_loal(const gchar* name) {
+ LoAL* new_loal_p = (LoAL*)g_slice_new(any_avp_type);
+
+ if (! name) {
+ name = "anonymous";
+ }
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_p: %X name=%s",new_loal_p,name);
+#endif
+
+ new_loal_p->name = scs_subscribe(avp_strings,name);
+ new_loal_p->null.avpl = NULL;
+ new_loal_p->null.next = &new_loal_p->null;
+ new_loal_p->null.prev = &new_loal_p->null;
+ new_loal_p->len = 0;
+ return new_loal_p;
+}
+
+/**
+ * loal_append:
+ * @param loal the loal on which to operate.
+ * @param avpl the avpl to append.
+ *
+ * Appends an avpl to a loal.
+ *
+ **/
+extern void loal_append(LoAL* loal, AVPL* avpl) {
+ LoALnode* node = (LoALnode*)g_slice_new(any_avp_type);
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %X",node);
+#endif
+
+ node->avpl = avpl;
+ node->next = &loal->null;
+ node->prev = loal->null.prev;
+
+ loal->null.prev->next = node;
+ loal->null.prev = node;
+ loal->len++;
+}
+
+
+/**
+ * extract_first_avpl:
+ * @param loal the loal on which to operate.
+ *
+ * Extracts the first avpl contained in a loal.
+ *
+ * Return value: a pointer to the extracted avpl.
+ *
+ **/
+extern AVPL* extract_first_avpl(LoAL* loal) {
+ LoALnode* node;
+ AVPL* avpl;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name);
+#endif
+
+ node = loal->null.next;
+
+ loal->null.next->next->prev = &loal->null;
+ loal->null.next = node->next;
+
+ loal->len--;
+
+ avpl = node->avpl;
+
+ if ( avpl ) {
+ g_slice_free(any_avp_type,(any_avp_type*)node);
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name);
+ dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
+#endif
+ }
+
+ return avpl;
+}
+
+/**
+* extract_first_avpl:
+ * @param loal the loal on which to operate.
+ *
+ * Extracts the last avpl contained in a loal.
+ *
+ * Return value: a pointer to the extracted avpl.
+ *
+ **/
+extern AVPL* extract_last_avpl(LoAL* loal){
+ LoALnode* node;
+ AVPL* avpl;
+
+ node = loal->null.prev;
+
+ loal->null.prev->prev->next = &loal->null;
+ loal->null.prev = node->prev;
+
+ loal->len--;
+
+ avpl = node->avpl;
+
+ if ( avpl ) {
+ g_slice_free(any_avp_type,(any_avp_type*)node);
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
+#endif
+ }
+
+ return avpl;
+}
+
+/**
+ * extract_first_avpl:
+ * @param loal the loal on which to operate.
+ * @param cookie pointer to the pointer variable to contain the state between calls
+ *
+ * At each call will return the following avpl from a loal. The given cookie
+ * will be used to manatain the state between calls.
+ *
+ * Return value: a pointer to the next avpl.
+ *
+ **/
+extern AVPL* get_next_avpl(LoAL* loal,void** cookie) {
+ LoALnode* node;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%X node=%X",loal,*cookie);
+#endif
+
+ if (*cookie) {
+ node = (LoALnode*) *cookie;
+ } else {
+ node = loal->null.next;
+ }
+
+ *cookie = node->next;
+
+ return node->avpl;
+}
+
+/**
+ * delete_loal:
+ * @param loal the loal to be deleted.
+ * @param avpls_too whether avpls contained by the loal should be deleted as well
+ * @param avps_too whether avps contained by the avpls should be also deleted
+ *
+ * Destroys a loal and eventually desstroys avpls and avps.
+ *
+ **/
+extern void delete_loal(LoAL* loal, gboolean avpls_too, gboolean avps_too) {
+ AVPL* avpl;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %X",loal);
+#endif
+
+ while(( avpl = extract_last_avpl(loal) )) {
+ if (avpls_too) {
+ delete_avpl(avpl,avps_too);
+ }
+ }
+
+ scs_unsubscribe(avp_strings,loal->name);
+ g_slice_free(any_avp_type,(any_avp_type*)loal);
+}
+
+
+
+/****************************************************************************
+ ******************* the following are used in load_loal_from_file
+ ****************************************************************************/
+
+/**
+ * load_loal_error:
+ * Used by loal_from_file to handle errors while loading.
+ **/
+static LoAL* load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, const gchar* fmt, ...) {
+ va_list list;
+ gchar* desc;
+ LoAL* ret = NULL;
+ gchar* err;
+
+ va_start( list, fmt );
+ desc = g_strdup_vprintf(fmt, list);
+ va_end( list );
+
+ if (loal) {
+ err = g_strdup_printf("Error Loading LoAL from file: in %s at line: %i, %s",loal->name,linenum,desc);
+ } else {
+ err = g_strdup_printf("Error Loading LoAL at line: %i, %s",linenum,desc);
+ }
+ ret = new_loal(err);
+
+ g_free(desc);
+ g_free(err);
+
+ if (fp) fclose(fp);
+ if (loal) delete_loal(loal,TRUE,TRUE);
+ if (curr) delete_avpl(curr,TRUE);
+
+ return ret;
+}
+
+
+/* the maximum length allowed for a line */
+#define MAX_ITEM_LEN 8192
+
+/* this two ugly things are used for tokenizing */
+#define AVP_OP_CHAR '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
+
+#define AVP_NAME_CHAR 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
+case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
+case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
+case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
+case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
+case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
+case '7': case '8': case '9': case '.'
+
+
+/**
+ * loal_from_file:
+ * @param filename the file containing a loals text representation.
+ *
+ * Given a filename it will attempt to load a loal containing a copy of
+ * the avpls represented in the file.
+ *
+ * Return value: if successful a pointer to the new populated loal, else NULL.
+ *
+ **/
+extern LoAL* loal_from_file(gchar* filename) {
+ FILE *fp = NULL;
+ gchar c;
+ int i = 0;
+ guint32 linenum = 1;
+ gchar *linenum_buf;
+ gchar *name;
+ gchar *value;
+ gchar op = '?';
+ LoAL *loal_error, *loal = new_loal(filename);
+ AVPL* curr = NULL;
+ AVP* avp;
+
+ enum _load_loal_states {
+ START,
+ BEFORE_NAME,
+ IN_NAME,
+ IN_VALUE,
+ MY_IGNORE
+ } state;
+
+ linenum_buf = (gchar*)g_malloc(MAX_ITEM_LEN);
+ name = (gchar*)g_malloc(MAX_ITEM_LEN);
+ value = (gchar*)g_malloc(MAX_ITEM_LEN);
+#ifndef _WIN32
+ if (! getuid()) {
+ loal_error = load_loal_error(fp,loal,curr,linenum,"MATE Will not run as root");
+ goto error;
+ }
+#endif
+
+ state = START;
+
+ if (( fp = ws_fopen(filename,"r") )) {
+ while(( c = (gchar) fgetc(fp) )){
+
+ if ( feof(fp) ) {
+ if ( ferror(fp) ) {
+ report_read_failure(filename,errno);
+ loal_error = load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename);
+ goto error;
+ }
+ break;
+ }
+
+ if ( c == '\n' ) {
+ linenum++;
+ }
+
+ if ( i >= MAX_ITEM_LEN - 1 ) {
+ loal_error = load_loal_error(fp,loal,curr,linenum,"Maximum item length exceeded");
+ goto error;
+ }
+
+ switch(state) {
+ case MY_IGNORE:
+ switch (c) {
+ case '\n':
+ state = START;
+ i = 0;
+ continue;
+ default:
+ continue;
+ }
+ case START:
+ switch (c) {
+ case ' ': case '\t':
+ /* ignore whitespace at line start */
+ continue;
+ case '\n':
+ /* ignore empty lines */
+ i = 0;
+ continue;
+ case AVP_NAME_CHAR:
+ state = IN_NAME;
+ i = 0;
+ name[i++] = c;
+ name[i] = '\0';
+ g_snprintf(linenum_buf,MAX_ITEM_LEN,"%s:%u",filename,linenum);
+ curr = new_avpl(linenum_buf);
+ continue;
+ case '#':
+ state = MY_IGNORE;
+ continue;
+ default:
+ loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
+ goto error;
+ }
+ case BEFORE_NAME:
+ i = 0;
+ name[0] = '\0';
+ switch (c) {
+ case '\\':
+ c = (gchar) fgetc(fp);
+ if (c != '\n') ungetc(c,fp);
+ continue;
+ case ' ':
+ case '\t':
+ continue;
+ case AVP_NAME_CHAR:
+ state = IN_NAME;
+
+ name[i++] = c;
+ name[i] = '\0';
+ continue;
+ case '\n':
+ loal_append(loal,curr);
+ state = START;
+ continue;
+ default:
+ loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
+ goto error;
+ }
+ case IN_NAME:
+ switch (c) {
+ case ';':
+ state = BEFORE_NAME;
+
+ op = '?';
+ name[i] = '\0';
+ value[0] = '\0';
+ i = 0;
+
+ avp = new_avp(name,value,op);
+
+ if (! insert_avp(curr,avp) ) {
+ delete_avp(avp);
+ }
+
+ continue;
+ case AVP_OP_CHAR:
+ name[i] = '\0';
+ i = 0;
+ op = c;
+ state = IN_VALUE;
+ continue;
+ case AVP_NAME_CHAR:
+ name[i++] = c;
+ continue;
+ case '\n':
+ loal_error = load_loal_error(fp,loal,curr,linenum,"operator expected found new line");
+ goto error;
+ default:
+ loal_error = load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c);
+ goto error;
+ }
+ case IN_VALUE:
+ switch (c) {
+ case '\\':
+ value[i++] = (gchar) fgetc(fp);
+ continue;
+ case ';':
+ state = BEFORE_NAME;
+
+ value[i] = '\0';
+ i = 0;
+
+ avp = new_avp(name,value,op);
+
+ if (! insert_avp(curr,avp) ) {
+ delete_avp(avp);
+ }
+ continue;
+ case '\n':
+ loal_error = load_loal_error(fp,loal,curr,linenum,"';' expected found new line");
+ goto error;
+ default:
+ value[i++] = c;
+ continue;
+ }
+ }
+ }
+ fclose (fp);
+
+ g_free(linenum_buf);
+ g_free(name);
+ g_free(value);
+
+ return loal;
+
+ } else {
+ report_open_failure(filename,errno,FALSE);
+ loal_error = load_loal_error(NULL,loal,NULL,0,"Cannot Open file '%s'",filename);
+ }
+
+error:
+ g_free(linenum_buf);
+ g_free(name);
+ g_free(value);
+
+ return loal_error;
+}
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */