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