name change
[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 * 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 <wiretap/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 static void rename_avp(AVP* avp, gchar* name) {
433         gchar* s = avp->n;
434         avp->n = scs_subscribe(avp_strings,name);
435         scs_unsubscribe(avp_strings,s);
436 }
437
438 /**
439  * new_avpl:
440  * @name: the name the avpl will have.
441  *
442  * Creates an empty avpl.
443  *
444  * Return value: a pointer to the newly created avpl.
445  *
446  **/
447 extern AVPL* new_avpl(const gchar* name) {
448         AVPL* new_avpl = g_mem_chunk_alloc(avp_chunk);
449
450 #ifdef _AVP_DEBUGGING
451         dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpl: %X name=%s",new_avpl,name);
452 #endif
453
454         new_avpl->name = name ? scs_subscribe(avp_strings, name) : scs_subscribe(avp_strings, "");
455         new_avpl->len = 0;
456         new_avpl->null.avp = NULL;
457         new_avpl->null.next = &new_avpl->null;
458         new_avpl->null.prev = &new_avpl->null;
459
460
461         return new_avpl;
462 }
463
464 extern void rename_avpl(AVPL* avpl, gchar* name) {
465         scs_unsubscribe(avp_strings,avpl->name);
466         avpl->name = scs_subscribe(avp_strings,name);
467 }
468
469 /**
470  * insert_avp:
471  * @avpl: the avpl in which to insert.
472  * @avp: the avp to be inserted.
473  *
474  * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
475  *
476  * Return value: whether it was inserted or not.
477  *
478  * BEWARE: Check the return value, you might need to delete the avp if
479  *         it is not inserted.
480  **/
481 extern gboolean insert_avp(AVPL* avpl, AVP* avp) {
482         AVPN* new = g_mem_chunk_alloc(avp_chunk);
483         AVPN* c;
484
485         new->avp = avp;
486
487 #ifdef _AVP_DEBUGGING
488         dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %X",new);
489         dbg_print(dbg_avpl_op,4,dbg_fp,"insert_avp: %X %X %s%c%s;",avpl,avp,avp->n,avp->o,avp->v);
490 #endif
491
492         /* get to the insertion point */
493         for(c=avpl->null.next; c->avp; c = c->next) {
494
495                 if ( avp->n == c->avp->n ) {
496
497                         if (avp->v > c->avp->v) {
498                                 break;
499                         }
500
501                         if (avp->v == c->avp->v) {
502                                 if (avp->o == AVP_OP_EQUAL) {
503 #ifdef _AVP_DEBUGGING
504                                         dbg_print(dbg_avpl_op,7,dbg_fp,"delete_avpn: %X",new);
505 #endif
506                                         g_mem_chunk_free(avp_chunk,new);
507                                         return FALSE;
508                                 }
509                         }
510                 }
511
512                 if (avp->n > c->avp->n) {
513                         break;
514                 }
515         }
516
517 #ifdef _AVP_DEBUGGING
518         dbg_print(dbg_avpl,5,dbg_fp,"insert_avp:  inserting %X in %X before %X;",avp,avpl,c);
519 #endif
520
521         new->next = c;
522         new->prev = c->prev;
523         c->prev->next = new;
524         c->prev = new;
525
526         avpl->len++;
527
528 #ifdef _AVP_DEBUGGING
529         dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
530 #endif
531
532         return TRUE;
533 }
534
535 /**
536  * get_avp_by_name:
537  * @avpl: the avpl from which to try to get the avp.
538  * @name: the name of the avp we are looking for.
539  * @cookie: variable in which to store the state between calls.
540  *
541  * Gets  pointer to the next avp whose name is given; uses cookie to store its
542  * state between calls.
543  *
544  * Return value: a pointer to the next matching avp if there's one, else NULL.
545  *
546  **/
547 extern AVP* get_avp_by_name(AVPL* avpl, gchar* name, void** cookie) {
548         AVPN* curr;
549         AVPN* start = (AVPN*) *cookie;
550
551 #ifdef _AVP_DEBUGGING
552         dbg_print(dbg_avpl_op,7,dbg_fp,"get_avp_by_name: entering: %X %s %X",avpl,name,*cookie);
553 #endif
554
555         name = scs_subscribe(avp_strings, name);
556
557         if (!start) start = avpl->null.next;
558
559         for ( curr = start; curr->avp; curr = curr->next ) {
560                 if ( curr->avp->n == name ) {
561                         break;
562                 }
563         }
564
565         *cookie = curr;
566
567 #ifdef _AVP_DEBUGGING
568         dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %X",curr);
569 #endif
570
571         scs_unsubscribe(avp_strings, name);
572
573         return curr->avp;
574 }
575
576 /**
577  * extract_avp_by_name:
578  * @avpl: the avpl from which to try to extract the avp.
579  * @name: the name of the avp we are looking for.
580  *
581  * Extracts from the avpl the next avp whose name is given;
582  *
583  * Return value: a pointer to extracted avp if there's one, else NULL.
584  *
585  **/
586 extern AVP* extract_avp_by_name(AVPL* avpl, gchar* name) {
587         AVPN* curr;
588         AVP* avp = NULL;
589
590 #ifdef _AVP_DEBUGGING
591         dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %X %s",avpl,name);
592 #endif
593
594         name = scs_subscribe(avp_strings, name);
595
596         for ( curr = avpl->null.next; curr->avp; curr = curr->next ) {
597                 if ( curr->avp->n == name ) {
598                         break;
599                 }
600         }
601
602         scs_unsubscribe(avp_strings, name);
603
604         if( ! curr->avp ) return NULL;
605
606         curr->next->prev = curr->prev;
607         curr->prev->next = curr->next;
608
609         avp = curr->avp;
610
611         g_mem_chunk_free(avp_chunk,curr);
612
613         (avpl->len)--;
614
615 #ifdef _AVP_DEBUGGING
616         dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
617 #endif
618
619 #ifdef _AVP_DEBUGGING
620         dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %X",avp);
621 #endif
622
623         return avp;
624 }
625
626
627 /**
628  * extract_first_avp:
629  * @avpl: the avpl from which to try to extract the avp.
630  *
631  * Extracts the fisrt avp from the avpl.
632  *
633  * Return value: a pointer to extracted avp if there's one, else NULL.
634  *
635  **/
636 extern AVP* extract_first_avp(AVPL* avpl) {
637         AVP* avp;
638         AVPN* node;
639
640 #ifdef _AVP_DEBUGGING
641         dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %X",avpl);
642 #endif
643
644         node = avpl->null.next;
645
646         avpl->null.next->prev = &avpl->null;
647         avpl->null.next = node->next;
648
649         avp = node->avp;
650
651         if (avp) {
652                 g_mem_chunk_free(avp_chunk,node);
653                 (avpl->len)--;
654 #ifdef _AVP_DEBUGGING
655                 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
656 #endif
657         }
658
659 #ifdef _AVP_DEBUGGING
660         dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %X",avp);
661 #endif
662
663         return avp;
664
665 }
666
667
668 /**
669  * extract_last_avp:
670  * @avpl: the avpl from which to try to extract the avp.
671  *
672  * Extracts the last avp from the avpl.
673  *
674  * Return value: a pointer to extracted avp if there's one, else NULL.
675  *
676  **/
677 extern AVP* extract_last_avp(AVPL* avpl) {
678         AVP* avp;
679         AVPN* node;
680
681         node = avpl->null.prev;
682
683         avpl->null.prev->next = &avpl->null;
684         avpl->null.prev = node->prev;
685
686         avp = node->avp;
687
688         if (avp) {
689                 g_mem_chunk_free(avp_chunk,node);
690                 (avpl->len)--;
691 #ifdef _AVP_DEBUGGING
692                 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
693 #endif
694         }
695
696 #ifdef _AVP_DEBUGGING
697         dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",avp);
698 #endif
699
700         return avp;
701
702 }
703
704
705 /**
706  * delete_avpl:
707  * @avpl: the avpl from which to try to extract the avp.
708  * @avps_too: whether or not it should delete the avps as well.
709  *
710  * Destroys an avpl and releases the resources it uses. If told to do
711  * so releases the avps as well.
712  *
713  **/
714 extern void delete_avpl(AVPL* avpl, gboolean avps_too) {
715         AVP* avp;
716 #ifdef _AVP_DEBUGGING
717         dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %X",avpl);
718 #endif
719
720         while(( avp = extract_last_avp(avpl))) {
721                 if (avps_too) {
722                         delete_avp(avp);
723                 }
724         }
725
726         scs_unsubscribe(avp_strings,avpl->name);
727         g_mem_chunk_free(avp_chunk,avpl);
728 }
729
730
731
732 /**
733  * get_next_avp:
734  * @avpl: the avpl from which to try to get the avps.
735  * @cookie: variable in which to store the state between calls.
736  *
737  * Iterates on an avpl to get its avps.
738  *
739  * Return value: a pointer to the next avp if there's one, else NULL.
740  *
741  **/
742 extern AVP* get_next_avp(AVPL* avpl, void** cookie) {
743         AVPN* node;
744
745 #ifdef _AVP_DEBUGGING
746         dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %X avpn: %X",avpl,*cookie);
747 #endif
748
749         if (*cookie) {
750                 node = (AVPN*) *cookie;
751         } else {
752                 node = avpl->null.next;
753         }
754
755         *cookie = node->next;
756
757 #ifdef _AVP_DEBUGGING
758         dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",node->avp);
759 #endif
760
761         return node->avp;
762 }
763
764 /**
765  * avpl_to_str:
766  * @avpl: the avpl to represent.
767  *
768  * Creates a newly allocated string containing a representation of an avpl.
769  *
770  * Return value: a pointer to the newly allocated string.
771  *
772  **/
773 gchar* avpl_to_str(AVPL* avpl) {
774         AVPN* c;
775         GString* s = g_string_new("");
776         gchar* avp_s;
777         gchar* r;
778
779         for(c=avpl->null.next; c->avp; c = c->next) {
780                 avp_s = avp_to_str(c->avp);
781                 g_string_sprintfa(s," %s;",avp_s);
782                 g_free(avp_s);
783         }
784
785         r = s->str;
786         g_string_free(s,FALSE);
787
788         /* g_strchug(r); ? */
789         return r;
790 }
791
792 extern gchar* avpl_to_dotstr(AVPL* avpl) {
793         AVPN* c;
794         GString* s = g_string_new("");
795         gchar* avp_s;
796         gchar* r;
797
798         for(c=avpl->null.next; c->avp; c = c->next) {
799                 avp_s = avp_to_str(c->avp);
800                 g_string_sprintfa(s," .%s;",avp_s);
801                 g_free(avp_s);
802         }
803
804         r = s->str;
805         g_string_free(s,FALSE);
806
807         /* g_strchug(r); ? */
808         return r;
809 }
810
811 /**
812 * merge_avpl:
813  * @dst: the avpl in which to merge the avps.
814  * @src: the avpl from which to get the avps.
815  * @copy: whether avps should be copied instead of referenced.
816  *
817  * Adds the avps of src that are not existent in dst into dst.
818  *
819  * Return value: a pointer to the newly allocated string.
820  *
821  **/
822 extern void merge_avpl(AVPL* dst, AVPL* src, gboolean copy_avps) {
823         AVPN* cd = NULL;
824         AVPN* cs = NULL;
825         gint c;
826         AVP* copy;
827
828 #ifdef _AVP_DEBUGGING
829         dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %X %X",dst,src);
830 #endif
831
832         cs = src->null.next;
833         cd = dst->null.next;
834
835         while(cs->avp) {
836
837                 if(cd->avp) {
838                         c = (guint) cd->avp->n - (guint) cs->avp->n;
839                 } else {
840                         c = -1;
841                 }
842
843                 if (c > 0) {
844                         if (cd->avp) cd = cd->next;
845                 } else if (c < 0) {
846                         if (copy_avps) {
847                                 copy = avp_copy(cs->avp);
848                                 if ( ! insert_avp(dst,copy) ) {
849                                         delete_avp(copy);
850                                 }
851                         } else {
852                                 insert_avp(dst,cs->avp);
853                         }
854
855                         cs = cs->next;
856                 } else {
857                         if ( ! cd->avp || ! (cd->avp->v == cs->avp->v)  ) {
858                                 if (copy_avps) {
859                                         copy = avp_copy(cs->avp);
860                                         if ( ! insert_avp(dst,copy) ) {
861                                                 delete_avp(copy);
862                                         }
863                                 } else {
864                                         insert_avp(dst,cs->avp);
865                                 }
866                         }
867                         cs = cs->next;
868                         if (cd->avp) cd = cd->next;
869                 }
870         }
871
872 #ifdef _AVP_DEBUGGING
873         dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
874 #endif
875
876         return;
877 }
878
879
880 /**
881  * merge_avpl:
882  * @name: the name of the new avpl.
883  * @avpl: the avpl from which to get the avps.
884  * @copy_avps: whether avps should be copied instead of referenced.
885  *
886  * Creates a new avpl containing the same avps as the given avpl
887  * It will either reference or copie the avps.
888  *
889  * Return value: a pointer to the newly allocated string.
890  *
891  **/
892 extern AVPL* new_avpl_from_avpl(const gchar* name, AVPL* avpl, gboolean copy_avps) {
893         AVPL* newavpl = new_avpl(name);
894         void* cookie = NULL;
895         AVP* avp;
896         AVP* copy;
897
898 #ifdef _AVP_DEBUGGING
899         dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_from_avpl: %X from=%X name='%s'",newavpl,avpl,name);
900 #endif
901
902         while(( avp = get_next_avp(avpl,&cookie) )) {
903                 if (copy_avps) {
904                         copy = avp_copy(avp);
905                         if ( ! insert_avp(newavpl,copy) ) {
906                                 delete_avp(copy);
907                         }
908                 } else {
909                         insert_avp(newavpl,avp);
910                 }
911         }
912
913 #ifdef _AVP_DEBUGGING
914         dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done");
915 #endif
916
917         return newavpl;
918 }
919
920 /**
921 * match_avp:
922  * @src: an src to be compared agains an "op" avp
923  * @op: the "op" avp that will be matched against the src avp
924  *
925  * Checks whether or not two avp's match.
926  *
927  * Return value: a pointer to the src avp if there's a match.
928  *
929  **/
930 extern AVP* match_avp(AVP* src, AVP* op) {
931         gchar** splited;
932         int i;
933         gchar* p;
934         guint ls;
935         guint lo;
936         float fs = 0.0;
937         float fo = 0.0;
938         gboolean lower = FALSE;
939
940 #ifdef _AVP_DEBUGGING
941         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);
942 #endif
943
944         if ( src->n != op->n ) {
945                 return NULL;
946         }
947
948         switch (op->o) {
949                 case AVP_OP_EXISTS:
950                         return src;
951                 case AVP_OP_EQUAL:
952                         return src->v == op->v ? src : NULL;
953                 case AVP_OP_NOTEQUAL:
954                         return !( src->v == op->v) ? src : NULL;
955                 case AVP_OP_STARTS:
956                         return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL;
957                 case AVP_OP_ONEOFF:
958                         splited = g_strsplit(op->v,"|",0);
959                         if (splited) {
960                                 for (i=0;splited[i];i++) {
961                                         if(g_str_equal(splited[i],src->v)) {
962                                                 g_strfreev(splited);
963                                                 return src;
964                                         }
965                                 }
966                                 g_strfreev(splited);
967                         }
968                         return NULL;
969
970                 case AVP_OP_LOWER:
971                         lower = TRUE;
972                 case AVP_OP_HIGHER:
973
974                         fs = (float) strtod(src->v, NULL);
975                         fo = (float) strtod(src->v, NULL);
976
977                         if (lower) {
978                                 if (fs<fo) return src;
979                                 else return NULL;
980                         } else {
981                                 if (fs>fo) return src;
982                                 else return NULL;
983                         }
984                 case AVP_OP_ENDS:
985                         /* does this work? */
986                         ls = strlen(src->v);
987                         lo = strlen(op->v);
988
989                         if ( ls < lo ) {
990                                 return NULL;
991                         } else {
992                                 p = src->v + ( ls - lo );
993                                 return g_str_equal(p,op->v) ? src : NULL;
994                         }
995
996                 /* case AVP_OP_TRANSF: */
997                 /*      return do_transform(src,op); */
998                 case AVP_OP_CONTAINS:
999                         /* TODO */
1000                         return NULL;
1001         }
1002         /* will never get here */
1003         return NULL;
1004 }
1005
1006
1007
1008 /* TODO: rename me */
1009 /**
1010  * new_avpl_loose_match:
1011  * @name: the name of the resulting avpl
1012  * @src: avpl to be matched agains an "op" avpl
1013  * @op: the "op" avpl that will be matched against the src avpl
1014  * @copy_avps: whether the avps in the resulting avpl should be copied
1015  *
1016  * creates an avp list containing any avps in src matching any avps in op
1017  * it will eventually create an empty list in none match
1018  *
1019  * Return value: a pointer to the newly created avpl containing the
1020  *                               matching avps.
1021  **/
1022 extern AVPL* new_avpl_loose_match(const gchar* name,
1023                                                                   AVPL* src,
1024                                                                   AVPL* op,
1025                                                                   gboolean copy_avps) {
1026
1027         AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
1028         AVPN* co = NULL;
1029         AVPN* cs = NULL;
1030         gint  c;
1031         AVP* m;
1032         AVP* copy;
1033
1034 #ifdef _AVP_DEBUGGING
1035         dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_loose_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
1036 #endif
1037
1038
1039         cs = src->null.next;
1040         co = op->null.next;
1041         while(1) {
1042
1043                 if (!co->avp) {
1044                         return newavpl;
1045                 }
1046
1047                 if (!cs->avp) {
1048                         return newavpl;
1049                 }
1050
1051
1052                 c = (guint) co->avp->n - (guint) cs->avp->n;
1053
1054                 if ( c > 0 ) {
1055                         if (co->avp) co = co->next;
1056                 } else if (c < 0) {
1057                         if (cs->avp) cs = cs->next;
1058                 } else {
1059                         m = match_avp(cs->avp,co->avp);
1060                         if(m) {
1061
1062                                 if (copy_avps) {
1063                                         copy = avp_copy(m);
1064                                         if ( ! insert_avp(newavpl,copy) ) {
1065                                                 delete_avp(copy);
1066                                         }
1067                                 } else {
1068                                         insert_avp(newavpl,m);
1069                                 }
1070
1071
1072                         }
1073
1074                         if (cs->avp) cs = cs->next;
1075
1076                 }
1077         }
1078
1079 #ifdef _AVP_DEBUGGING
1080         dbg_print(dbg_avpl_op,6,dbg_fp,"new_avpl_loose_match: done!");
1081 #endif
1082
1083         return NULL;
1084 }
1085
1086 /* TODO: rename me */
1087 /**
1088 * new_avpl_every_match:
1089  * @name: the name of the resulting avpl
1090  * @src: avpl to be matched agains an "op" avpl
1091  * @op: the "op" avpl that will be matched against the src avpl
1092  * @copy_avps: whether the avps in the resulting avpl should be copied
1093  *
1094  * creates an avp list containing any avps in src matching every avp in op
1095  * it will not create a list if there is not a match for every attribute in op
1096  *
1097  * Return value: a pointer to the newly created avpl containing the
1098  *                               matching avps.
1099  **/
1100 extern AVPL* new_avpl_every_match(const gchar* name, AVPL* src, AVPL* op, gboolean copy_avps) {
1101         AVPL* newavpl;
1102         AVPN* co = NULL;
1103         AVPN* cs = NULL;
1104         gint c;
1105         AVP* m;
1106         AVP* copy;
1107         gboolean matches;
1108         
1109 #ifdef _AVP_DEBUGGING
1110         dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_every_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
1111 #endif
1112         if (src->len == 0) return NULL;
1113         
1114         newavpl = new_avpl(scs_subscribe(avp_strings, name));
1115         
1116         if (op->len == 0)
1117                 return newavpl;
1118         
1119         matches = TRUE;
1120
1121         cs = src->null.next;
1122         co = op->null.next;
1123         while(1) {
1124
1125                 if (!co->avp) {
1126                         break;
1127                 }
1128
1129                 if (!cs->avp) {
1130                         break;
1131                 }
1132
1133                 c = (guint) co->avp->n - (guint) cs->avp->n;
1134
1135                 if ( c > 0 ) {
1136                         delete_avpl(newavpl,TRUE);
1137                         return NULL;
1138                 } else if (c < 0) {
1139                         cs = cs->next;
1140                         if (! cs->avp ) {
1141                                 break;
1142                         }
1143                 } else {
1144                         m = match_avp(cs->avp,co->avp);
1145
1146                         if(m) {
1147                                 matches++;
1148                                 cs = cs->next;
1149                                 co = co->next;
1150
1151                                 if (copy_avps) {
1152                                         copy = avp_copy(m);
1153                                         if ( ! insert_avp(newavpl,copy) ) {
1154                                                 delete_avp(copy);
1155                                         }
1156                                 } else {
1157                                         insert_avp(newavpl,m);
1158                                 }
1159
1160                         } else {
1161                                 cs = cs->next;
1162                         }
1163                 }
1164
1165         }
1166
1167         if (matches) {
1168                 return newavpl;
1169         } else {
1170                 delete_avpl(newavpl,TRUE);
1171                 return NULL;
1172         }
1173 }
1174
1175
1176 /* TODO: rename me */
1177 /**
1178  * new_avpl_exact_match:
1179  * @name: the name of the resulting avpl
1180  * @src: avpl to be matched agains an "op" avpl
1181  * @op: the "op" avpl that will be matched against the src avpl
1182  * @copy_avps: whether the avps in the resulting avpl should be copied
1183  *
1184  * creates an avp list containing every avp in src matching every avp in op
1185  * it will not create a list unless every avp in op is matched only once
1186  * to every avp in op.
1187  *
1188  * Return value: a pointer to the newly created avpl containing the
1189  *                               matching avps.
1190  **/
1191 extern AVPL* new_avpl_exact_match(const gchar* name,AVPL* src, AVPL* op, gboolean copy_avps) {
1192         AVPL* newavpl = new_avpl(name);
1193         AVPN* co = NULL;
1194         AVPN* cs = NULL;
1195         gint c;
1196         AVP* m;
1197     AVP* copy;
1198
1199 #ifdef _AVP_DEBUGGING
1200         dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_every_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
1201 #endif
1202
1203         if (op->len == 0)
1204                 return newavpl;
1205         
1206         if (src->len == 0) {
1207                 delete_avpl(newavpl,FALSE);
1208                 return NULL;
1209         }
1210
1211         cs = src->null.next;
1212         co = op->null.next;
1213         while(1) {
1214
1215                 c = (guint) co->avp->n - (guint) cs->avp->n;
1216
1217                 if ( c > 0 ) {
1218                         delete_avpl(newavpl,TRUE);
1219                         return NULL;
1220                 } else if (c < 0) {
1221                         cs = cs->next;
1222                         if (! cs->avp ) {
1223                                 delete_avpl(newavpl,TRUE);
1224                                 return NULL;
1225                         }
1226                 } else {
1227                         m = match_avp(cs->avp,co->avp);
1228
1229                         if(m) {
1230                                 cs = cs->next;
1231                                 co = co->next;
1232
1233                                 if (copy_avps) {
1234                                         copy = avp_copy(m);
1235                                         if ( ! insert_avp(newavpl,copy) ) {
1236                                                 delete_avp(copy);
1237                                         }
1238                                 } else {
1239                                         insert_avp(newavpl,m);
1240                                 }
1241
1242
1243                                 if (!co->avp) {
1244                                         return newavpl;
1245                                 }
1246                                 if (!cs->avp) {
1247                                         delete_avpl(newavpl,TRUE);
1248                                         return NULL;
1249                                 }
1250                         } else {
1251                                 delete_avpl(newavpl,TRUE);
1252                                 return NULL;
1253                         }
1254                 }
1255
1256         }
1257
1258         /* should never be reached */
1259         return NULL;
1260 }
1261
1262 extern AVPL* new_avpl_from_match(avpl_match_mode mode, const gchar* name,AVPL* src, AVPL* op, gboolean copy_avps) {
1263         AVPL* avpl = NULL;
1264         
1265         switch (mode) {
1266                 case AVPL_STRICT:
1267                         avpl = new_avpl_exact_match(name,src,op,copy_avps);
1268                         break;
1269                 case AVPL_LOOSE:
1270                         avpl = new_avpl_loose_match(name,src,op,copy_avps);
1271                         break;
1272                 case AVPL_EVERY:
1273                         avpl = new_avpl_every_match(name,src,op,copy_avps);
1274                         break;
1275                 case AVPL_NO_MATCH:
1276                         avpl = new_avpl_from_avpl(name,src,copy_avps);
1277                         merge_avpl(avpl, op, copy_avps);
1278                         break;
1279         }
1280         
1281         return avpl;
1282 }
1283
1284 /**
1285  * new_avpl_transform:
1286  *
1287  * creates an empty avpl transformation
1288  *
1289  * Return value: a pointer to the newly created avpl transformation
1290  **/
1291 static AVPL_Transf* new_avpl_transform(gchar* name, AVPL* mixed, avpl_match_mode match_mode, avpl_replace_mode replace_mode) {
1292         AVPL_Transf* t = g_malloc(sizeof(AVPL_Transf));
1293         AVP* avp;
1294
1295         t->name = g_strdup(name);
1296         t->match = new_avpl("match");
1297         t->replace = new_avpl("replace");
1298         t->match_mode = match_mode;
1299         t->replace_mode  = replace_mode;
1300         t->next = NULL;
1301         t->map = NULL;
1302
1303         while (( avp = extract_first_avp(mixed) )) {
1304                 if (*(avp->n) == '.') {
1305                         rename_avp(avp,((avp->n)+1));
1306                         insert_avp(t->replace, avp);
1307                 } else {
1308                         insert_avp(t->match, avp);
1309                 }
1310         }
1311
1312         return t;
1313 }
1314
1315
1316 /**
1317  * delete_avpl_transform:
1318  * @it: a pointer to the avpl transformation object
1319  *
1320  * Destroys an avpl transformation object and releases all the resources it
1321  * uses.
1322  *
1323  **/
1324 extern void delete_avpl_transform(AVPL_Transf* op) {
1325         AVPL_Transf* next;
1326
1327         for (; op ; op = next) {
1328                 next = op->next;
1329
1330                 g_free(op->name);
1331
1332                 if (op->match) {
1333                         delete_avpl(op->match,TRUE);
1334                 }
1335
1336                 if (op->replace) {
1337                         delete_avpl(op->replace,TRUE);
1338                 }
1339
1340                 g_free(op);
1341         }
1342
1343 }
1344
1345
1346 /**
1347  * avpl_transform:
1348  * @src: the source avpl for the transform operation.
1349  * @op: a pointer to the avpl transformation object to apply.
1350  *
1351  * Applies the "op" transformation to an avpl, matches it and eventually
1352  * replaces or inserts the transformed avps.
1353  *
1354  * Return value: whether the transformation was performed or not.
1355  **/
1356 extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
1357         AVPL* avpl = NULL;
1358         AVPN* cs;
1359         AVPN* cm;
1360         AVPN* n;
1361
1362 #ifdef _AVP_DEBUGGING
1363         dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%X op=%X",src,op);
1364 #endif
1365
1366         for ( ; op ; op = op->next) {
1367                 
1368                 avpl = new_avpl_from_match(op->match_mode, src->name,src, op->match, TRUE);
1369
1370                 if (avpl) {
1371                         switch (op->replace_mode) {
1372                                 case AVPL_NO_REPLACE:
1373                                         delete_avpl(avpl,TRUE);
1374                                         return;
1375                                 case AVPL_INSERT:
1376                                         merge_avpl(src,op->replace,TRUE);
1377                                         delete_avpl(avpl,TRUE);
1378                                         return;
1379                                 case AVPL_REPLACE:
1380                                         cs = src->null.next;
1381                                         cm = avpl->null.next;
1382                                         while(cs->avp) {
1383                                                 if (cm->avp && cs->avp->n == cm->avp->n && cs->avp->v == cm->avp->v) {
1384                                                         n = cs->next;
1385
1386                                                         cs->prev->next = cs->next;
1387                                                         cs->next->prev = cs->prev;
1388                                                         g_mem_chunk_free(avp_chunk,cs);
1389
1390                                                         cs = n;
1391                                                         cm = cm->next;
1392                                                 } else {
1393                                                         cs = cs->next;
1394                                                 }
1395                                         }
1396
1397                                         merge_avpl(src,op->replace,TRUE);
1398                                         delete_avpl(avpl,TRUE);
1399                                         return;
1400                         }
1401                 }
1402         }
1403 }
1404
1405
1406 /**
1407  * new_loal:
1408  * @name: the name the loal will take.
1409  *
1410  * Creates an empty list of avp lists.
1411  *
1412  * Return value: a pointer to the newly created loal.
1413  **/
1414 extern LoAL* new_loal(const gchar* name) {
1415         LoAL* new_loal = g_mem_chunk_alloc(avp_chunk);
1416
1417         if (! name) {
1418                 name = "anonymous";
1419         }
1420
1421 #ifdef _AVP_DEBUGGING
1422         dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal: %X name=%s",new_loal,name);
1423 #endif
1424
1425         new_loal->name = scs_subscribe(avp_strings,name);
1426         new_loal->null.avpl = NULL;
1427         new_loal->null.next = &new_loal->null;
1428         new_loal->null.prev = &new_loal->null;
1429         new_loal->len = 0;
1430         return new_loal;
1431 }
1432
1433 /**
1434  * loal_append:
1435  * @loal: the loal on which to operate.
1436  * @avpl: the avpl to append.
1437  *
1438  * Appends an avpl to a loal.
1439  *
1440  **/
1441 extern void loal_append(LoAL* loal, AVPL* avpl) {
1442         LoALnode* node = g_mem_chunk_alloc(avp_chunk);
1443
1444 #ifdef _AVP_DEBUGGING
1445         dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %X",node);
1446 #endif
1447
1448         node->avpl = avpl;
1449         node->next = &loal->null;
1450         node->prev = loal->null.prev;
1451
1452         loal->null.prev->next = node;
1453         loal->null.prev = node;
1454         loal->len++;
1455 }
1456
1457
1458 /**
1459  * extract_first_avpl:
1460  * @loal: the loal on which to operate.
1461  *
1462  * Extracts the first avpl contained in a loal.
1463  *
1464  * Return value: a pointer to the extracted avpl.
1465  *
1466  **/
1467 extern AVPL* extract_first_avpl(LoAL* loal) {
1468         LoALnode* node;
1469         AVPL* avpl;
1470
1471 #ifdef _AVP_DEBUGGING
1472         dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name);
1473 #endif
1474
1475         node = loal->null.next;
1476
1477         loal->null.next->next->prev = &loal->null;
1478         loal->null.next = node->next;
1479
1480         loal->len--;
1481
1482         avpl = node->avpl;
1483
1484         if ( avpl ) {
1485                 g_mem_chunk_free(avp_chunk,node);
1486
1487 #ifdef _AVP_DEBUGGING
1488                 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name);
1489                 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
1490 #endif
1491         }
1492
1493         return avpl;
1494 }
1495
1496 /**
1497 * extract_first_avpl:
1498  * @loal: the loal on which to operate.
1499  *
1500  * Extracts the last avpl contained in a loal.
1501  *
1502  * Return value: a pointer to the extracted avpl.
1503  *
1504  **/
1505 extern AVPL* extract_last_avpl(LoAL* loal){
1506         LoALnode* node;
1507         AVPL* avpl;
1508
1509         node = loal->null.prev;
1510
1511         loal->null.prev->prev->next = &loal->null;
1512         loal->null.prev = node->prev;
1513
1514         loal->len--;
1515
1516         avpl = node->avpl;
1517
1518         if ( avpl ) {
1519                 g_mem_chunk_free(avp_chunk,node);
1520 #ifdef _AVP_DEBUGGING
1521                 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
1522 #endif
1523         }
1524
1525         return avpl;
1526 }
1527
1528 /**
1529  * extract_first_avpl:
1530  * @loal: the loal on which to operate.
1531  * @cookie pointer to the pointer variable to contain the state between calls
1532  *
1533  * At each call will return the following avpl from a loal. The given cookie
1534  * will be used to manatain the state between calls.
1535  *
1536  * Return value: a pointer to the next avpl.
1537  *
1538  **/
1539 extern AVPL* get_next_avpl(LoAL* loal,void** cookie) {
1540         LoALnode* node;
1541
1542 #ifdef _AVP_DEBUGGING
1543         dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%X node=%X",loal,*cookie);
1544 #endif
1545
1546         if (*cookie) {
1547                 node = (LoALnode*) *cookie;
1548         } else {
1549                 node = loal->null.next;
1550         }
1551
1552         *cookie = node->next;
1553
1554         return node->avpl;
1555 }
1556
1557 /**
1558  * delete_loal:
1559  * @loal: the loal to be deleted.
1560  * @avpls_too: whether avpls contained by the loal should be deleted as well
1561  * @avps_too: whether avps contained by the avpls should be also deleted
1562  *
1563  * Destroys a loal and eventually desstroys avpls and avps.
1564  *
1565  **/
1566 extern void delete_loal(LoAL* loal, gboolean avpls_too, gboolean avps_too) {
1567         AVPL* avpl;
1568
1569 #ifdef _AVP_DEBUGGING
1570         dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %X",loal);
1571 #endif
1572
1573         while(( avpl = extract_last_avpl(loal) )) {
1574                 if (avpls_too) {
1575                         delete_avpl(avpl,avps_too);
1576                 }
1577         }
1578
1579         scs_unsubscribe(avp_strings,loal->name);
1580         g_mem_chunk_free(avp_chunk,loal);
1581 }
1582
1583
1584
1585 /****************************************************************************
1586  ******************* the following are used in load_loal_from_file
1587  ****************************************************************************/
1588
1589 /**
1590  * load_loal_error:
1591  * Used by loal_from_file to handle errors while loading.
1592  **/
1593 static LoAL* load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, const gchar* fmt, ...) {
1594         va_list list;
1595         gchar* desc;
1596         LoAL* ret = NULL;
1597         gchar* err;
1598         
1599         va_start( list, fmt );
1600         desc = g_strdup_vprintf(fmt, list);
1601         va_end( list );
1602
1603
1604         err = g_strdup_printf("Error Loading LoAL from file: in %s at line: %i, %s",loal->name,linenum,desc);
1605         ret = new_loal(err);
1606
1607         g_free(desc);
1608         g_free(err);
1609
1610         if (fp) fclose(fp);
1611         if (loal) delete_loal(loal,TRUE,TRUE);
1612         if (curr) delete_avpl(curr,TRUE);
1613
1614         return ret;
1615 }
1616
1617
1618 /*  the maximum length allowed for a line */
1619 #define MAX_ITEM_LEN    8192
1620
1621 /* this two ugly things are used for tokenizing */
1622 #define AVP_OP_CHAR '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
1623
1624 #define AVP_NAME_CHAR 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
1625 case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
1626 case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
1627 case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
1628 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
1629 case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
1630 case '7': case '8': case '9': case '.'
1631
1632
1633 /**
1634  * loal_from_file:
1635  * @filename: the file containing a loals text representation.
1636  *
1637  * Given a filename it will attempt to load a loal containing a copy of
1638  * the avpls represented in the file.
1639  *
1640  * Return value: if successful a pointer to the new populated loal, else NULL.
1641  *
1642  **/
1643 extern LoAL* loal_from_file(gchar* filename) {
1644         FILE *fp = NULL;
1645         gchar c;
1646         int i = 0;
1647         guint32 linenum = 1;
1648         gchar linenum_buf[MAX_ITEM_LEN];
1649         gchar name[MAX_ITEM_LEN];
1650         gchar value[MAX_ITEM_LEN];
1651         gchar op = '?';
1652         LoAL *loal = new_loal(filename);
1653         AVPL* curr = NULL;
1654         AVP* avp;
1655
1656         enum _load_loal_states {
1657                 START,
1658                 BEFORE_NAME,
1659                 IN_NAME,
1660                 IN_VALUE,
1661                 MY_IGNORE
1662         } state;
1663
1664 #ifndef _WIN32
1665         if (! getuid()) {
1666                 return load_loal_error(fp,loal,curr,linenum,"MATE Will not run as root");
1667         }
1668 #endif
1669
1670         state = START;
1671
1672         if (( fp = eth_fopen(filename,"r") )) {
1673                 while(( c = (gchar) fgetc(fp) )){
1674
1675                         if ( feof(fp) ) {
1676                                 if ( ferror(fp) ) {
1677                                         report_read_failure(filename,errno);
1678                                         return load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename);
1679                                 }
1680                                 break;
1681                         }
1682
1683                         if ( c == '\n' ) {
1684                                 linenum++;
1685                         }
1686
1687                         if ( i >= MAX_ITEM_LEN - 1  ) {
1688                                 return load_loal_error(fp,loal,curr,linenum,"Maximum item length exceeded");
1689                         }
1690
1691                         switch(state) {
1692                                 case MY_IGNORE:
1693                                         switch (c) {
1694                                                 case '\n':
1695                                                         state = START;
1696                                                         i = 0;
1697                                                         continue;
1698                                                 default:
1699                                                         continue;
1700                                         }
1701                                         continue;
1702                                 case START:
1703                                         switch (c) {
1704                                                 case ' ': case '\t':
1705                                                         /* ignore whitespace at line start */
1706                                                         continue;
1707                                                 case '\n':
1708                                                         /* ignore empty lines */
1709                                                         i = 0;
1710                                                         continue;
1711                                                 case AVP_NAME_CHAR:
1712                                                         state = IN_NAME;
1713                                                         i = 0;
1714                                                         name[i++] = c;
1715                                                         name[i] = '\0';
1716                                                         g_snprintf(linenum_buf,sizeof(linenum_buf),"%s:%u",filename,linenum);
1717                                                         curr = new_avpl(linenum_buf);
1718                                                         continue;
1719                                                 case '#':
1720                                                         state = MY_IGNORE;
1721                                                         continue;
1722                                                 default:
1723                                                         return load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1724                                         }
1725                                 case BEFORE_NAME:
1726                                         i = 0;
1727                                         name[0] = '\0';
1728                                         switch (c) {
1729                                                 case '\\':
1730                                                         c = fgetc(fp);
1731                                                         if (c != '\n') ungetc(c,fp);
1732                                                         continue;
1733                                                 case ' ':
1734                                                 case '\t':
1735                                                         continue;
1736                                                 case AVP_NAME_CHAR:
1737                                                         state = IN_NAME;
1738
1739                                                         name[i++] = c;
1740                                                         name[i] = '\0';
1741                                                         continue;
1742                                                 case '\n':
1743                                                         loal_append(loal,curr);
1744                                                         state = START;
1745                                                         continue;
1746                                                 default:
1747                                                         return load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1748                                         }
1749                                         case IN_NAME:
1750                                                 switch (c) {
1751                                                         case ';':
1752                                                                 state = BEFORE_NAME;
1753
1754                                                                 op = '?';
1755                                                                 name[i] = '\0';
1756                                                                 value[0] = '\0';
1757                                                                 i = 0;
1758
1759                                                                 avp = new_avp(name,value,op);
1760
1761                                                                 if (! insert_avp(curr,avp) ) {
1762                                                                         delete_avp(avp);
1763                                                                 }
1764
1765                                                                 continue;
1766                                                         case AVP_OP_CHAR:
1767                                                                 name[i] = '\0';
1768                                                                 i = 0;
1769                                                                 op = c;
1770                                                                 state = IN_VALUE;
1771                                                                 continue;
1772                                                         case AVP_NAME_CHAR:
1773                                                                 name[i++] = c;
1774                                                                 continue;
1775                                                         case '\n':
1776                                                                 return load_loal_error(fp,loal,curr,linenum,"operator expected found new line");
1777                                                         default:
1778                                                                 return load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c);
1779                                                 }
1780                                         case IN_VALUE:
1781                                                 switch (c) {
1782                                                         case '\\':
1783                                                                 value[i++] = fgetc(fp);
1784                                                                 continue;
1785                                                         case ';':
1786                                                                 state = BEFORE_NAME;
1787
1788                                                                 value[i] = '\0';
1789                                                                 i = 0;
1790
1791                                                                 avp = new_avp(name,value,op);
1792
1793                                                                 if (! insert_avp(curr,avp) ) {
1794                                                                         delete_avp(avp);
1795                                                                 }
1796                                                                 continue;
1797                                                         case '\n':
1798                                                                 return load_loal_error(fp,loal,curr,linenum,"';' expected found new line");
1799                                                         default:
1800                                                                 value[i++] = c;
1801                                                                 continue;
1802                                                 }
1803                         }
1804                 }
1805                 fclose (fp);
1806
1807                 return loal;
1808
1809         } else {
1810                 report_open_failure(filename,errno,FALSE);
1811                 return load_loal_error(NULL,loal,NULL,0,"Cannot Open file '%s'",filename);
1812         }
1813 }