2 * MATE -- Meta Analysis Tracing Engine
3 * Utility Library: Single Copy Strings and Attribute Value Pairs
5 * Copyright 2004, Luis E. Garcia Ontanon <luis.ontanon@gmail.com>
9 * Ethereal - Network traffic analyzer
10 * By Gerald Combs <gerald@ethereal.com>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 #include "mate_util.h"
36 + fix debug_print levels
37 + chunks for scs_strings (do I realy need em??)
38 + checking bounds in (almost) every operator
40 + rethink '&' operator
41 - bounds check on op_match()
43 ? add {xxx} to get avps named xxxx from the src avpl
44 ? add (yyy) to do aaa+31 or and similar stuff
45 - add perlre operator?
51 /***************************************************************************
53 ***************************************************************************
54 * This is the debug facility of the thing.
55 ***************************************************************************/
58 * which: a pointer to the current level of debugging for a feature
59 * how: the level over which this message should be printed out
60 * where: the file on which to print (g_message if null)
61 * fmt, ...: what to print
64 void dbg_print(const guint* which, guint how, FILE* where, guint8* fmt, ... ) {
65 static guint8 debug_buffer[DEBUG_BUFFER_SIZE];
68 if ( ! which || *which < how ) return;
70 va_start( list, fmt );
71 g_vsnprintf(debug_buffer,DEBUG_BUFFER_SIZE,fmt,list);
75 g_message(debug_buffer);
77 fputs(debug_buffer,where);
83 /***************************************************************************
85 ***************************************************************************
86 * In order to save memory and since strings repeat more often than don't,
87 * we'll keep only one copy of each as key to a hash with a count of
88 * subscribers as value.
89 ***************************************************************************/
90 /* FIXME: use hash fom glib 1.X not 2.X */
94 * @collection: the scs hash
96 * Initializes the scs hash.
99 void scs_init(GHashTable** hash) {
101 value: guint number of subscribers */
103 if (*hash) g_hash_table_destroy(*hash);
105 *hash = g_hash_table_new_full(g_str_hash,g_str_equal,g_free,g_free);
112 * @collection: the scs hash
115 * Checks if the given string exists already and if so it increases the count of
116 * subsscribers and returns a pointer to the stored string. If not It will copy
117 * the given string store it in the hash and return the pointer to the copy.
118 * Remember, containment is handled internally, take care of your own strings.
120 * Return value: a pointer to the subscribed string.
122 guint8* scs_subscribe(GHashTable* collection, guint8* s) {
126 g_hash_table_lookup_extended(collection,s,(gpointer*)&ok,(gpointer*)&ip);
132 ip = g_malloc(sizeof(int));
135 g_hash_table_insert(collection,s,ip);
143 * @collection: the scs hash
146 * decreases the count of subscribers, if zero frees the internal copy of
149 void scs_unsubscribe(GHashTable* collection, guint8* s) {
153 g_hash_table_lookup_extended(collection,s,(gpointer*)&ok,(gpointer*)&ip);
156 if (*ip == 0) g_hash_table_remove(collection,ok);
159 g_warning("unsusbcribe: already deleted: '%s'?",s);
164 * scs_subscribe_printf:
165 * @fmt: a format string ...
167 * Formats the input and subscribes it.
169 * Return value: the stored copy of the formated string.
172 extern guint8* scs_subscribe_printf(GHashTable* collection, guint8* fmt, ...) {
178 va_start( list, fmt );
179 s = g_strdup_vprintf(fmt, list);
182 g_hash_table_lookup_extended(collection,s,(gpointer*)&ok,(gpointer*)&ip);
189 ip = g_malloc0(sizeof(int));
191 g_hash_table_insert(collection,s,ip);
198 /***************************************************************************
200 ***************************************************************************
201 * The Thing operates mainly on avps, avpls and loals
202 * - attribute value pairs (two strings: the name and the value and an opeartor)
203 * - avp lists a somehow sorted list of avps
204 * - loal (list of avp lists) an arbitrarily sorted list of avpls
207 ***************************************************************************/
210 typedef union _any_avp_type {
219 static GMemChunk* avp_chunk = NULL;
220 static GHashTable* avp_strings = NULL;
222 #ifdef _AVP_DEBUGGING
223 static FILE* dbg_fp = NULL;
225 static int dbg_level = 0;
226 static int* dbg = &dbg_level;
228 static int dbg_avp_level = 0;
229 static int* dbg_avp = &dbg_avp_level;
231 static int dbg_avp_op_level = 0;
232 static int* dbg_avp_op = &dbg_avp_op_level;
234 static int dbg_avpl_level = 0;
235 static int* dbg_avpl = &dbg_avpl_level;
237 static int dbg_avpl_op_level = 0;
238 static int* dbg_avpl_op = &dbg_avpl_op_level;
242 * @fp: the file in which to send debugging output.
243 * @general: a pointer to the level of debugging of facility "general"
244 * @avp: a pointer to the level of debugging of facility "avp"
245 * @avp_op: a pointer to the level of debugging of facility "avp_op"
246 * @avpl: a pointer to the level of debugging of facility "avpl"
247 * @avpl_op: a pointer to the level of debugging of facility "avpl_op"
249 * (If enabled set's up the debug facilities for the avp library.
252 extern void setup_avp_debug(FILE* fp, int* general, int* avp, int* avp_op, int* avpl, int* avpl_op) {
258 dbg_avpl_op = avpl_op;
261 #endif /* _AVP_DEBUGGING */
265 * @chunk_size: the initial chunk's size.
267 * (Re)Initializes the avp library.
270 extern void avp_init(void) {
272 scs_init(&avp_strings);
276 g_mem_chunk_destroy(avp_chunk);
279 avp_chunk = g_mem_chunk_new("avp_chunk", sizeof(any_avp_type),
280 AVP_CHUNK_SIZE, G_ALLOC_AND_FREE);
286 * new_avp_from_finfo:
287 * @name: the name the avp will have.
288 * @finfo: the field_info from which to fetch the data.
290 * Creates an avp from a field_info record.
292 * Return value: a pointer to the newly created avp.
295 extern AVP* new_avp_from_finfo(guint8* name, field_info* finfo) {
296 AVP* new = g_mem_chunk_alloc(avp_chunk);
299 new->n = scs_subscribe(avp_strings, name);
301 if (finfo->value.ftype->get_value_integer) {
302 value = scs_subscribe_printf(avp_strings, "%i",fvalue_get_integer(&finfo->value));
303 #ifdef _AVP_DEBUGGING
304 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: from integer: %s",value);
306 } else if (finfo->value.ftype->val_to_string_repr) {
307 str = fvalue_to_string_repr(&finfo->value,FTREPR_DISPLAY,NULL);
308 value = scs_subscribe(avp_strings, str);
309 #ifdef _AVP_DEBUGGING
310 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: from string: %s",value);
312 } else if (finfo->value.ftype->get_value_floating) {
313 value = scs_subscribe_printf(avp_strings, "%f",fvalue_get_floating(&finfo->value));
314 #ifdef _AVP_DEBUGGING
315 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: from float: %s",value);
318 #ifdef _AVP_DEBUGGING
319 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: a proto: %s",finfo->hfinfo->abbrev);
321 value = scs_subscribe(avp_strings, finfo->hfinfo->abbrev);
328 #ifdef _AVP_DEBUGGING
329 dbg_print (dbg_avp,1,dbg_fp,"new_avp_from_finfo: %X %s%c%s;",(guint32) new,new->n,new->o,new->v);
338 * @name: the name the avp will have.
339 * @value: the value the avp will have.
340 * @o: the operator of this avp.
342 * Creates an avp given every parameter.
344 * Return value: a pointer to the newly created avp.
347 extern AVP* new_avp(guint8* name, guint8* value, guint8 o) {
348 AVP* new = g_mem_chunk_alloc(avp_chunk);
350 new->n = scs_subscribe(avp_strings, name);
351 new->v = scs_subscribe(avp_strings, value);
354 #ifdef _AVP_DEBUGGING
355 dbg_print(dbg_avp,1,dbg_fp,"new_avp: %X %s%c%s;",(guint32) new,new->n,new->o,new->v);
363 * @avp: the avp to delete.
365 * Destroys an avp and releases the resources it uses.
368 extern void delete_avp(AVP* avp) {
369 #ifdef _AVP_DEBUGGING
370 dbg_print(dbg_avp,1,dbg_fp,"delete_avp: %X %s%c%s;",(guint32) avp,avp->n,avp->o,avp->v);
373 scs_unsubscribe(avp_strings, avp->n);
374 scs_unsubscribe(avp_strings, avp->v);
375 g_mem_chunk_free(avp_chunk,avp);
381 * @from: the avp to be copied.
383 * Creates an avp whose name op and value are copyes of the given one.
385 * Return value: a pointer to the newly created avp.
388 extern AVP* avp_copy(AVP* from) {
389 AVP* new = g_mem_chunk_alloc(avp_chunk);
391 new->n = scs_subscribe(avp_strings, from->n);
392 new->v = scs_subscribe(avp_strings, from->v);
395 #ifdef _AVP_DEBUGGING
396 dbg_print(dbg_avp,1,dbg_fp,"copy_avp: %X %s%c%s;",(guint32) new,new->n,new->o,new->v);
403 extern void rename_avp(AVP* avp, guint8* name) {
405 avp->n = scs_subscribe(avp_strings,name);
406 scs_unsubscribe(avp_strings,s);
411 * @name: the name the avpl will have.
413 * Creates an empty avpl.
415 * Return value: a pointer to the newly created avpl.
418 extern AVPL* new_avpl(guint8* name) {
419 AVPL* new_avpl = g_mem_chunk_alloc(avp_chunk);
421 #ifdef _AVP_DEBUGGING
422 dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpl: %X name=%s",new_avpl,name);
425 new_avpl->name = scs_subscribe(avp_strings, name);
427 new_avpl->null.avp = NULL;
428 new_avpl->null.next = &new_avpl->null;
429 new_avpl->null.prev = &new_avpl->null;
435 extern void rename_avpl(AVPL* avpl, guint8* name) {
436 scs_unsubscribe(avp_strings,avpl->name);
437 avpl->name = scs_subscribe(avp_strings,name);
442 * @avpl: the avpl in which to insert.
443 * @avp: the avp to be inserted.
445 * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
447 * Return value: whether it was inserted or not.
449 * BEWARE: Check the return value, you might need to delete the avp if
450 * it is not inserted.
452 extern gboolean insert_avp(AVPL* avpl, AVP* avp) {
453 AVPN* new = g_mem_chunk_alloc(avp_chunk);
458 #ifdef _AVP_DEBUGGING
459 dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %X",new);
460 dbg_print(dbg_avpl_op,4,dbg_fp,"insert_avp: %X %X %s%c%s;",avpl,avp,avp->n,avp->o,avp->v);
463 /* get to the insertion point */
464 for(c=avpl->null.next; c->avp; c = c->next) {
466 if ( avp->n == c->avp->n ) {
468 if (avp->v > c->avp->v) {
472 if (avp->v == c->avp->v) {
473 if (avp->o == AVP_OP_EQUAL) {
474 #ifdef _AVP_DEBUGGING
475 dbg_print(dbg_avpl_op,7,dbg_fp,"delete_avpn: %X",new);
477 g_mem_chunk_free(avp_chunk,new);
483 if (avp->n > c->avp->n) {
488 #ifdef _AVP_DEBUGGING
489 dbg_print(dbg_avpl,5,dbg_fp,"insert_avp: inserting %X in %X before %X;",avp,avpl,c);
499 #ifdef _AVP_DEBUGGING
500 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
508 * @avpl: the avpl from which to try to get the avp.
509 * @name: the name of the avp we are looking for.
510 * @cookie: variable in which to store the state between calls.
512 * Gets pointer to the next avp whose name is given; uses cookie to store its
513 * state between calls.
515 * Return value: a pointer to the next matching avp if there's one, else NULL.
518 extern AVP* get_avp_by_name(AVPL* avpl, guint8* name, void** cookie) {
520 AVPN* start = (AVPN*) *cookie;
522 #ifdef _AVP_DEBUGGING
523 dbg_print(dbg_avpl_op,7,dbg_fp,"get_avp_by_name: entering: %X %s %X",avpl,name,*cookie);
526 name = scs_subscribe(avp_strings, name);
528 if (!start) start = avpl->null.next;
530 for ( curr = start; curr->avp; curr = curr->next ) {
531 if ( curr->avp->n == name ) {
538 #ifdef _AVP_DEBUGGING
539 dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %X",curr);
542 scs_unsubscribe(avp_strings, name);
548 * extract_avp_by_name:
549 * @avpl: the avpl from which to try to extract the avp.
550 * @name: the name of the avp we are looking for.
552 * Extracts from the avpl the next avp whose name is given;
554 * Return value: a pointer to extracted avp if there's one, else NULL.
557 extern AVP* extract_avp_by_name(AVPL* avpl, guint8* name) {
561 #ifdef _AVP_DEBUGGING
562 dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %X %s",avpl,name);
565 name = scs_subscribe(avp_strings, name);
567 for ( curr = avpl->null.next; curr->avp; curr = curr->next ) {
568 if ( curr->avp->n == name ) {
573 scs_unsubscribe(avp_strings, name);
575 if( ! curr->avp ) return NULL;
577 curr->next->prev = curr->prev;
578 curr->prev->next = curr->next;
582 g_mem_chunk_free(avp_chunk,curr);
586 #ifdef _AVP_DEBUGGING
587 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
590 #ifdef _AVP_DEBUGGING
591 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %X",avp);
600 * @avpl: the avpl from which to try to extract the avp.
602 * Extracts the fisrt avp from the avpl.
604 * Return value: a pointer to extracted avp if there's one, else NULL.
607 extern AVP* extract_first_avp(AVPL* avpl) {
611 #ifdef _AVP_DEBUGGING
612 dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %X",avpl);
615 node = avpl->null.next;
617 avpl->null.next->prev = &avpl->null;
618 avpl->null.next = node->next;
623 g_mem_chunk_free(avp_chunk,node);
625 #ifdef _AVP_DEBUGGING
626 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
630 #ifdef _AVP_DEBUGGING
631 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %X",avp);
641 * @avpl: the avpl from which to try to extract the avp.
643 * Extracts the last avp from the avpl.
645 * Return value: a pointer to extracted avp if there's one, else NULL.
648 extern AVP* extract_last_avp(AVPL* avpl) {
652 node = avpl->null.prev;
654 avpl->null.prev->next = &avpl->null;
655 avpl->null.prev = node->prev;
660 g_mem_chunk_free(avp_chunk,node);
662 #ifdef _AVP_DEBUGGING
663 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
667 #ifdef _AVP_DEBUGGING
668 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",avp);
678 * @avpl: the avpl from which to try to extract the avp.
679 * @avps_too: whether or not it should delete the avps as well.
681 * Destroys an avpl and releases the resources it uses. If told to do
682 * so releases the avps as well.
685 extern void delete_avpl(AVPL* avpl, gboolean avps_too) {
687 #ifdef _AVP_DEBUGGING
688 dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %X",avpl);
691 while(( avp = extract_last_avp(avpl))) {
697 scs_unsubscribe(avp_strings,avpl->name);
698 g_mem_chunk_free(avp_chunk,avpl);
705 * @avpl: the avpl from which to try to get the avps.
706 * @cookie: variable in which to store the state between calls.
708 * Iterates on an avpl to get its avps.
710 * Return value: a pointer to the next avp if there's one, else NULL.
713 extern AVP* get_next_avp(AVPL* avpl, void** cookie) {
716 #ifdef _AVP_DEBUGGING
717 dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %X avpn: %X",avpl,*cookie);
721 node = (AVPN*) *cookie;
723 node = avpl->null.next;
726 *cookie = node->next;
728 #ifdef _AVP_DEBUGGING
729 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",node->avp);
737 * @avpl: the avpl to represent.
739 * Creates a newly allocated string containing a representation of an avpl.
741 * Return value: a pointer to the newly allocated string.
744 guint8* avpl_to_str(AVPL* avpl) {
746 GString* s = g_string_new("");
750 for(c=avpl->null.next; c->avp; c = c->next) {
751 avp_s = avp_to_str(c->avp);
752 g_string_sprintfa(s," %s;",avp_s);
757 g_string_free(s,FALSE);
759 /* g_strchug(r); ? */
763 extern guint8* avpl_to_dotstr(AVPL* avpl) {
765 GString* s = g_string_new("");
769 for(c=avpl->null.next; c->avp; c = c->next) {
770 avp_s = avp_to_str(c->avp);
771 g_string_sprintfa(s," .%s;",avp_s);
776 g_string_free(s,FALSE);
778 /* g_strchug(r); ? */
784 * @dst: the avpl in which to merge the avps.
785 * @src: the avpl from which to get the avps.
786 * @copy: whether avps should be copied instead of referenced.
788 * Adds the avps of src that are not existent in dst into dst.
790 * Return value: a pointer to the newly allocated string.
793 extern void merge_avpl(AVPL* dst, AVPL* src, gboolean copy_avps) {
799 #ifdef _AVP_DEBUGGING
800 dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %X %X",dst,src);
809 c = (guint) cd->avp->n - (guint) cs->avp->n;
815 if (cd->avp) cd = cd->next;
818 copy = avp_copy(cs->avp);
819 if ( ! insert_avp(dst,copy) ) {
823 insert_avp(dst,cs->avp);
828 if ( ! cd->avp || ! (cd->avp->v == cs->avp->v) ) {
830 copy = avp_copy(cs->avp);
831 if ( ! insert_avp(dst,copy) ) {
835 insert_avp(dst,cs->avp);
839 if (cd->avp) cd = cd->next;
843 #ifdef _AVP_DEBUGGING
844 dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
853 * @name: the name of the new avpl.
854 * @avpl: the avpl from which to get the avps.
855 * @copy_avps: whether avps should be copied instead of referenced.
857 * Creates a new avpl containing the same avps as the given avpl
858 * It will either reference or copie the avps.
860 * Return value: a pointer to the newly allocated string.
863 extern AVPL* new_avpl_from_avpl(guint8* name, AVPL* avpl, gboolean copy_avps) {
864 AVPL* newavpl = new_avpl(name);
869 #ifdef _AVP_DEBUGGING
870 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_from_avpl: %X from=%X name='%s'",newavpl,avpl,name);
873 while(( avp = get_next_avp(avpl,&cookie) )) {
875 copy = avp_copy(avp);
876 if ( ! insert_avp(newavpl,copy) ) {
880 insert_avp(newavpl,avp);
884 #ifdef _AVP_DEBUGGING
885 dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done");
892 #define TRANS_NUM '-': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0'
894 /* BROKEN, makes no sense right now */
895 /* FIXME: Use subscribe/unsubscribe */
896 static AVP* avp_transform(AVP* src, AVP* op) {
900 GString* replace_str = NULL;
901 GString* num_str = NULL;
902 GString* return_str = NULL;
915 for(i = 0; i < strlen(op->v); i++) {
918 if (state == END) break;
924 num_str = g_string_new("");
933 g_string_append_c(num_str,c);
936 num = strtol(num_str->str,NULL,10);
937 g_string_free(num_str,TRUE);
938 replace_str = g_string_new("");
942 /* will ignore any char that is not a number */
953 g_string_append_c(replace_str,c);
957 /* it will never reach */
966 return_str = g_string_new(v);
967 g_string_erase(return_str,0,num);
968 g_string_prepend(return_str,replace_str->str);
969 } else if (num < 0) {
970 return_str = g_string_new(v);
971 g_string_truncate(return_str,return_str->len+num);
972 g_string_append(return_str,replace_str->str);
975 return_str = g_string_new(replace_str->str);
978 g_mem_chunk_free(avp_chunk,v);
979 g_string_free(replace_str,TRUE);
982 src->v = return_str->str;
983 g_string_free(return_str,FALSE);
992 * @src: an src to be compared agains an "op" avp
993 * @op: the "op" avp that will be matched against the src avp
995 * Checks whether or not two avp's match.
997 * Return value: a pointer to the src avp if there's a match.
1000 extern AVP* match_avp(AVP* src, AVP* op) {
1008 gboolean lower = FALSE;
1010 #ifdef _AVP_DEBUGGING
1011 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);
1014 if ( src->n != op->n ) {
1022 return src->v == op->v ? src : NULL;
1023 case AVP_OP_NOTEQUAL:
1024 return !( src->v == op->v) ? src : NULL;
1026 return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL;
1028 splited = g_strsplit(op->v,"|",0);
1030 for (i=0;splited[i];i++) {
1031 if(g_str_equal(splited[i],src->v)) {
1032 g_strfreev(splited);
1036 g_strfreev(splited);
1044 fs = strtof(src->v, NULL);
1045 fo = strtof(src->v, NULL);
1048 if (fs<fo) return src;
1051 if (fs>fo) return src;
1055 /* does this work? */
1056 ls = strlen(src->v);
1062 p = src->v + ( ls - lo );
1063 return g_str_equal(p,op->v) ? src : NULL;
1066 /* case AVP_OP_TRANSF: */
1067 /* return do_transform(src,op); */
1068 case AVP_OP_CONTAINS:
1072 /* will never get here */
1078 /* TODO: rename me */
1080 * new_avpl_loose_match:
1081 * @name: the name of the resulting avpl
1082 * @src: avpl to be matched agains an "op" avpl
1083 * @op: the "op" avpl that will be matched against the src avpl
1084 * @copy_avps: whether the avps in the resulting avpl should be copied
1086 * creates an avp list containing any avps in src matching any avps in op
1087 * it will eventually create an empty list in none match
1089 * Return value: a pointer to the newly created avpl containing the
1092 extern AVPL* new_avpl_loose_match(guint8* name,
1095 gboolean copy_avps) {
1097 AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
1104 #ifdef _AVP_DEBUGGING
1105 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_loose_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
1109 cs = src->null.next;
1122 c = (guint) co->avp->n - (guint) cs->avp->n;
1125 if (co->avp) co = co->next;
1127 if (cs->avp) cs = cs->next;
1129 m = match_avp(cs->avp,co->avp);
1134 if ( ! insert_avp(newavpl,copy) ) {
1138 insert_avp(newavpl,m);
1144 if (cs->avp) cs = cs->next;
1149 #ifdef _AVP_DEBUGGING
1150 dbg_print(dbg_avpl_op,6,dbg_fp,"new_avpl_loose_match: done!");
1156 /* TODO: rename me */
1158 * new_avpl_every_match:
1159 * @name: the name of the resulting avpl
1160 * @src: avpl to be matched agains an "op" avpl
1161 * @op: the "op" avpl that will be matched against the src avpl
1162 * @copy_avps: whether the avps in the resulting avpl should be copied
1164 * creates an avp list containing any avps in src matching every avp in op
1165 * it will not create a list if there is not a match for every attribute in op
1167 * Return value: a pointer to the newly created avpl containing the
1170 extern AVPL* new_avpl_every_match(guint8* name, AVPL* src, AVPL* op, gboolean copy_avps) {
1171 AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
1178 #ifdef _AVP_DEBUGGING
1179 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_every_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
1182 gboolean matches = TRUE;
1184 cs = src->null.next;
1196 c = (guint) co->avp->n - (guint) cs->avp->n;
1199 delete_avpl(newavpl,TRUE);
1207 m = match_avp(cs->avp,co->avp);
1216 if ( ! insert_avp(newavpl,copy) ) {
1220 insert_avp(newavpl,m);
1233 delete_avpl(newavpl,TRUE);
1239 /* TODO: rename me */
1241 * new_avpl_exact_match:
1242 * @name: the name of the resulting avpl
1243 * @src: avpl to be matched agains an "op" avpl
1244 * @op: the "op" avpl that will be matched against the src avpl
1245 * @copy_avps: whether the avps in the resulting avpl should be copied
1247 * creates an avp list containing every avp in src matching every avp in op
1248 * it will not create a list unless every avp in op is matched only once
1249 * to every avp in op.
1251 * Return value: a pointer to the newly created avpl containing the
1254 extern AVPL* new_avpl_exact_match(guint8* name,AVPL* src, AVPL* op, gboolean copy_avps) {
1255 AVPL* newavpl = new_avpl(name);
1262 #ifdef _AVP_DEBUGGING
1263 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_every_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
1266 cs = src->null.next;
1270 c = (guint) co->avp->n - (guint) cs->avp->n;
1273 delete_avpl(newavpl,TRUE);
1278 delete_avpl(newavpl,TRUE);
1282 m = match_avp(cs->avp,co->avp);
1290 if ( ! insert_avp(newavpl,copy) ) {
1294 insert_avp(newavpl,m);
1302 delete_avpl(newavpl,TRUE);
1306 delete_avpl(newavpl,TRUE);
1313 /* should never be reached */
1319 * new_avpl_transform:
1321 * creates an empty avpl transformation
1323 * Return value: a pointer to the newly created avpl transformation
1325 extern AVPL_Transf* new_avpl_transform(guint8* name, AVPL* mixed, avpl_match_mode match_mode, avpl_replace_mode replace_mode) {
1326 AVPL_Transf* t = g_malloc(sizeof(AVPL_Transf));
1329 t->name = g_strdup(name);
1330 t->match = new_avpl("match");
1331 t->replace = new_avpl("replace");
1332 t->match_mode = match_mode;
1333 t->replace_mode = replace_mode;
1337 while (( avp = extract_first_avp(mixed) )) {
1338 if (*(avp->n) == '.') {
1339 rename_avp(avp,(avp->n+1));
1340 insert_avp(t->replace, avp);
1342 insert_avp(t->match, avp);
1351 * delete_avpl_transform:
1352 * @it: a pointer to the avpl transformation object
1354 * Destroys an avpl transformation object and releases all the resources it
1358 extern void delete_avpl_transform(AVPL_Transf* op) {
1361 for (; op ; op = next) {
1367 delete_avpl(op->match,TRUE);
1371 delete_avpl(op->replace,TRUE);
1382 * @src: the source avpl for the transform operation.
1383 * @op: a pointer to the avpl transformation object to apply.
1385 * Applies the "op" transformation to an avpl, matches it and eventually
1386 * replaces or inserts the transformed avps.
1388 * Return value: whether the transformation was performed or not.
1390 extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
1397 #ifdef _AVP_DEBUGGING
1398 dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%X op=%X",src,op);
1401 for ( ; op ; op = op->next) {
1402 switch (op->match_mode) {
1404 avpl = new_avpl_exact_match(src->name,src,op->match,TRUE);
1407 avpl = new_avpl_loose_match(src->name,src,op->match,TRUE);
1410 avpl = new_avpl_every_match(src->name,src,op->match,TRUE);
1413 avpl = new_avpl(src->name);
1418 switch (op->replace_mode) {
1419 case AVPL_NO_REPLACE:
1420 delete_avpl(avpl,TRUE);
1423 merge_avpl(src,op->replace,TRUE);
1424 delete_avpl(avpl,TRUE);
1427 cs = src->null.next;
1428 cm = avpl->null.next;
1431 if (cs->avp == cm->avp) {
1434 cs->prev->next = cs->next;
1435 cs->next->prev = cs->prev;
1436 g_mem_chunk_free(avp_chunk,cs);
1445 merge_avpl(src,avpl,TRUE);
1446 delete_avpl(avpl,TRUE);
1456 * @name: the name the loal will take.
1458 * Creates an empty list of avp lists.
1460 * Return value: a pointer to the newly created loal.
1462 extern LoAL* new_loal(guint8* name) {
1463 LoAL* new_loal = g_mem_chunk_alloc(avp_chunk);
1469 #ifdef _AVP_DEBUGGING
1470 dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal: %X name=%s",new_loal,name);
1473 new_loal->name = scs_subscribe(avp_strings,name);
1474 new_loal->null.avpl = NULL;
1475 new_loal->null.next = &new_loal->null;
1476 new_loal->null.prev = &new_loal->null;
1483 * @loal: the loal on which to operate.
1484 * @avpl: the avpl to append.
1486 * Appends an avpl to a loal.
1489 extern void loal_append(LoAL* loal, AVPL* avpl) {
1490 LoALnode* node = g_mem_chunk_alloc(avp_chunk);
1492 #ifdef _AVP_DEBUGGING
1493 dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %X",node);
1497 node->next = &loal->null;
1498 node->prev = loal->null.prev;
1500 loal->null.prev->next = node;
1501 loal->null.prev = node;
1506 * extract_first_avpl:
1507 * @loal: the loal on which to operate.
1509 * Extracts the first avpl contained in a loal.
1511 * Return value: a pointer to the extracted avpl.
1514 extern AVPL* extract_first_avpl(LoAL* loal) {
1518 #ifdef _AVP_DEBUGGING
1519 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name);
1522 node = loal->null.next;
1524 loal->null.next->next->prev = &loal->null;
1525 loal->null.next = node->next;
1532 g_mem_chunk_free(avp_chunk,node);
1534 #ifdef _AVP_DEBUGGING
1535 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name);
1536 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
1544 * extract_first_avpl:
1545 * @loal: the loal on which to operate.
1547 * Extracts the last avpl contained in a loal.
1549 * Return value: a pointer to the extracted avpl.
1552 extern AVPL* extract_last_avpl(LoAL* loal){
1556 node = loal->null.prev;
1558 loal->null.prev->prev->next = &loal->null;
1559 loal->null.prev = node->prev;
1566 g_mem_chunk_free(avp_chunk,node);
1567 #ifdef _AVP_DEBUGGING
1568 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
1576 * extract_first_avpl:
1577 * @loal: the loal on which to operate.
1578 * @cookie pointer to the pointer variable to contain the state between calls
1580 * At each call will return the following avpl from a loal. The given cookie
1581 * will be used to manatain the state between calls.
1583 * Return value: a pointer to the next avpl.
1586 extern AVPL* get_next_avpl(LoAL* loal,void** cookie) {
1589 #ifdef _AVP_DEBUGGING
1590 dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%X node=%X",loal,*cookie);
1594 node = (LoALnode*) *cookie;
1596 node = loal->null.next;
1599 *cookie = node->next;
1606 * @loal: the loal to be deleted.
1607 * @avpls_too: whether avpls contained by the loal should be deleted as well
1608 * @avps_too: whether avps contained by the avpls should be also deleted
1610 * Destroys a loal and eventually desstroys avpls and avps.
1613 extern void delete_loal(LoAL* loal, gboolean avpls_too, gboolean avps_too) {
1616 #ifdef _AVP_DEBUGGING
1617 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %X",loal);
1620 while(( avpl = extract_last_avpl(loal) )) {
1622 delete_avpl(avpl,avps_too);
1626 scs_unsubscribe(avp_strings,loal->name);
1627 g_mem_chunk_free(avp_chunk,loal);
1632 /****************************************************************************
1633 ******************* the following are used in load_loal_from_file
1634 ****************************************************************************/
1638 * Used by loal_from_file to handle errors while loading.
1640 void load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, guint8* fmt, ...) {
1645 va_start( list, fmt );
1646 desc = g_strdup_vprintf(fmt, list);
1650 if (loal) delete_loal(loal,TRUE,TRUE);
1651 if (curr) delete_avpl(curr,TRUE);
1653 g_warning("Error Loading LoAL from file: at line: %i, %s",linenum,desc);
1660 /* the maximum lenght allowed for a line */
1661 #define MAX_ITEM_LEN 8192
1663 /* this two ugly things are used for tokenizing */
1664 #define AVP_OP_CHAR '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
1666 #define AVP_NAME_CHAR 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
1667 case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
1668 case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
1669 case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
1670 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
1671 case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
1672 case '7': case '8': case '9': case '.'
1677 * @filename: the file containing a loals text representation.
1679 * Given a filename it will attempt to load a loal containing a copy of
1680 * the avpls represented in the file.
1682 * Return value: if successful a pointer to the new populated loal, else NULL.
1685 extern LoAL* loal_from_file(guint8* filename) {
1689 guint32 linenum = 1;
1690 guint8 name[MAX_ITEM_LEN];
1691 guint8 value[MAX_ITEM_LEN];
1693 LoAL *loal = new_loal(filename);
1697 enum _load_loal_states {
1707 g_warning( "MATE Will not run as root");
1714 if (( fp = fopen(filename,"r") )) {
1715 while(( c = (guint8) fgetc(fp) )){
1719 load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename);
1729 if ( i >= MAX_ITEM_LEN - 1 ) {
1730 load_loal_error(fp,loal,curr,linenum,"Maximum item lenght exceeded");
1747 case ' ': case '\t':
1748 /* ignore whitespace at line start */
1751 /* ignore empty lines */
1760 curr = new_avpl("");
1766 load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1775 if (c != '\n') ungetc(c,fp);
1787 loal_append(loal,curr);
1791 load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1797 state = BEFORE_NAME;
1804 avp = new_avp(name,value,op);
1806 if (! insert_avp(curr,avp) ) {
1821 load_loal_error(fp,loal,curr,linenum,"operator expected found new line");
1824 load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c);
1830 value[i++] = fgetc(fp);
1833 state = BEFORE_NAME;
1838 avp = new_avp(name,value,op);
1840 if (! insert_avp(curr,avp) ) {
1845 load_loal_error(fp,loal,curr,linenum,"';' expected found new line");
1858 load_loal_error(NULL,loal,NULL,0,"Cannot Open file '%s'",filename);