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