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