Update for Mate from Luis Ontanon:
[obnox/wireshark/wip.git] / plugins / mate / mate_util.c
1 /* mate_util.c
2 * MATE -- Meta Analysis Tracing Engine
3 * Utility Library: Single Copy Strings and Attribute Value Pairs
4 *
5 * Copyright 2004, Luis E. Garcia Ontanon <luis.ontanon@gmail.com>
6 *
7 * $Id$
8 *
9 * Ethereal - Network traffic analyzer
10 * By Gerald Combs <gerald@ethereal.com>
11 * Copyright 1998 Gerald Combs
12 *
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.
17 *
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.
22 *
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.
26 */
27
28 #include "config.h"
29 #include <glib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include "mate.h"
33 #include "mate_util.h"
34
35 /* TODO:
36 + fix debug_print levels
37 + chunks for scs_strings (do I realy need em??)
38         + checking bounds in (almost) every operator
39 - operators
40         + rethink '&' operator
41         - bounds check on op_match()
42         ? change &{} into []
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?
46  - transform
47         + map (N->M hash)
48 */
49
50
51 /***************************************************************************
52 *  dbg_print
53 ***************************************************************************
54 * This is the debug facility of the thing.
55 ***************************************************************************/
56
57 /* dbg_print:
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
62  */
63
64 void dbg_print(const guint* which, guint how, FILE* where, guint8* fmt, ... ) {
65         static guint8 debug_buffer[DEBUG_BUFFER_SIZE];
66         va_list list;
67
68         if ( ! which || *which < how ) return;
69
70         va_start( list, fmt );
71         g_vsnprintf(debug_buffer,DEBUG_BUFFER_SIZE,fmt,list);
72         va_end( list );
73
74         if (! where) {
75                 g_message(debug_buffer);
76         } else {
77                 fputs(debug_buffer,where);
78         }
79
80 }
81
82
83 /***************************************************************************
84  *  single copy strings
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 */
91
92 /**
93  * scs_init:
94  * @collection: the scs hash
95  *
96  *  Initializes the scs hash.
97  **/
98
99 void scs_init(GHashTable** hash) {
100         /* key: a string
101          value: guint number of subscribers */
102
103         if (*hash) g_hash_table_destroy(*hash);
104
105         *hash =  g_hash_table_new_full(g_str_hash,g_str_equal,g_free,g_free);
106
107 }
108
109
110 /**
111  * subscribe:
112  * @collection: the scs hash
113  * @s: a string
114  *
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.
119  *
120  * Return value: a pointer to the subscribed string.
121  **/
122 guint8* scs_subscribe(GHashTable* collection, guint8* s) {
123         guint8* ok = NULL;
124         guint* ip = NULL;
125
126         g_hash_table_lookup_extended(collection,s,(gpointer*)&ok,(gpointer*)&ip);
127
128         if (ip) {
129                 (*ip)++;
130                 s = ok;
131         } else {
132                 ip = g_malloc(sizeof(int));
133                 *ip = 0;
134                 s = g_strdup(s);
135                 g_hash_table_insert(collection,s,ip);
136         }
137
138         return s;
139 }
140
141 /**
142  * unsubscribe:
143  * @collection: the scs hash
144  * @s: a string.
145  *
146  * decreases the count of subscribers, if zero frees the internal copy of
147  * the string.
148  **/
149 void scs_unsubscribe(GHashTable* collection, guint8* s) {
150         guint8* ok = NULL;
151         guint* ip = NULL;
152
153         g_hash_table_lookup_extended(collection,s,(gpointer*)&ok,(gpointer*)&ip);
154
155         if (ip) {
156                 if (*ip == 0) g_hash_table_remove(collection,ok);
157                 else (*ip)--;
158         } else {
159                 g_warning("unsusbcribe: already deleted: '%s'?",s);
160         }
161 }
162
163 /**
164  * scs_subscribe_printf:
165  * @fmt: a format string ...
166  *
167  * Formats the input and subscribes it.
168  *
169  * Return value: the stored copy of the formated string.
170  *
171  **/
172 extern guint8* scs_subscribe_printf(GHashTable* collection, guint8* fmt, ...) {
173         va_list list;
174         guint8* ok = NULL;
175         guint8* s = NULL;
176         guint* ip = NULL;
177
178         va_start( list, fmt );
179         s = g_strdup_vprintf(fmt, list);
180         va_end( list );
181
182         g_hash_table_lookup_extended(collection,s,(gpointer*)&ok,(gpointer*)&ip);
183
184         if (ip) {
185                 (*ip)++;
186                 g_free(s);
187                 s = ok;
188         } else {
189                 ip = g_malloc0(sizeof(int));
190                 *ip = 0;
191                 g_hash_table_insert(collection,s,ip);
192         }
193
194         return s;
195 }
196
197
198 /***************************************************************************
199 *  AVPs & Co.
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
205 *
206 *
207 ***************************************************************************/
208
209
210 typedef union _any_avp_type {
211         AVP avp;
212         AVPN avpn;
213         AVPL avpl;
214         LoAL loal;
215         LoALnode loaln;
216 } any_avp_type;
217
218
219 static GMemChunk* avp_chunk = NULL;
220 static GHashTable* avp_strings = NULL;
221
222 #ifdef _AVP_DEBUGGING
223 static FILE* dbg_fp = NULL;
224
225 static int dbg_level = 0;
226 static int* dbg = &dbg_level;
227
228 static int dbg_avp_level = 0;
229 static int* dbg_avp = &dbg_avp_level;
230
231 static int dbg_avp_op_level = 0;
232 static int* dbg_avp_op = &dbg_avp_op_level;
233
234 static int dbg_avpl_level = 0;
235 static int* dbg_avpl = &dbg_avpl_level;
236
237 static int dbg_avpl_op_level = 0;
238 static int* dbg_avpl_op = &dbg_avpl_op_level;
239
240 /**
241  * setup_avp_debug:
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"
248  *
249  * (If enabled set's up the debug facilities for the avp library.
250  *
251  **/
252 extern void setup_avp_debug(FILE* fp, int* general, int* avp, int* avp_op, int* avpl, int* avpl_op) {
253         dbg_fp = fp;
254         dbg = general;
255         dbg_avp = avp;
256         dbg_avp_op = avp_op;
257         dbg_avpl = avpl;
258         dbg_avpl_op = avpl_op;
259 }
260
261 #endif /* _AVP_DEBUGGING */
262
263 /**
264  * avp_init:
265  * @chunk_size: the initial chunk's size.
266  *
267  * (Re)Initializes the avp library.
268  *
269  **/
270 extern void avp_init(void) {
271
272         scs_init(&avp_strings);
273
274
275         if ( avp_chunk ) {
276                 g_mem_chunk_destroy(avp_chunk);
277         }
278
279         avp_chunk = g_mem_chunk_new("avp_chunk", sizeof(any_avp_type),
280                                                                 AVP_CHUNK_SIZE, G_ALLOC_AND_FREE);
281
282 }
283
284
285 /**
286  * new_avp_from_finfo:
287  * @name: the name the avp will have.
288  * @finfo: the field_info from which to fetch the data.
289  *
290  * Creates an avp from a field_info record.
291  *
292  * Return value: a pointer to the newly created avp.
293  *
294  **/
295 extern AVP* new_avp_from_finfo(guint8* name, field_info* finfo) {
296         AVP* new = g_mem_chunk_alloc(avp_chunk);
297         guint8* value;
298         guint8* str;
299         new->n = scs_subscribe(avp_strings, name);
300
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);
305 #endif
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);
311 #endif
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);
316 #endif
317         } else {
318 #ifdef _AVP_DEBUGGING
319                 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: a proto: %s",finfo->hfinfo->abbrev);
320 #endif
321                 value = scs_subscribe(avp_strings, finfo->hfinfo->abbrev);
322         }
323
324         new->v = value;
325
326         new->o = '=';
327
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);
330 #endif
331
332         return new;
333 }
334
335
336 /**
337  * new_avp:
338  * @name: the name the avp will have.
339  * @value: the value the avp will have.
340  * @o: the operator of this avp.
341  *
342  * Creates an avp given every parameter.
343  *
344  * Return value: a pointer to the newly created avp.
345  *
346  **/
347 extern AVP* new_avp(guint8* name, guint8* value, guint8 o) {
348         AVP* new = g_mem_chunk_alloc(avp_chunk);
349
350         new->n = scs_subscribe(avp_strings, name);
351         new->v = scs_subscribe(avp_strings, value);
352         new->o = o;
353
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);
356 #endif
357         return new;
358 }
359
360
361 /**
362 * delete_avp:
363  * @avp: the avp to delete.
364  *
365  * Destroys an avp and releases the resources it uses.
366  *
367  **/
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);
371 #endif
372
373         scs_unsubscribe(avp_strings, avp->n);
374         scs_unsubscribe(avp_strings, avp->v);
375         g_mem_chunk_free(avp_chunk,avp);
376 }
377
378
379 /**
380 * avp_copy:
381  * @from: the avp to be copied.
382  *
383  * Creates an avp whose name op and value are copyes of the given one.
384  *
385  * Return value: a pointer to the newly created avp.
386  *
387  **/
388 extern AVP* avp_copy(AVP* from) {
389         AVP* new = g_mem_chunk_alloc(avp_chunk);
390
391         new->n = scs_subscribe(avp_strings, from->n);
392         new->v = scs_subscribe(avp_strings, from->v);
393         new->o = from->o;
394
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);
397 #endif
398
399         return new;
400 }
401
402
403 extern void rename_avp(AVP* avp, guint8* name) {
404         guint8* s = avp->n;
405         avp->n = scs_subscribe(avp_strings,name);
406         scs_unsubscribe(avp_strings,s);
407 }
408
409 /**
410  * new_avpl:
411  * @name: the name the avpl will have.
412  *
413  * Creates an empty avpl.
414  *
415  * Return value: a pointer to the newly created avpl.
416  *
417  **/
418 extern AVPL* new_avpl(guint8* name) {
419         AVPL* new_avpl = g_mem_chunk_alloc(avp_chunk);
420
421 #ifdef _AVP_DEBUGGING
422         dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpl: %X name=%s",new_avpl,name);
423 #endif
424
425         new_avpl->name = scs_subscribe(avp_strings, name);
426         new_avpl->len = 0;
427         new_avpl->null.avp = NULL;
428         new_avpl->null.next = &new_avpl->null;
429         new_avpl->null.prev = &new_avpl->null;
430
431
432         return new_avpl;
433 }
434
435 extern void rename_avpl(AVPL* avpl, guint8* name) {
436         scs_unsubscribe(avp_strings,avpl->name);
437         avpl->name = scs_subscribe(avp_strings,name);
438 }
439
440 /**
441  * insert_avp:
442  * @avpl: the avpl in which to insert.
443  * @avp: the avp to be inserted.
444  *
445  * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
446  *
447  * Return value: whether it was inserted or not.
448  *
449  * BEWARE: Check the return value, you might need to delete the avp if
450  *         it is not inserted.
451  **/
452 extern gboolean insert_avp(AVPL* avpl, AVP* avp) {
453         AVPN* new = g_mem_chunk_alloc(avp_chunk);
454         AVPN* c;
455
456         new->avp = avp;
457
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);
461 #endif
462
463         /* get to the insertion point */
464         for(c=avpl->null.next; c->avp; c = c->next) {
465
466                 if ( avp->n == c->avp->n ) {
467
468                         if (avp->v > c->avp->v) {
469                                 break;
470                         }
471
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);
476 #endif
477                                         g_mem_chunk_free(avp_chunk,new);
478                                         return FALSE;
479                                 }
480                         }
481                 }
482
483                 if (avp->n > c->avp->n) {
484                         break;
485                 }
486         }
487
488 #ifdef _AVP_DEBUGGING
489         dbg_print(dbg_avpl,5,dbg_fp,"insert_avp:  inserting %X in %X before %X;",avp,avpl,c);
490 #endif
491
492         new->next = c;
493         new->prev = c->prev;
494         c->prev->next = new;
495         c->prev = new;
496
497         avpl->len++;
498
499 #ifdef _AVP_DEBUGGING
500         dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
501 #endif
502
503         return TRUE;
504 }
505
506 /**
507  * get_avp_by_name:
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.
511  *
512  * Gets  pointer to the next avp whose name is given; uses cookie to store its
513  * state between calls.
514  *
515  * Return value: a pointer to the next matching avp if there's one, else NULL.
516  *
517  **/
518 extern AVP* get_avp_by_name(AVPL* avpl, guint8* name, void** cookie) {
519         AVPN* curr;
520         AVPN* start = (AVPN*) *cookie;
521
522 #ifdef _AVP_DEBUGGING
523         dbg_print(dbg_avpl_op,7,dbg_fp,"get_avp_by_name: entering: %X %s %X",avpl,name,*cookie);
524 #endif
525
526         name = scs_subscribe(avp_strings, name);
527
528         if (!start) start = avpl->null.next;
529
530         for ( curr = start; curr->avp; curr = curr->next ) {
531                 if ( curr->avp->n == name ) {
532                         break;
533                 }
534         }
535
536         *cookie = curr;
537
538 #ifdef _AVP_DEBUGGING
539         dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %X",curr);
540 #endif
541
542         scs_unsubscribe(avp_strings, name);
543
544         return curr->avp;
545 }
546
547 /**
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.
551  *
552  * Extracts from the avpl the next avp whose name is given;
553  *
554  * Return value: a pointer to extracted avp if there's one, else NULL.
555  *
556  **/
557 extern AVP* extract_avp_by_name(AVPL* avpl, guint8* name) {
558         AVPN* curr;
559         AVP* avp = NULL;
560
561 #ifdef _AVP_DEBUGGING
562         dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %X %s",avpl,name);
563 #endif
564
565         name = scs_subscribe(avp_strings, name);
566
567         for ( curr = avpl->null.next; curr->avp; curr = curr->next ) {
568                 if ( curr->avp->n == name ) {
569                         break;
570                 }
571         }
572
573         scs_unsubscribe(avp_strings, name);
574
575         if( ! curr->avp ) return NULL;
576
577         curr->next->prev = curr->prev;
578         curr->prev->next = curr->next;
579
580         avp = curr->avp;
581
582         g_mem_chunk_free(avp_chunk,curr);
583
584         (avpl->len)--;
585
586 #ifdef _AVP_DEBUGGING
587         dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
588 #endif
589
590 #ifdef _AVP_DEBUGGING
591         dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %X",avp);
592 #endif
593
594         return avp;
595 }
596
597
598 /**
599  * extract_first_avp:
600  * @avpl: the avpl from which to try to extract the avp.
601  *
602  * Extracts the fisrt avp from the avpl.
603  *
604  * Return value: a pointer to extracted avp if there's one, else NULL.
605  *
606  **/
607 extern AVP* extract_first_avp(AVPL* avpl) {
608         AVP* avp;
609         AVPN* node;
610
611 #ifdef _AVP_DEBUGGING
612         dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %X",avpl);
613 #endif
614
615         node = avpl->null.next;
616
617         avpl->null.next->prev = &avpl->null;
618         avpl->null.next = node->next;
619
620         avp = node->avp;
621
622         if (avp) {
623                 g_mem_chunk_free(avp_chunk,node);
624                 (avpl->len)--;
625 #ifdef _AVP_DEBUGGING
626                 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
627 #endif
628         }
629
630 #ifdef _AVP_DEBUGGING
631         dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %X",avp);
632 #endif
633
634         return avp;
635
636 }
637
638
639 /**
640  * extract_last_avp:
641  * @avpl: the avpl from which to try to extract the avp.
642  *
643  * Extracts the last avp from the avpl.
644  *
645  * Return value: a pointer to extracted avp if there's one, else NULL.
646  *
647  **/
648 extern AVP* extract_last_avp(AVPL* avpl) {
649         AVP* avp;
650         AVPN* node;
651
652         node = avpl->null.prev;
653
654         avpl->null.prev->next = &avpl->null;
655         avpl->null.prev = node->prev;
656
657         avp = node->avp;
658
659         if (avp) {
660                 g_mem_chunk_free(avp_chunk,node);
661                 (avpl->len)--;
662 #ifdef _AVP_DEBUGGING
663                 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
664 #endif
665         }
666
667 #ifdef _AVP_DEBUGGING
668         dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",avp);
669 #endif
670
671         return avp;
672
673 }
674
675
676 /**
677  * delete_avpl:
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.
680  *
681  * Destroys an avpl and releases the resources it uses. If told to do
682  * so releases the avps as well.
683  *
684  **/
685 extern void delete_avpl(AVPL* avpl, gboolean avps_too) {
686         AVP* avp;
687 #ifdef _AVP_DEBUGGING
688         dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %X",avpl);
689 #endif
690
691         while(( avp = extract_last_avp(avpl))) {
692                 if (avps_too) {
693                         delete_avp(avp);
694                 }
695         }
696
697         scs_unsubscribe(avp_strings,avpl->name);
698         g_mem_chunk_free(avp_chunk,avpl);
699 }
700
701
702
703 /**
704  * get_next_avp:
705  * @avpl: the avpl from which to try to get the avps.
706  * @cookie: variable in which to store the state between calls.
707  *
708  * Iterates on an avpl to get its avps.
709  *
710  * Return value: a pointer to the next avp if there's one, else NULL.
711  *
712  **/
713 extern AVP* get_next_avp(AVPL* avpl, void** cookie) {
714         AVPN* node;
715
716 #ifdef _AVP_DEBUGGING
717         dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %X avpn: %X",avpl,*cookie);
718 #endif
719
720         if (*cookie) {
721                 node = (AVPN*) *cookie;
722         } else {
723                 node = avpl->null.next;
724         }
725
726         *cookie = node->next;
727
728 #ifdef _AVP_DEBUGGING
729         dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",node->avp);
730 #endif
731
732         return node->avp;
733 }
734
735 /**
736  * avpl_to_str:
737  * @avpl: the avpl to represent.
738  *
739  * Creates a newly allocated string containing a representation of an avpl.
740  *
741  * Return value: a pointer to the newly allocated string.
742  *
743  **/
744 guint8* avpl_to_str(AVPL* avpl) {
745         AVPN* c;
746         GString* s = g_string_new("");
747         guint8* avp_s;
748         guint8* r;
749
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);
753                 g_free(avp_s);
754         }
755
756         r = s->str;
757         g_string_free(s,FALSE);
758
759         /* g_strchug(r); ? */
760         return r;
761 }
762
763 extern guint8* avpl_to_dotstr(AVPL* avpl) {
764         AVPN* c;
765         GString* s = g_string_new("");
766         guint8* avp_s;
767         guint8* r;
768
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);
772                 g_free(avp_s);
773         }
774
775         r = s->str;
776         g_string_free(s,FALSE);
777
778         /* g_strchug(r); ? */
779         return r;
780 }
781
782 /**
783 * merge_avpl:
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.
787  *
788  * Adds the avps of src that are not existent in dst into dst.
789  *
790  * Return value: a pointer to the newly allocated string.
791  *
792  **/
793 extern void merge_avpl(AVPL* dst, AVPL* src, gboolean copy_avps) {
794         AVPN* cd = NULL;
795         AVPN* cs = NULL;
796         gint c;
797         AVP* copy;
798
799 #ifdef _AVP_DEBUGGING
800         dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %X %X",dst,src);
801 #endif
802
803         cs = src->null.next;
804         cd = dst->null.next;
805
806         while(cs->avp) {
807
808                 if(cd->avp) {
809                         c = (guint) cd->avp->n - (guint) cs->avp->n;
810                 } else {
811                         c = -1;
812                 }
813
814                 if (c > 0) {
815                         if (cd->avp) cd = cd->next;
816                 } else if (c < 0) {
817                         if (copy_avps) {
818                                 copy = avp_copy(cs->avp);
819                                 if ( ! insert_avp(dst,copy) ) {
820                                         delete_avp(copy);
821                                 }
822                         } else {
823                                 insert_avp(dst,cs->avp);
824                         }
825
826                         cs = cs->next;
827                 } else {
828                         if ( ! cd->avp || ! (cd->avp->v == cs->avp->v)  ) {
829                                 if (copy_avps) {
830                                         copy = avp_copy(cs->avp);
831                                         if ( ! insert_avp(dst,copy) ) {
832                                                 delete_avp(copy);
833                                         }
834                                 } else {
835                                         insert_avp(dst,cs->avp);
836                                 }
837                         }
838                         cs = cs->next;
839                         if (cd->avp) cd = cd->next;
840                 }
841         }
842
843 #ifdef _AVP_DEBUGGING
844         dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
845 #endif
846
847         return;
848 }
849
850
851 /**
852  * merge_avpl:
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.
856  *
857  * Creates a new avpl containing the same avps as the given avpl
858  * It will either reference or copie the avps.
859  *
860  * Return value: a pointer to the newly allocated string.
861  *
862  **/
863 extern AVPL* new_avpl_from_avpl(guint8* name, AVPL* avpl, gboolean copy_avps) {
864         AVPL* newavpl = new_avpl(name);
865         void* cookie = NULL;
866         AVP* avp;
867         AVP* copy;
868
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);
871 #endif
872
873         while(( avp = get_next_avp(avpl,&cookie) )) {
874                 if (copy_avps) {
875                         copy = avp_copy(avp);
876                         if ( ! insert_avp(newavpl,copy) ) {
877                                 delete_avp(copy);
878                         }
879                 } else {
880                         insert_avp(newavpl,avp);
881                 }
882         }
883
884 #ifdef _AVP_DEBUGGING
885         dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done");
886 #endif
887
888         return newavpl;
889 }
890
891
892 #define TRANS_NUM   '-': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0'
893
894 /* BROKEN, makes no sense right now */
895 /* FIXME: Use subscribe/unsubscribe */
896 static AVP* avp_transform(AVP* src, AVP* op) {
897         unsigned int i;
898         guint8 c;
899
900         GString* replace_str = NULL;
901         GString* num_str = NULL;
902         GString* return_str = NULL;
903         guint8* v;
904         long num = 0;
905
906         enum _tranf_state {
907                 START,
908                 IN_NUM,
909                 IN_REPLACE,
910                 END
911         } state;
912
913         state = START;
914
915         for(i = 0; i < strlen(op->v); i++) {
916                 c= op->v[i];
917
918                 if (state == END) break;
919
920                 switch(state) {
921                         case START:
922                                 switch(c) {
923                                         case '{':
924                                                 num_str = g_string_new("");
925                                                 state = IN_NUM;
926                                                 continue;
927                                         default:
928                                                 continue;
929                                 };
930                         case IN_NUM:
931                                 switch(c) {
932                                         case TRANS_NUM:
933                                                 g_string_append_c(num_str,c);
934                                                 continue;
935                                         case ':':
936                                                 num = strtol(num_str->str,NULL,10);
937                                                 g_string_free(num_str,TRUE);
938                                                 replace_str = g_string_new("");
939                                                 state = IN_REPLACE;
940                                                 continue;
941                                         default:
942                                                 /* will ignore any char that is not a number */
943                                                 continue;
944                                 };
945                         case IN_REPLACE:
946                                 switch(c) {
947                                         case '\\':
948                                                 continue;
949                                         case '}':
950                                                 state = END;
951                                                 continue;
952                                         default :
953                                                 g_string_append_c(replace_str,c);
954                                                 continue;
955                                 }
956                         case END:
957                                 /* it will never reach */
958                                 continue;
959                 }
960
961         }
962
963         v = src->v;
964
965         if (num > 0) {
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);
973
974         } else {
975                 return_str = g_string_new(replace_str->str);
976         }
977
978         g_mem_chunk_free(avp_chunk,v);
979         g_string_free(replace_str,TRUE);
980
981         src->o = '=';
982         src->v = return_str->str;
983         g_string_free(return_str,FALSE);
984
985         return src;
986 }
987
988
989
990 /**
991 * match_avp:
992  * @src: an src to be compared agains an "op" avp
993  * @op: the "op" avp that will be matched against the src avp
994  *
995  * Checks whether or not two avp's match.
996  *
997  * Return value: a pointer to the src avp if there's a match.
998  *
999  **/
1000 extern AVP* match_avp(AVP* src, AVP* op) {
1001         gchar** splited;
1002         int i;
1003         gchar* p;
1004         guint ls;
1005         guint lo;
1006         float fs = 0.0;
1007         float fo = 0.0;
1008         gboolean lower = FALSE;
1009
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);
1012 #endif
1013
1014         if ( src->n != op->n ) {
1015                 return NULL;
1016         }
1017
1018         switch (op->o) {
1019                 case AVP_OP_EXISTS:
1020                         return src;
1021                 case AVP_OP_EQUAL:
1022                         return src->v == op->v ? src : NULL;
1023                 case AVP_OP_NOTEQUAL:
1024                         return !( src->v == op->v) ? src : NULL;
1025                 case AVP_OP_STARTS:
1026                         return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL;
1027                 case AVP_OP_ONEOFF:
1028                         splited = g_strsplit(op->v,"|",0);
1029                         if (splited) {
1030                                 for (i=0;splited[i];i++) {
1031                                         if(g_str_equal(splited[i],src->v)) {
1032                                                 g_strfreev(splited);
1033                                                 return src;
1034                                         }
1035                                 }
1036                                 g_strfreev(splited);
1037                         }
1038                         return NULL;
1039
1040                 case AVP_OP_LOWER:
1041                         lower = TRUE;
1042                 case AVP_OP_HIGHER:
1043
1044                         fs = (float) strtod(src->v, NULL);
1045                         fo = (float) strtod(src->v, NULL);
1046
1047                         if (lower) {
1048                                 if (fs<fo) return src;
1049                                 else return NULL;
1050                         } else {
1051                                 if (fs>fo) return src;
1052                                 else return NULL;
1053                         }
1054                 case AVP_OP_ENDS:
1055                         /* does this work? */
1056                         ls = strlen(src->v);
1057                         lo = strlen(op->v);
1058
1059                         if ( ls < lo ) {
1060                                 return NULL;
1061                         } else {
1062                                 p = src->v + ( ls - lo );
1063                                 return g_str_equal(p,op->v) ? src : NULL;
1064                         }
1065
1066                 /* case AVP_OP_TRANSF: */
1067                 /*      return do_transform(src,op); */
1068                 case AVP_OP_CONTAINS:
1069                         /* TODO */
1070                         return NULL;
1071         }
1072         /* will never get here */
1073         return NULL;
1074 }
1075
1076
1077
1078 /* TODO: rename me */
1079 /**
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
1085  *
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
1088  *
1089  * Return value: a pointer to the newly created avpl containing the
1090  *                               matching avps.
1091  **/
1092 extern AVPL* new_avpl_loose_match(guint8* name,
1093                                                                   AVPL* src,
1094                                                                   AVPL* op,
1095                                                                   gboolean copy_avps) {
1096
1097         AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
1098         AVPN* co = NULL;
1099         AVPN* cs = NULL;
1100         gint  c;
1101         AVP* m;
1102         AVP* copy;
1103
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);
1106 #endif
1107
1108
1109         cs = src->null.next;
1110         co = op->null.next;
1111         while(1) {
1112
1113                 if (!co->avp) {
1114                         return newavpl;
1115                 }
1116
1117                 if (!cs->avp) {
1118                         return newavpl;
1119                 }
1120
1121
1122                 c = (guint) co->avp->n - (guint) cs->avp->n;
1123
1124                 if ( c > 0 ) {
1125                         if (co->avp) co = co->next;
1126                 } else if (c < 0) {
1127                         if (cs->avp) cs = cs->next;
1128                 } else {
1129                         m = match_avp(cs->avp,co->avp);
1130                         if(m) {
1131
1132                                 if (copy_avps) {
1133                                         copy = avp_copy(m);
1134                                         if ( ! insert_avp(newavpl,copy) ) {
1135                                                 delete_avp(copy);
1136                                         }
1137                                 } else {
1138                                         insert_avp(newavpl,m);
1139                                 }
1140
1141
1142                         }
1143
1144                         if (cs->avp) cs = cs->next;
1145
1146                 }
1147         }
1148
1149 #ifdef _AVP_DEBUGGING
1150         dbg_print(dbg_avpl_op,6,dbg_fp,"new_avpl_loose_match: done!");
1151 #endif
1152
1153         return NULL;
1154 }
1155
1156 /* TODO: rename me */
1157 /**
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
1163  *
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
1166  *
1167  * Return value: a pointer to the newly created avpl containing the
1168  *                               matching avps.
1169  **/
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));
1172         AVPN* co = NULL;
1173         AVPN* cs = NULL;
1174         gint c;
1175         AVP* m;
1176         AVP* copy;
1177
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);
1180 #endif
1181
1182         gboolean matches = TRUE;
1183
1184         cs = src->null.next;
1185         co = op->null.next;
1186         while(1) {
1187
1188                 if (!co->avp) {
1189                         break;
1190                 }
1191
1192                 if (!cs->avp) {
1193                         break;
1194                 }
1195
1196                 c = (guint) co->avp->n - (guint) cs->avp->n;
1197
1198                 if ( c > 0 ) {
1199                         delete_avpl(newavpl,TRUE);
1200                         return NULL;
1201                 } else if (c < 0) {
1202                         cs = cs->next;
1203                         if (! cs->avp ) {
1204                                 break;
1205                         }
1206                 } else {
1207                         m = match_avp(cs->avp,co->avp);
1208
1209                         if(m) {
1210                                 matches++;
1211                                 cs = cs->next;
1212                                 co = co->next;
1213
1214                                 if (copy_avps) {
1215                                         copy = avp_copy(m);
1216                                         if ( ! insert_avp(newavpl,copy) ) {
1217                                                 delete_avp(copy);
1218                                         }
1219                                 } else {
1220                                         insert_avp(newavpl,m);
1221                                 }
1222
1223                         } else {
1224                                 cs = cs->next;
1225                         }
1226                 }
1227
1228         }
1229
1230         if (matches) {
1231                 return newavpl;
1232         } else {
1233                 delete_avpl(newavpl,TRUE);
1234                 return NULL;
1235         }
1236 }
1237
1238
1239 /* TODO: rename me */
1240 /**
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
1246  *
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.
1250  *
1251  * Return value: a pointer to the newly created avpl containing the
1252  *                               matching avps.
1253  **/
1254 extern AVPL* new_avpl_exact_match(guint8* name,AVPL* src, AVPL* op, gboolean copy_avps) {
1255         AVPL* newavpl = new_avpl(name);
1256         AVPN* co = NULL;
1257         AVPN* cs = NULL;
1258         gint c;
1259         AVP* m;
1260     AVP* copy;
1261
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);
1264 #endif
1265
1266         cs = src->null.next;
1267         co = op->null.next;
1268         while(1) {
1269
1270                 c = (guint) co->avp->n - (guint) cs->avp->n;
1271
1272                 if ( c > 0 ) {
1273                         delete_avpl(newavpl,TRUE);
1274                         return NULL;
1275                 } else if (c < 0) {
1276                         cs = cs->next;
1277                         if (! cs->avp ) {
1278                                 delete_avpl(newavpl,TRUE);
1279                                 return NULL;
1280                         }
1281                 } else {
1282                         m = match_avp(cs->avp,co->avp);
1283
1284                         if(m) {
1285                                 cs = cs->next;
1286                                 co = co->next;
1287
1288                                 if (copy_avps) {
1289                                         copy = avp_copy(m);
1290                                         if ( ! insert_avp(newavpl,copy) ) {
1291                                                 delete_avp(copy);
1292                                         }
1293                                 } else {
1294                                         insert_avp(newavpl,m);
1295                                 }
1296
1297
1298                                 if (!co->avp) {
1299                                         return newavpl;
1300                                 }
1301                                 if (!cs->avp) {
1302                                         delete_avpl(newavpl,TRUE);
1303                                         return NULL;
1304                                 }
1305                         } else {
1306                                 delete_avpl(newavpl,TRUE);
1307                                 return NULL;
1308                         }
1309                 }
1310
1311         }
1312
1313         /* should never be reached */
1314         return NULL;
1315 }
1316
1317 extern AVPL* new_avpl_from_match(avpl_match_mode mode, guint8* name,AVPL* src, AVPL* op, gboolean copy_avps) {
1318         AVPL* avpl = NULL;
1319         
1320         switch (mode) {
1321                 case AVPL_STRICT:
1322                         avpl = new_avpl_exact_match(name,src,op,copy_avps);
1323                         break;
1324                 case AVPL_LOOSE:
1325                         avpl = new_avpl_loose_match(name,src,op,copy_avps);
1326                         break;
1327                 case AVPL_EVERY:
1328                         avpl = new_avpl_every_match(name,src,op,copy_avps);
1329                         break;
1330                 case AVPL_NO_MATCH:
1331                         avpl = new_avpl_from_avpl(name,src,copy_avps);
1332                         merge_avpl(avpl, op, copy_avps);
1333                         break;
1334         }
1335         
1336         return avpl;
1337 }
1338
1339 /**
1340  * new_avpl_transform:
1341  *
1342  * creates an empty avpl transformation
1343  *
1344  * Return value: a pointer to the newly created avpl transformation
1345  **/
1346 extern AVPL_Transf* new_avpl_transform(guint8* name, AVPL* mixed, avpl_match_mode match_mode, avpl_replace_mode replace_mode) {
1347         AVPL_Transf* t = g_malloc(sizeof(AVPL_Transf));
1348         AVP* avp;
1349
1350         t->name = g_strdup(name);
1351         t->match = new_avpl("match");
1352         t->replace = new_avpl("replace");
1353         t->match_mode = match_mode;
1354         t->replace_mode  = replace_mode;
1355         t->next = NULL;
1356         t->map = NULL;
1357
1358         while (( avp = extract_first_avp(mixed) )) {
1359                 if (*(avp->n) == '.') {
1360                         rename_avp(avp,(avp->n+1));
1361                         insert_avp(t->replace, avp);
1362                 } else {
1363                         insert_avp(t->match, avp);
1364                 }
1365         }
1366
1367         return t;
1368 }
1369
1370
1371 /**
1372  * delete_avpl_transform:
1373  * @it: a pointer to the avpl transformation object
1374  *
1375  * Destroys an avpl transformation object and releases all the resources it
1376  * uses.
1377  *
1378  **/
1379 extern void delete_avpl_transform(AVPL_Transf* op) {
1380         AVPL_Transf* next;
1381
1382         for (; op ; op = next) {
1383                 next = op->next;
1384
1385                 g_free(op->name);
1386
1387                 if (op->match) {
1388                         delete_avpl(op->match,TRUE);
1389                 }
1390
1391                 if (op->replace) {
1392                         delete_avpl(op->replace,TRUE);
1393                 }
1394
1395                 g_free(op);
1396         }
1397
1398 }
1399
1400
1401 /**
1402  * avpl_transform:
1403  * @src: the source avpl for the transform operation.
1404  * @op: a pointer to the avpl transformation object to apply.
1405  *
1406  * Applies the "op" transformation to an avpl, matches it and eventually
1407  * replaces or inserts the transformed avps.
1408  *
1409  * Return value: whether the transformation was performed or not.
1410  **/
1411 extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
1412         AVPL* avpl = NULL;
1413         AVPN* cs;
1414         AVPN* cm;
1415         AVPN* n;
1416         gboolean d;
1417
1418 #ifdef _AVP_DEBUGGING
1419         dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%X op=%X",src,op);
1420 #endif
1421
1422         for ( ; op ; op = op->next) {
1423                 
1424                 avpl = new_avpl_from_match(op->match_mode, src->name,src, op->match, TRUE);
1425
1426                 if (avpl) {
1427                         switch (op->replace_mode) {
1428                                 case AVPL_NO_REPLACE:
1429                                         delete_avpl(avpl,TRUE);
1430                                         return;
1431                                 case AVPL_INSERT:
1432                                         merge_avpl(src,op->replace,TRUE);
1433                                         delete_avpl(avpl,TRUE);
1434                                         return;
1435                                 case AVPL_REPLACE:
1436                                         cs = src->null.next;
1437                                         cm = avpl->null.next;
1438                                         d = FALSE;
1439                                         while(cs->avp) {
1440                                                 if (cs->avp == cm->avp) {
1441                                                         n = cs->next;
1442
1443                                                         cs->prev->next = cs->next;
1444                                                         cs->next->prev = cs->prev;
1445                                                         g_mem_chunk_free(avp_chunk,cs);
1446
1447                                                         cs = n;
1448                                                         cm = cm->next;
1449                                                 } else {
1450                                                         cs = cs->next;
1451                                                 }
1452                                         }
1453
1454                                                 merge_avpl(src,avpl,TRUE);
1455                                         delete_avpl(avpl,TRUE);
1456                                         return;
1457                         }
1458                 }
1459         }
1460 }
1461
1462
1463 /**
1464  * new_loal:
1465  * @name: the name the loal will take.
1466  *
1467  * Creates an empty list of avp lists.
1468  *
1469  * Return value: a pointer to the newly created loal.
1470  **/
1471 extern LoAL* new_loal(guint8* name) {
1472         LoAL* new_loal = g_mem_chunk_alloc(avp_chunk);
1473
1474         if (! name) {
1475                 name = "anonymous";
1476         }
1477
1478 #ifdef _AVP_DEBUGGING
1479         dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal: %X name=%s",new_loal,name);
1480 #endif
1481
1482         new_loal->name = scs_subscribe(avp_strings,name);
1483         new_loal->null.avpl = NULL;
1484         new_loal->null.next = &new_loal->null;
1485         new_loal->null.prev = &new_loal->null;
1486
1487         return new_loal;
1488 }
1489
1490 /**
1491  * loal_append:
1492  * @loal: the loal on which to operate.
1493  * @avpl: the avpl to append.
1494  *
1495  * Appends an avpl to a loal.
1496  *
1497  **/
1498 extern void loal_append(LoAL* loal, AVPL* avpl) {
1499         LoALnode* node = g_mem_chunk_alloc(avp_chunk);
1500
1501 #ifdef _AVP_DEBUGGING
1502         dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %X",node);
1503 #endif
1504
1505         node->avpl = avpl;
1506         node->next = &loal->null;
1507         node->prev = loal->null.prev;
1508
1509         loal->null.prev->next = node;
1510         loal->null.prev = node;
1511 }
1512
1513
1514 /**
1515  * extract_first_avpl:
1516  * @loal: the loal on which to operate.
1517  *
1518  * Extracts the first avpl contained in a loal.
1519  *
1520  * Return value: a pointer to the extracted avpl.
1521  *
1522  **/
1523 extern AVPL* extract_first_avpl(LoAL* loal) {
1524         LoALnode* node;
1525         AVPL* avpl;
1526
1527 #ifdef _AVP_DEBUGGING
1528         dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name);
1529 #endif
1530
1531         node = loal->null.next;
1532
1533         loal->null.next->next->prev = &loal->null;
1534         loal->null.next = node->next;
1535
1536         loal->len--;
1537
1538         avpl = node->avpl;
1539
1540         if ( avpl ) {
1541                 g_mem_chunk_free(avp_chunk,node);
1542
1543 #ifdef _AVP_DEBUGGING
1544                 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name);
1545                 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
1546 #endif
1547         }
1548
1549         return avpl;
1550 }
1551
1552 /**
1553 * extract_first_avpl:
1554  * @loal: the loal on which to operate.
1555  *
1556  * Extracts the last avpl contained in a loal.
1557  *
1558  * Return value: a pointer to the extracted avpl.
1559  *
1560  **/
1561 extern AVPL* extract_last_avpl(LoAL* loal){
1562         LoALnode* node;
1563         AVPL* avpl;
1564
1565         node = loal->null.prev;
1566
1567         loal->null.prev->prev->next = &loal->null;
1568         loal->null.prev = node->prev;
1569
1570         loal->len--;
1571
1572         avpl = node->avpl;
1573
1574         if ( avpl ) {
1575                 g_mem_chunk_free(avp_chunk,node);
1576 #ifdef _AVP_DEBUGGING
1577                 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
1578 #endif
1579         }
1580
1581         return avpl;
1582 }
1583
1584 /**
1585  * extract_first_avpl:
1586  * @loal: the loal on which to operate.
1587  * @cookie pointer to the pointer variable to contain the state between calls
1588  *
1589  * At each call will return the following avpl from a loal. The given cookie
1590  * will be used to manatain the state between calls.
1591  *
1592  * Return value: a pointer to the next avpl.
1593  *
1594  **/
1595 extern AVPL* get_next_avpl(LoAL* loal,void** cookie) {
1596         LoALnode* node;
1597
1598 #ifdef _AVP_DEBUGGING
1599         dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%X node=%X",loal,*cookie);
1600 #endif
1601
1602         if (*cookie) {
1603                 node = (LoALnode*) *cookie;
1604         } else {
1605                 node = loal->null.next;
1606         }
1607
1608         *cookie = node->next;
1609
1610         return node->avpl;
1611 }
1612
1613 /**
1614  * delete_loal:
1615  * @loal: the loal to be deleted.
1616  * @avpls_too: whether avpls contained by the loal should be deleted as well
1617  * @avps_too: whether avps contained by the avpls should be also deleted
1618  *
1619  * Destroys a loal and eventually desstroys avpls and avps.
1620  *
1621  **/
1622 extern void delete_loal(LoAL* loal, gboolean avpls_too, gboolean avps_too) {
1623         AVPL* avpl;
1624
1625 #ifdef _AVP_DEBUGGING
1626         dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %X",loal);
1627 #endif
1628
1629         while(( avpl = extract_last_avpl(loal) )) {
1630                 if (avpls_too) {
1631                         delete_avpl(avpl,avps_too);
1632                 }
1633         }
1634
1635         scs_unsubscribe(avp_strings,loal->name);
1636         g_mem_chunk_free(avp_chunk,loal);
1637 }
1638
1639
1640
1641 /****************************************************************************
1642  ******************* the following are used in load_loal_from_file
1643  ****************************************************************************/
1644
1645 /**
1646  * load_loal_error:
1647  * Used by loal_from_file to handle errors while loading.
1648  **/
1649 void load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, guint8* fmt, ...) {
1650         va_list list;
1651         guint8* desc;
1652
1653
1654         va_start( list, fmt );
1655         desc = g_strdup_vprintf(fmt, list);
1656         va_end( list );
1657
1658         if (fp) fclose(fp);
1659         if (loal) delete_loal(loal,TRUE,TRUE);
1660         if (curr) delete_avpl(curr,TRUE);
1661
1662         g_warning("Error Loading LoAL from file: at line: %i, %s",linenum,desc);
1663         g_free(desc);
1664
1665         return;
1666 }
1667
1668
1669 /*  the maximum lenght allowed for a line */
1670 #define MAX_ITEM_LEN    8192
1671
1672 /* this two ugly things are used for tokenizing */
1673 #define AVP_OP_CHAR '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
1674
1675 #define AVP_NAME_CHAR 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
1676 case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
1677 case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
1678 case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
1679 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
1680 case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
1681 case '7': case '8': case '9': case '.'
1682
1683
1684 /**
1685  * loal_from_file:
1686  * @filename: the file containing a loals text representation.
1687  *
1688  * Given a filename it will attempt to load a loal containing a copy of
1689  * the avpls represented in the file.
1690  *
1691  * Return value: if successful a pointer to the new populated loal, else NULL.
1692  *
1693  **/
1694 extern LoAL* loal_from_file(guint8* filename) {
1695         FILE *fp;
1696         guint8 c;
1697         int i = 0;
1698         guint32 linenum = 1;
1699         guint8 name[MAX_ITEM_LEN];
1700         guint8 value[MAX_ITEM_LEN];
1701         guint8 op = '?';
1702         LoAL *loal = new_loal(filename);
1703         AVPL* curr = NULL;
1704         AVP* avp;
1705
1706         enum _load_loal_states {
1707                 START,
1708                 BEFORE_NAME,
1709                 IN_NAME,
1710                 IN_VALUE,
1711                 MY_IGNORE
1712         } state;
1713
1714 #ifndef _WIN32
1715         if (! getuid()) {
1716                 g_warning( "MATE Will not run as root");
1717                 return NULL;
1718         }
1719 #endif
1720
1721         state = START;
1722
1723         if (( fp = fopen(filename,"r") )) {
1724                 while(( c = (guint8) fgetc(fp) )){
1725
1726                         if ( feof(fp) ) {
1727                                 if ( ferror(fp) ) {
1728                                         load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename);
1729                                         return NULL;
1730                                 }
1731                                 break;
1732                         }
1733
1734                         if ( c == '\n' ) {
1735                                 linenum++;
1736                         }
1737
1738                         if ( i >= MAX_ITEM_LEN - 1  ) {
1739                                 load_loal_error(fp,loal,curr,linenum,"Maximum item lenght exceeded");
1740                                 return NULL;
1741                         }
1742
1743                         switch(state) {
1744                                 case MY_IGNORE:
1745                                         switch (c) {
1746                                                 case '\n':
1747                                                         state = START;
1748                                                         i = 0;
1749                                                         continue;
1750                                                 default:
1751                                                         continue;
1752                                         }
1753                                         continue;
1754                                 case START:
1755                                         switch (c) {
1756                                                 case ' ': case '\t':
1757                                                         /* ignore whitespace at line start */
1758                                                         continue;
1759                                                 case '\n':
1760                                                         /* ignore empty lines */
1761                                                         i = 0;
1762                                                         continue;
1763                                                 case AVP_NAME_CHAR:
1764                                                         state = IN_NAME;
1765                                                         i = 0;
1766                                                         name[i++] = c;
1767                                                         name[i] = '\0';
1768
1769                                                         curr = new_avpl("");
1770                                                         continue;
1771                                                 case '#':
1772                                                         state = MY_IGNORE;
1773                                                         continue;
1774                                                 default:
1775                                                         load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1776                                                         return NULL;
1777                                         }
1778                                 case BEFORE_NAME:
1779                                         i = 0;
1780                                         name[0] = '\0';
1781                                         switch (c) {
1782                                                 case '\\':
1783                                                         c = fgetc(fp);
1784                                                         if (c != '\n') ungetc(c,fp);
1785                                                         continue;
1786                                                 case ' ':
1787                                                 case '\t':
1788                                                         continue;
1789                                                 case AVP_NAME_CHAR:
1790                                                         state = IN_NAME;
1791
1792                                                         name[i++] = c;
1793                                                         name[i] = '\0';
1794                                                         continue;
1795                                                 case '\n':
1796                                                         loal_append(loal,curr);
1797                                                         state = START;
1798                                                         continue;
1799                                                 default:
1800                                                         load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1801                                                         return NULL;
1802                                         }
1803                                         case IN_NAME:
1804                                                 switch (c) {
1805                                                         case ';':
1806                                                                 state = BEFORE_NAME;
1807
1808                                                                 op = '?';
1809                                                                 name[i] = '\0';
1810                                                                 value[0] = '\0';
1811                                                                 i = 0;
1812
1813                                                                 avp = new_avp(name,value,op);
1814
1815                                                                 if (! insert_avp(curr,avp) ) {
1816                                                                         delete_avp(avp);
1817                                                                 }
1818
1819                                                                 continue;
1820                                                         case AVP_OP_CHAR:
1821                                                                 name[i] = '\0';
1822                                                                 i = 0;
1823                                                                 op = c;
1824                                                                 state = IN_VALUE;
1825                                                                 continue;
1826                                                         case AVP_NAME_CHAR:
1827                                                                 name[i++] = c;
1828                                                                 continue;
1829                                                         case '\n':
1830                                                                 load_loal_error(fp,loal,curr,linenum,"operator expected found new line");
1831                                                                 return NULL;
1832                                                         default:
1833                                                                 load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c);
1834                                                                 return NULL;
1835                                                 }
1836                                         case IN_VALUE:
1837                                                 switch (c) {
1838                                                         case '\\':
1839                                                                 value[i++] = fgetc(fp);
1840                                                                 continue;
1841                                                         case ';':
1842                                                                 state = BEFORE_NAME;
1843
1844                                                                 value[i] = '\0';
1845                                                                 i = 0;
1846
1847                                                                 avp = new_avp(name,value,op);
1848
1849                                                                 if (! insert_avp(curr,avp) ) {
1850                                                                         delete_avp(avp);
1851                                                                 }
1852                                                                 continue;
1853                                                         case '\n':
1854                                                                 load_loal_error(fp,loal,curr,linenum,"';' expected found new line");
1855                                                                 return NULL;
1856                                                         default:
1857                                                                 value[i++] = c;
1858                                                                 continue;
1859                                                 }
1860                         }
1861                 }
1862                 fclose (fp);
1863
1864                 return loal;
1865
1866         } else {
1867                 load_loal_error(NULL,loal,NULL,0,"Cannot Open file '%s'",filename);
1868                 return NULL;
1869         }
1870 }