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