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