regen pidl all: rm epan/dissectors/pidl/*-stamp; pushd epan/dissectors/pidl/ && make...
[wireshark-sm.git] / plugins / epan / mate / mate_util.c
blobdcfe36c0ddbfbc4621b4c79c9fab2c1b3d03b901
1 /* mate_util.c
2 * MATE -- Meta Analysis Tracing Engine
3 * Utility Library: Single Copy Strings and Attribute Value Pairs
5 * Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include "config.h"
16 #include "mate.h"
17 #include "mate_util.h"
19 #include <errno.h>
20 #include <wsutil/file_util.h>
23 /***************************************************************************
24 * dbg_print
25 ***************************************************************************
26 * This is the debug facility of the thing.
27 ***************************************************************************/
29 /* dbg_print:
30 * which: a pointer to the current level of debugging for a feature
31 * how: the level over which this message should be printed out
32 * where: the file on which to print (ws_message if null)
33 * fmt, ...: what to print
36 void dbg_print(const int* which, int how, FILE* where, const char* fmt, ... ) {
37 static char debug_buffer[DEBUG_BUFFER_SIZE];
38 va_list list;
40 if ( ! which || *which < how ) return;
42 va_start( list, fmt );
43 vsnprintf(debug_buffer,DEBUG_BUFFER_SIZE,fmt,list);
44 va_end( list );
46 if (! where) {
47 ws_message("%s", debug_buffer);
48 } else {
49 fputs(debug_buffer,where);
50 fputs("\n",where);
55 /***************************************************************************
56 * single copy strings
57 ***************************************************************************
58 * Strings repeat more often than don't. In order to save memory
59 * we'll keep only one copy of each as key to a hash with a count of
60 * subscribers as value.
61 ***************************************************************************/
63 /**
64 * scs_init:
66 * Initializes the scs hash.
67 **/
69 struct _scs_collection {
70 GHashTable* hash; /* key: a string value: unsigned number of subscribers */
73 /* ToDo? free any string,ctr entries pointed to by the hash table ??
74 * XXX: AFAIKT destroy_scs_collection() might be called only when reading a
75 * mate config file. Since reading a new config file can apparently currently
76 * only be done once after starting Wireshark, in theory this fcn
77 * currently should never be called since there will never be an existing
78 * scs_collection to be destroyed.
80 static void destroy_scs_collection(SCS_collection* c) {
81 if (c->hash) g_hash_table_destroy(c->hash);
84 static SCS_collection* scs_init(void) {
85 SCS_collection* c = g_new(SCS_collection, 1);
87 c->hash = g_hash_table_new(g_str_hash,g_str_equal);
89 return c;
93 /**
94 * subscribe:
95 * @param c the scs hash
96 * @param s a string
98 * Checks if the given string exists already and if so it increases the count of
99 * subsscribers and returns a pointer to the stored string. If not It will copy
100 * the given string store it in the hash and return the pointer to the copy.
101 * Remember, containment is handled internally, take care of your own strings.
103 * Return value: a pointer to the subscribed string.
105 char* scs_subscribe(SCS_collection* c, const char* s) {
106 char* orig = NULL;
107 unsigned* ip = NULL;
108 size_t len = 0;
110 g_hash_table_lookup_extended(c->hash,(const void *)s,(void * *)&orig,(void * *)&ip);
112 if (ip) {
113 (*ip)++;
114 } else {
115 ip = g_slice_new(unsigned);
116 *ip = 0;
118 len = strlen(s) + 1;
120 if (len <= SCS_SMALL_SIZE) {
121 len = SCS_SMALL_SIZE;
122 } else if (len <= SCS_MEDIUM_SIZE) {
123 len = SCS_MEDIUM_SIZE;
124 } else if (len <= SCS_LARGE_SIZE) {
125 len = SCS_LARGE_SIZE;
126 } else if (len < SCS_HUGE_SIZE) {
127 len = SCS_HUGE_SIZE;
128 } else {
129 len = SCS_HUGE_SIZE;
130 ws_warning("mate SCS: string truncated due to huge size");
133 orig = (char *)g_slice_alloc(len);
134 (void) g_strlcpy(orig,s,len);
136 g_hash_table_insert(c->hash,orig,ip);
139 return orig;
143 * unsubscribe:
144 * @param c the scs hash
145 * @param s a string.
147 * decreases the count of subscribers, if zero frees the internal copy of
148 * the string.
150 void scs_unsubscribe(SCS_collection* c, char* s) {
151 char* orig = NULL;
152 unsigned* ip = NULL;
153 size_t len = 0xffff;
155 g_hash_table_lookup_extended(c->hash,(const void *)s,(void * *)&orig,(void * *)&ip);
157 if (ip) {
158 if (*ip == 0) {
159 g_hash_table_remove(c->hash,orig);
161 len = strlen(orig);
163 if (len < SCS_SMALL_SIZE) {
164 len = SCS_SMALL_SIZE;
165 } else if (len < SCS_MEDIUM_SIZE) {
166 len = SCS_MEDIUM_SIZE;
167 } else if (len < SCS_LARGE_SIZE) {
168 len = SCS_LARGE_SIZE;
169 } else {
170 len = SCS_HUGE_SIZE;
173 g_slice_free1(len, orig);
174 g_slice_free(unsigned,ip);
176 else {
177 (*ip)--;
179 } else {
180 ws_warning("unsubscribe: not subscribed");
185 * scs_subscribe_printf:
186 * @param fmt a format string ...
188 * Formats the input and subscribes it.
190 * Return value: the stored copy of the formated string.
193 char* scs_subscribe_printf(SCS_collection* c, char* fmt, ...) {
194 va_list list;
195 static char buf[SCS_HUGE_SIZE];
197 va_start( list, fmt );
198 vsnprintf(buf, SCS_HUGE_SIZE, fmt, list);
199 va_end( list );
201 return scs_subscribe(c,buf);
204 /***************************************************************************
205 * AVPs & Co.
206 ***************************************************************************
207 * The Thing operates mainly on avps, avpls and loals
208 * - attribute value pairs (two strings: the name and the value and an operator)
209 * - avp lists a somehow sorted list of avps
210 * - loal (list of avp lists) an arbitrarily sorted list of avpls
213 ***************************************************************************/
216 typedef union _any_avp_type {
217 AVP avp;
218 AVPN avpn;
219 AVPL avpl;
220 LoAL loal;
221 LoALnode loaln;
222 } any_avp_type;
225 static SCS_collection* avp_strings;
227 #ifdef _AVP_DEBUGGING
228 static FILE* dbg_fp;
230 static int dbg_level;
231 static int* dbg = &dbg_level;
233 static int dbg_avp_level;
234 static int* dbg_avp = &dbg_avp_level;
236 static int dbg_avp_op_level;
237 static int* dbg_avp_op = &dbg_avp_op_level;
239 static int dbg_avpl_level;
240 static int* dbg_avpl = &dbg_avpl_level;
242 static int dbg_avpl_op_level;
243 static int* dbg_avpl_op = &dbg_avpl_op_level;
246 * setup_avp_debug:
247 * @param fp the file in which to send debugging output.
248 * @param general a pointer to the level of debugging of facility "general"
249 * @param avp a pointer to the level of debugging of facility "avp"
250 * @param avp_op a pointer to the level of debugging of facility "avp_op"
251 * @param avpl a pointer to the level of debugging of facility "avpl"
252 * @param avpl_op a pointer to the level of debugging of facility "avpl_op"
254 * If enabled sets up the debug facilities for the avp library.
257 extern void setup_avp_debug(FILE* fp, int* general, int* avp, int* avp_op, int* avpl, int* avpl_op) {
258 dbg_fp = fp;
259 dbg = general;
260 dbg_avp = avp;
261 dbg_avp_op = avp_op;
262 dbg_avpl = avpl;
263 dbg_avpl_op = avpl_op;
266 #endif /* _AVP_DEBUGGING */
269 * avp_init:
271 * (Re)Initializes the avp library.
274 extern void avp_init(void) {
276 if (avp_strings) destroy_scs_collection(avp_strings);
277 avp_strings = scs_init();
282 * new_avp_from_finfo:
283 * @param name the name the avp will have.
284 * @param finfo the field_info from which to fetch the data.
286 * Creates an avp from a field_info record.
288 * Return value: a pointer to the newly created avp.
291 extern AVP* new_avp_from_finfo(const char* name, field_info* finfo) {
292 AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
293 char* value;
294 char* repr;
296 new_avp_val->n = scs_subscribe(avp_strings, name);
298 repr = fvalue_to_string_repr(NULL, finfo->value, FTREPR_DISPLAY, finfo->hfinfo->display);
300 if (repr) {
301 value = scs_subscribe(avp_strings, repr);
302 wmem_free(NULL, repr);
303 #ifdef _AVP_DEBUGGING
304 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: from string: %s",value);
305 #endif
306 } else {
307 #ifdef _AVP_DEBUGGING
308 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: a proto: %s",finfo->hfinfo->abbrev);
309 #endif
310 value = scs_subscribe(avp_strings, "");
313 new_avp_val->v = value;
315 new_avp_val->o = '=';
317 #ifdef _AVP_DEBUGGING
318 dbg_print (dbg_avp,1,dbg_fp,"new_avp_from_finfo: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
319 #endif
321 return new_avp_val;
326 * new_avp:
327 * @param name the name the avp will have.
328 * @param value the value the avp will have.
329 * @param o the operator of this avp.
331 * Creates an avp given every parameter.
333 * Return value: a pointer to the newly created avp.
336 extern AVP* new_avp(const char* name, const char* value, char o) {
337 AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
339 new_avp_val->n = scs_subscribe(avp_strings, name);
340 new_avp_val->v = scs_subscribe(avp_strings, value);
341 new_avp_val->o = o;
343 #ifdef _AVP_DEBUGGING
344 dbg_print(dbg_avp,1,dbg_fp,"new_avp_val: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
345 #endif
346 return new_avp_val;
351 * delete_avp:
352 * @param avp the avp to delete.
354 * Destroys an avp and releases the resources it uses.
357 extern void delete_avp(AVP* avp) {
358 #ifdef _AVP_DEBUGGING
359 dbg_print(dbg_avp,1,dbg_fp,"delete_avp: %p %s%c%s;",avp,avp->n,avp->o,avp->v);
360 #endif
362 scs_unsubscribe(avp_strings, avp->n);
363 scs_unsubscribe(avp_strings, avp->v);
364 g_slice_free(any_avp_type,(any_avp_type*)avp);
369 * avp_copy:
370 * @param from the avp to be copied.
372 * Creates an avp whose name op and value are copies of the given one.
374 * Return value: a pointer to the newly created avp.
377 extern AVP* avp_copy(AVP* from) {
378 AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
380 new_avp_val->n = scs_subscribe(avp_strings, from->n);
381 new_avp_val->v = scs_subscribe(avp_strings, from->v);
382 new_avp_val->o = from->o;
384 #ifdef _AVP_DEBUGGING
385 dbg_print(dbg_avp,1,dbg_fp,"copy_avp: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
386 #endif
388 return new_avp_val;
392 * new_avpl:
393 * @param name the name the avpl will have.
395 * Creates an empty avpl.
397 * Return value: a pointer to the newly created avpl.
400 extern AVPL* new_avpl(const char* name) {
401 AVPL* new_avpl_p = (AVPL*)g_slice_new(any_avp_type);
403 #ifdef _AVP_DEBUGGING
404 dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpl_p: %p name=%s",new_avpl_p,name);
405 #endif
407 new_avpl_p->name = name ? scs_subscribe(avp_strings, name) : scs_subscribe(avp_strings, "");
408 new_avpl_p->len = 0;
409 new_avpl_p->null.avp = NULL;
410 new_avpl_p->null.next = &new_avpl_p->null;
411 new_avpl_p->null.prev = &new_avpl_p->null;
414 return new_avpl_p;
417 extern void rename_avpl(AVPL* avpl, char* name) {
418 scs_unsubscribe(avp_strings,avpl->name);
419 avpl->name = scs_subscribe(avp_strings,name);
423 * insert_avp_before_node:
424 * @param avpl the avpl in which to insert.
425 * @param next_node the next node before which the new avpn has to be inserted.
426 * @param avp the avp to be inserted.
427 * @param copy_avp whether the original AVP or a copy thereof must be inserted.
429 * Pre-condition: the avp is sorted before before_avp and does not already exist
430 * in the avpl.
432 static void insert_avp_before_node(AVPL* avpl, AVPN* next_node, AVP *avp, bool copy_avp) {
433 AVPN* new_avp_val = (AVPN*)g_slice_new(any_avp_type);
435 new_avp_val->avp = copy_avp ? avp_copy(avp) : avp;
437 #ifdef _AVP_DEBUGGING
438 dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %p",new_avp_val);
439 dbg_print(dbg_avpl,5,dbg_fp,"insert_avp: inserting %p in %p before %p;",avp,avpl,next_node);
440 #endif
442 new_avp_val->next = next_node;
443 new_avp_val->prev = next_node->prev;
444 next_node->prev->next = new_avp_val;
445 next_node->prev = new_avp_val;
447 avpl->len++;
449 #ifdef _AVP_DEBUGGING
450 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
451 #endif
455 * insert_avp:
456 * @param avpl the avpl in which to insert.
457 * @param avp the avp to be inserted.
459 * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
461 * Return value: whether it was inserted or not.
463 * BEWARE: Check the return value, you might need to delete the avp if
464 * it is not inserted.
466 extern bool insert_avp(AVPL* avpl, AVP* avp) {
467 AVPN* c;
469 #ifdef _AVP_DEBUGGING
470 dbg_print(dbg_avpl_op,4,dbg_fp,"insert_avp: %p %p %s%c%s;",avpl,avp,avp->n,avp->o,avp->v);
471 #endif
473 /* get to the insertion point */
474 for (c=avpl->null.next; c->avp; c = c->next) {
475 int name_diff = strcmp(avp->n, c->avp->n);
477 if (name_diff == 0) {
478 int value_diff = strcmp(avp->v, c->avp->v);
480 if (value_diff < 0) {
481 break;
484 if (value_diff == 0) {
485 // ignore duplicate values, prevents (a=1, a=1)
486 // note that this is also used to insert
487 // conditions AVPs, so really check if the name,
488 // value and operator are all equal.
489 if (c->avp->o == avp->o && avp->o == AVP_OP_EQUAL) {
490 return false;
495 if (name_diff < 0) {
496 break;
500 insert_avp_before_node(avpl, c, avp, false);
502 return true;
506 * get_avp_by_name:
507 * @param avpl the avpl from which to try to get the avp.
508 * @param name the name of the avp we are looking for.
509 * @param cookie variable in which to store the state between calls.
511 * Gets pointer to the next avp whose name is given; uses cookie to store its
512 * state between calls.
514 * Return value: a pointer to the next matching avp if there's one, else NULL.
517 extern AVP* get_avp_by_name(AVPL* avpl, char* name, void** cookie) {
518 AVPN* curr;
519 AVPN* start = (AVPN*) *cookie;
521 #ifdef _AVP_DEBUGGING
522 dbg_print(dbg_avpl_op,7,dbg_fp,"get_avp_by_name: entering: %p %s %p",avpl,name,*cookie);
523 #endif
525 name = scs_subscribe(avp_strings, name);
527 if (!start) start = avpl->null.next;
529 for ( curr = start; curr->avp; curr = curr->next ) {
530 if ( curr->avp->n == name ) {
531 break;
535 *cookie = curr;
537 #ifdef _AVP_DEBUGGING
538 dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %p",curr);
539 #endif
541 scs_unsubscribe(avp_strings, name);
543 return curr->avp;
547 * extract_avp_by_name:
548 * @param avpl the avpl from which to try to extract the avp.
549 * @param name the name of the avp we are looking for.
551 * Extracts from the avpl the next avp whose name is given;
553 * Return value: a pointer to extracted avp if there's one, else NULL.
556 extern AVP* extract_avp_by_name(AVPL* avpl, char* name) {
557 AVPN* curr;
558 AVP* avp = NULL;
560 #ifdef _AVP_DEBUGGING
561 dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %p %s",avpl,name);
562 #endif
564 name = scs_subscribe(avp_strings, name);
566 for ( curr = avpl->null.next; curr->avp; curr = curr->next ) {
567 if ( curr->avp->n == name ) {
568 break;
572 scs_unsubscribe(avp_strings, name);
574 if( ! curr->avp ) return NULL;
576 curr->next->prev = curr->prev;
577 curr->prev->next = curr->next;
579 avp = curr->avp;
581 g_slice_free(any_avp_type,(any_avp_type*)curr);
583 (avpl->len)--;
585 #ifdef _AVP_DEBUGGING
586 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
587 #endif
589 #ifdef _AVP_DEBUGGING
590 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %p",avp);
591 #endif
593 return avp;
598 * extract_first_avp:
599 * @param avpl the avpl from which to try to extract the avp.
601 * Extracts the fisrt avp from the avpl.
603 * Return value: a pointer to extracted avp if there's one, else NULL.
606 extern AVP* extract_first_avp(AVPL* avpl) {
607 AVP* avp;
608 AVPN* node;
610 #ifdef _AVP_DEBUGGING
611 dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %p",avpl);
612 #endif
614 node = avpl->null.next;
616 avpl->null.next->prev = &avpl->null;
617 avpl->null.next = node->next;
619 avp = node->avp;
621 if (avp) {
622 g_slice_free(any_avp_type,(any_avp_type*)node);
623 (avpl->len)--;
624 #ifdef _AVP_DEBUGGING
625 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
626 #endif
629 #ifdef _AVP_DEBUGGING
630 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %p",avp);
631 #endif
633 return avp;
639 * extract_last_avp:
640 * @param avpl the avpl from which to try to extract the avp.
642 * Extracts the last avp from the avpl.
644 * Return value: a pointer to extracted avp if there's one, else NULL.
647 extern AVP* extract_last_avp(AVPL* avpl) {
648 AVP* avp;
649 AVPN* node;
651 node = avpl->null.prev;
653 avpl->null.prev->next = &avpl->null;
654 avpl->null.prev = node->prev;
656 avp = node->avp;
658 if (avp) {
659 g_slice_free(any_avp_type,(any_avp_type*)node);
660 (avpl->len)--;
661 #ifdef _AVP_DEBUGGING
662 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
663 #endif
666 #ifdef _AVP_DEBUGGING
667 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",avp);
668 #endif
670 return avp;
676 * delete_avpl:
677 * @param avpl the avpl from which to try to extract the avp.
678 * @param avps_too whether or not it should delete the avps as well.
680 * Destroys an avpl and releases the resources it uses. If told to do
681 * so releases the avps as well.
684 extern void delete_avpl(AVPL* avpl, bool avps_too) {
685 AVP* avp;
686 #ifdef _AVP_DEBUGGING
687 dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %p",avpl);
688 #endif
690 while(( avp = extract_last_avp(avpl))) {
691 if (avps_too) {
692 delete_avp(avp);
696 scs_unsubscribe(avp_strings,avpl->name);
697 g_slice_free(any_avp_type,(any_avp_type*)avpl);
703 * get_next_avp:
704 * @param avpl the avpl from which to try to get the avps.
705 * @param cookie variable in which to store the state between calls.
707 * Iterates on an avpl to get its avps.
709 * Return value: a pointer to the next avp if there's one, else NULL.
712 extern AVP* get_next_avp(AVPL* avpl, void** cookie) {
713 AVPN* node;
715 #ifdef _AVP_DEBUGGING
716 dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %p avpn: %p",avpl,*cookie);
717 #endif
719 if (*cookie) {
720 node = (AVPN*) *cookie;
721 } else {
722 node = avpl->null.next;
725 *cookie = node->next;
727 #ifdef _AVP_DEBUGGING
728 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",node->avp);
729 #endif
731 return node->avp;
735 * avpl_to_str:
736 * @param avpl the avpl to represent.
738 * Creates a newly allocated string containing a representation of an avpl.
740 * Return value: a pointer to the newly allocated string.
743 char* avpl_to_str(AVPL* avpl) {
744 AVPN* c;
745 GString* s = g_string_new("");
746 char* avp_s;
747 char* r;
749 for(c=avpl->null.next; c->avp; c = c->next) {
750 avp_s = avp_to_str(c->avp);
751 g_string_append_printf(s," %s;",avp_s);
752 g_free(avp_s);
755 r = g_string_free(s,FALSE);
757 /* g_strchug(r); ? */
758 return r;
761 extern char* avpl_to_dotstr(AVPL* avpl) {
762 AVPN* c;
763 GString* s = g_string_new("");
764 char* avp_s;
765 char* r;
767 for(c=avpl->null.next; c->avp; c = c->next) {
768 avp_s = avp_to_str(c->avp);
769 g_string_append_printf(s," .%s;",avp_s);
770 g_free(avp_s);
773 r = g_string_free(s,FALSE);
775 /* g_strchug(r); ? */
776 return r;
780 * merge_avpl:
781 * @param dst the avpl in which to merge the avps.
782 * @param src the avpl from which to get the avps.
783 * @param copy_avps whether avps should be copied instead of referenced.
785 * Adds the avps of src that are not existent in dst into dst.
788 extern void merge_avpl(AVPL* dst, AVPL* src, bool copy_avps) {
789 AVPN* cd = NULL;
790 AVPN* cs = NULL;
792 #ifdef _AVP_DEBUGGING
793 dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %p %p",dst,src);
794 #endif
796 cs = src->null.next;
797 cd = dst->null.next;
799 while (cs->avp && cd->avp) {
801 int name_diff = strcmp(cd->avp->n, cs->avp->n);
803 if (name_diff < 0) {
804 // dest < source, advance dest to find a better place to insert
805 cd = cd->next;
806 } else if (name_diff > 0) {
807 // dest > source, so it can be definitely inserted here.
808 insert_avp_before_node(dst, cd, cs->avp, copy_avps);
809 cs = cs->next;
810 } else {
811 // attribute names are equal. Ignore duplicate values but ensure that other values are sorted.
812 int value_diff = strcmp(cd->avp->v, cs->avp->v);
814 if (value_diff < 0) {
815 // dest < source, do not insert it yet
816 cd = cd->next;
817 } else if (value_diff > 0) {
818 // dest > source, insert AVP before the current dest AVP
819 insert_avp_before_node(dst, cd, cs->avp, copy_avps);
820 cs = cs->next;
821 } else {
822 // identical AVPs, do not create a duplicate.
823 cs = cs->next;
828 // if there are remaining source AVPs while there are no more destination
829 // AVPs (cd now represents the NULL item, after the last item), append
830 // all remaining source AVPs to the end
831 while (cs->avp) {
832 insert_avp_before_node(dst, cd, cs->avp, copy_avps);
833 cs = cs->next;
836 #ifdef _AVP_DEBUGGING
837 dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
838 #endif
840 return;
845 * new_avpl_from_avpl:
846 * @param name the name of the new avpl.
847 * @param avpl the avpl from which to get the avps.
848 * @param copy_avps whether avps should be copied instead of referenced.
850 * Creates a new avpl containing the same avps as the given avpl
851 * It will either reference or copie the avps.
853 * Return value: a pointer to the newly allocated string.
856 extern AVPL* new_avpl_from_avpl(const char* name, AVPL* avpl, bool copy_avps) {
857 AVPL* newavpl = new_avpl(name);
858 void* cookie = NULL;
859 AVP* avp;
860 AVP* copy;
862 #ifdef _AVP_DEBUGGING
863 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_from_avpl: %p from=%p name='%s'",newavpl,avpl,name);
864 #endif
866 while(( avp = get_next_avp(avpl,&cookie) )) {
867 if (copy_avps) {
868 copy = avp_copy(avp);
869 if ( ! insert_avp(newavpl,copy) ) {
870 delete_avp(copy);
872 } else {
873 insert_avp(newavpl,avp);
877 #ifdef _AVP_DEBUGGING
878 dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done");
879 #endif
881 return newavpl;
885 * match_avp:
886 * @param src an src to be compared against an "op" avp
887 * @param op the "op" avp that will be matched against the src avp
889 * Checks whether or not two avp's match.
891 * Return value: a pointer to the src avp if there's a match.
894 extern AVP* match_avp(AVP* src, AVP* op) {
895 char** splited;
896 int i;
897 char* p;
898 unsigned ls;
899 unsigned lo;
900 double fs = 0.0;
901 double fo = 0.0;
902 bool lower = false;
904 #ifdef _AVP_DEBUGGING
905 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);
906 #endif
908 if ( src->n != op->n ) {
909 return NULL;
912 switch (op->o) {
913 case AVP_OP_EXISTS:
914 return src;
915 case AVP_OP_EQUAL:
916 return src->v == op->v ? src : NULL;
917 case AVP_OP_NOTEQUAL:
918 return !( src->v == op->v) ? src : NULL;
919 case AVP_OP_STARTS:
920 return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL;
921 case AVP_OP_ONEOFF:
922 splited = g_strsplit(op->v,"|",0);
923 if (splited) {
924 for (i=0;splited[i];i++) {
925 if(g_str_equal(splited[i],src->v)) {
926 g_strfreev(splited);
927 return src;
930 g_strfreev(splited);
932 return NULL;
934 case AVP_OP_LOWER:
935 lower = true;
936 /* FALLTHRU */
937 case AVP_OP_HIGHER:
939 fs = g_ascii_strtod(src->v, NULL);
940 fo = g_ascii_strtod(op->v, NULL);
942 if (lower) {
943 if (fs<fo) return src;
944 else return NULL;
945 } else {
946 if (fs>fo) return src;
947 else return NULL;
949 case AVP_OP_ENDS:
950 /* does this work? */
951 ls = (unsigned) strlen(src->v);
952 lo = (unsigned) strlen(op->v);
954 if ( ls < lo ) {
955 return NULL;
956 } else {
957 p = src->v + ( ls - lo );
958 return g_str_equal(p,op->v) ? src : NULL;
961 /* case AVP_OP_TRANSF: */
962 /* return do_transform(src,op); */
963 case AVP_OP_CONTAINS:
964 return g_strrstr(src->v, op->v) ? src : NULL;
966 /* will never get here */
967 return NULL;
973 * new_avpl_loose_match:
974 * @param name the name of the resulting avpl
975 * @param src the data AVPL to be matched against a condition AVPL
976 * @param op the conditions AVPL that will be matched against the data AVPL
977 * @param copy_avps whether the avps in the resulting avpl should be copied
979 * Creates a new AVP list containing all data AVPs that matched any of the
980 * conditions AVPs. If there are no matches, an empty list will be returned.
982 * Note: Loose will always be considered a successful match, it matches zero or
983 * more conditions.
985 extern AVPL* new_avpl_loose_match(const char* name,
986 AVPL* src,
987 AVPL* op,
988 bool copy_avps) {
990 AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
991 AVPN* co = NULL;
992 AVPN* cs = NULL;
994 #ifdef _AVP_DEBUGGING
995 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_loose_match: %p src=%p op=%p name='%s'",newavpl,src,op,name);
996 #endif
999 cs = src->null.next;
1000 co = op->null.next;
1001 while (cs->avp && co->avp) {
1002 int name_diff = strcmp(co->avp->n, cs->avp->n);
1004 if (name_diff < 0) {
1005 // op < source, op is not matching
1006 co = co->next;
1007 } else if (name_diff > 0) {
1008 // op > source, source is not matching
1009 cs = cs->next;
1010 } else {
1011 // attribute match found, let's see if there is any condition (op) that accepts this data AVP.
1012 AVPN *cond = co;
1013 do {
1014 if (match_avp(cs->avp, cond->avp)) {
1015 insert_avp_before_node(newavpl, newavpl->null.prev->next, cs->avp, copy_avps);
1016 break;
1018 cond = cond->next;
1019 } while (cond->avp && cond->avp->n == cs->avp->n);
1020 cs = cs->next;
1024 // return matches (possible none)
1025 return newavpl;
1029 * new_avpl_pairs_match:
1030 * @param name the name of the resulting avpl
1031 * @param src the data AVPL to be matched against a condition AVPL
1032 * @param op the conditions AVPL that will be matched against the data AVPL
1033 * @param strict true if every condition must have a matching data AVP, false if
1034 * it is also acceptable that only one of the condition AVPs for the same
1035 * attribute is matching.
1036 * @param copy_avps whether the avps in the resulting avpl should be copied
1038 * Creates an AVP list by matching pairs of conditions and data AVPs, returning
1039 * the data AVPs. If strict is true, then each condition must be paired with a
1040 * matching data AVP. If strict is false, then some conditions are allowed to
1041 * fail when other conditions for the same attribute do have a match. Note that
1042 * if the condition AVPL is empty, the result will be a match (an empty list).
1044 * Return value: a pointer to the newly created avpl containing the
1045 * matching avps or NULL if there is no match.
1047 extern AVPL* new_avpl_pairs_match(const char* name, AVPL* src, AVPL* op, bool strict, bool copy_avps) {
1048 AVPL* newavpl;
1049 AVPN* co = NULL;
1050 AVPN* cs = NULL;
1051 const char *last_match = NULL;
1052 bool matched = true;
1054 newavpl = new_avpl(scs_subscribe(avp_strings, name));
1056 #ifdef _AVP_DEBUGGING
1057 dbg_print(dbg_avpl_op,3,dbg_fp,"%s: %p src=%p op=%p name='%s'",G_STRFUNC,newavpl,src,op,name);
1058 #endif
1060 cs = src->null.next;
1061 co = op->null.next;
1062 while (cs->avp && co->avp) {
1063 int name_diff = g_strcmp0(co->avp->n, cs->avp->n);
1064 const char *failed_match = NULL;
1066 if (name_diff < 0) {
1067 // op < source, op has no data avp with same attribute.
1068 failed_match = co->avp->n;
1069 co = co->next;
1070 } else if (name_diff > 0) {
1071 // op > source, the source avp is not matched by any condition
1072 cs = cs->next;
1073 } else {
1074 // Matching attributes found, now try to find a matching data AVP for the condition.
1075 if (match_avp(cs->avp, co->avp)) {
1076 insert_avp_before_node(newavpl, newavpl->null.prev->next, cs->avp, copy_avps);
1077 last_match = co->avp->n;
1078 cs = cs->next;
1079 } else {
1080 failed_match = co->avp->n;
1082 co = co->next;
1085 // condition did not match, check if we can continue matching.
1086 if (failed_match) {
1087 if (strict) {
1088 matched = false;
1089 break;
1090 } else if (last_match != failed_match) {
1091 // None of the conditions so far matched the attribute, check for other candidates
1092 if (!co->avp || co->avp->n != last_match) {
1093 matched = false;
1094 break;
1100 // if there are any conditions remaining, then those could not be matched
1101 if (matched && strict && co->avp) {
1102 matched = false;
1105 if (matched) {
1106 // there was a match, accept it
1107 return newavpl;
1108 } else {
1109 // no match, only delete AVPs too if they were copied
1110 delete_avpl(newavpl, copy_avps);
1111 return NULL;
1117 * new_avpl_from_match:
1118 * @param mode The matching method, one of AVPL_STRICT, AVPL_LOOSE, AVPL_EVERY.
1119 * @param name the name of the resulting avpl
1120 * @param src the data AVPL to be matched against a condition AVPL
1121 * @param op the conditions AVPL that will be matched against the data AVPL
1123 * Matches the conditions AVPL against the original AVPL according to the mode.
1124 * If there is no match, NULL is returned. If there is actually a match, then
1125 * the matching AVPs (a subset of the data) are returned.
1127 extern AVPL* new_avpl_from_match(avpl_match_mode mode, const char* name,AVPL* src, AVPL* op, bool copy_avps) {
1128 AVPL* avpl = NULL;
1130 switch (mode) {
1131 case AVPL_STRICT:
1132 avpl = new_avpl_pairs_match(name, src, op, true, copy_avps);
1133 break;
1134 case AVPL_LOOSE:
1135 avpl = new_avpl_loose_match(name,src,op,copy_avps);
1136 break;
1137 case AVPL_EVERY:
1138 avpl = new_avpl_pairs_match(name, src, op, false, copy_avps);
1139 break;
1140 case AVPL_NO_MATCH:
1141 // XXX this seems unused
1142 avpl = new_avpl_from_avpl(name,src,copy_avps);
1143 merge_avpl(avpl, op, copy_avps);
1144 break;
1147 return avpl;
1151 * delete_avpl_transform:
1152 * @param op a pointer to the avpl transformation object
1154 * Destroys an avpl transformation object and releases all the resources it
1155 * uses.
1158 extern void delete_avpl_transform(AVPL_Transf* op) {
1159 AVPL_Transf* next;
1161 for (; op ; op = next) {
1162 next = op->next;
1164 g_free(op->name);
1166 if (op->match) {
1167 delete_avpl(op->match,true);
1170 if (op->replace) {
1171 delete_avpl(op->replace,true);
1174 g_free(op);
1181 * avpl_transform:
1182 * @param src the source avpl for the transform operation.
1183 * @param op a pointer to the avpl transformation object to apply.
1185 * Applies the "op" transformation to an avpl, matches it and eventually
1186 * replaces or inserts the transformed avps.
1188 * Return value: whether the transformation was performed or not.
1190 extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
1191 AVPL* avpl = NULL;
1192 AVPN* cs;
1193 AVPN* cm;
1194 AVPN* n;
1196 #ifdef _AVP_DEBUGGING
1197 dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%p op=%p",src,op);
1198 #endif
1200 for ( ; op ; op = op->next) {
1202 avpl = new_avpl_from_match(op->match_mode, src->name,src, op->match, true);
1204 if (avpl) {
1205 switch (op->replace_mode) {
1206 case AVPL_NO_REPLACE:
1207 delete_avpl(avpl,true);
1208 return;
1209 case AVPL_INSERT:
1210 merge_avpl(src,op->replace,true);
1211 delete_avpl(avpl,true);
1212 return;
1213 case AVPL_REPLACE:
1214 cs = src->null.next;
1215 cm = avpl->null.next;
1216 // Removes AVPs from the source which are in the matched data.
1217 // Assume that the matched set is a subset of the source.
1218 while (cs->avp && cm->avp) {
1219 if (cs->avp->n == cm->avp->n && cs->avp->v == cm->avp->v) {
1220 n = cs->next;
1222 cs->prev->next = cs->next;
1223 cs->next->prev = cs->prev;
1224 g_slice_free(any_avp_type,(any_avp_type*)cs);
1226 cs = n;
1227 cm = cm->next;
1228 } else {
1229 // Current matched AVP is not equal to the current
1230 // source AVP. Since there must be a source AVP for
1231 // each matched AVP, advance current source and not
1232 // the match AVP.
1233 cs = cs->next;
1237 merge_avpl(src,op->replace,true);
1238 delete_avpl(avpl,true);
1239 return;
1247 * new_loal:
1248 * @param name the name the loal will take.
1250 * Creates an empty list of avp lists.
1252 * Return value: a pointer to the newly created loal.
1254 extern LoAL* new_loal(const char* name) {
1255 LoAL* new_loal_p = (LoAL*)g_slice_new(any_avp_type);
1257 if (! name) {
1258 name = "anonymous";
1261 #ifdef _AVP_DEBUGGING
1262 dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_p: %p name=%s",new_loal_p,name);
1263 #endif
1265 new_loal_p->name = scs_subscribe(avp_strings,name);
1266 new_loal_p->null.avpl = NULL;
1267 new_loal_p->null.next = &new_loal_p->null;
1268 new_loal_p->null.prev = &new_loal_p->null;
1269 new_loal_p->len = 0;
1270 return new_loal_p;
1274 * loal_append:
1275 * @param loal the loal on which to operate.
1276 * @param avpl the avpl to append.
1278 * Appends an avpl to a loal.
1281 extern void loal_append(LoAL* loal, AVPL* avpl) {
1282 LoALnode* node = (LoALnode*)g_slice_new(any_avp_type);
1284 #ifdef _AVP_DEBUGGING
1285 dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %p",node);
1286 #endif
1288 node->avpl = avpl;
1289 node->next = &loal->null;
1290 node->prev = loal->null.prev;
1292 loal->null.prev->next = node;
1293 loal->null.prev = node;
1294 loal->len++;
1299 * extract_first_avpl:
1300 * @param loal the loal on which to operate.
1302 * Extracts the first avpl contained in a loal.
1304 * Return value: a pointer to the extracted avpl.
1307 extern AVPL* extract_first_avpl(LoAL* loal) {
1308 LoALnode* node;
1309 AVPL* avpl;
1311 #ifdef _AVP_DEBUGGING
1312 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name);
1313 #endif
1315 node = loal->null.next;
1317 loal->null.next->next->prev = &loal->null;
1318 loal->null.next = node->next;
1320 loal->len--;
1322 avpl = node->avpl;
1324 if ( avpl ) {
1325 g_slice_free(any_avp_type,(any_avp_type*)node);
1327 #ifdef _AVP_DEBUGGING
1328 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name);
1329 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node);
1330 #endif
1333 return avpl;
1337 * extract_first_avpl:
1338 * @param loal the loal on which to operate.
1340 * Extracts the last avpl contained in a loal.
1342 * Return value: a pointer to the extracted avpl.
1345 extern AVPL* extract_last_avpl(LoAL* loal){
1346 LoALnode* node;
1347 AVPL* avpl;
1349 node = loal->null.prev;
1351 loal->null.prev->prev->next = &loal->null;
1352 loal->null.prev = node->prev;
1354 loal->len--;
1356 avpl = node->avpl;
1358 if ( avpl ) {
1359 g_slice_free(any_avp_type,(any_avp_type*)node);
1360 #ifdef _AVP_DEBUGGING
1361 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node);
1362 #endif
1365 return avpl;
1369 * extract_first_avpl:
1370 * @param loal the loal on which to operate.
1371 * @param cookie pointer to the pointer variable to contain the state between calls
1373 * At each call will return the following avpl from a loal. The given cookie
1374 * will be used to manatain the state between calls.
1376 * Return value: a pointer to the next avpl.
1379 extern AVPL* get_next_avpl(LoAL* loal,void** cookie) {
1380 LoALnode* node;
1382 #ifdef _AVP_DEBUGGING
1383 dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%p node=%p",loal,*cookie);
1384 #endif
1386 if (*cookie) {
1387 node = (LoALnode*) *cookie;
1388 } else {
1389 node = loal->null.next;
1392 *cookie = node->next;
1394 return node->avpl;
1398 * delete_loal:
1399 * @param loal the loal to be deleted.
1400 * @param avpls_too whether avpls contained by the loal should be deleted as well
1401 * @param avps_too whether avps contained by the avpls should be also deleted
1403 * Destroys a loal and eventually desstroys avpls and avps.
1406 extern void delete_loal(LoAL* loal, bool avpls_too, bool avps_too) {
1407 AVPL* avpl;
1409 #ifdef _AVP_DEBUGGING
1410 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %p",loal);
1411 #endif
1413 while(( avpl = extract_last_avpl(loal) )) {
1414 if (avpls_too) {
1415 delete_avpl(avpl,avps_too);
1419 scs_unsubscribe(avp_strings,loal->name);
1420 g_slice_free(any_avp_type,(any_avp_type*)loal);
1425 /****************************************************************************
1426 ******************* the following are used in load_loal_from_file
1427 ****************************************************************************/
1430 * load_loal_error:
1431 * Used by loal_from_file to handle errors while loading.
1433 static LoAL* load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, const char* fmt, ...) {
1434 va_list list;
1435 char* desc;
1436 LoAL* ret = NULL;
1437 char* err;
1439 va_start( list, fmt );
1440 desc = ws_strdup_vprintf(fmt, list);
1441 va_end( list );
1443 if (loal) {
1444 err = ws_strdup_printf("Error Loading LoAL from file: in %s at line: %i, %s",loal->name,linenum,desc);
1445 } else {
1446 err = ws_strdup_printf("Error Loading LoAL at line: %i, %s",linenum,desc);
1448 ret = new_loal(err);
1450 g_free(desc);
1451 g_free(err);
1453 if (fp) fclose(fp);
1454 if (loal) delete_loal(loal,true,true);
1455 if (curr) delete_avpl(curr,true);
1457 return ret;
1461 /* the maximum length allowed for a line */
1462 #define MAX_ITEM_LEN 8192
1464 /* this two ugly things are used for tokenizing */
1465 #define AVP_OP_CHAR '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
1467 #define AVP_NAME_CHAR 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
1468 case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
1469 case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
1470 case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
1471 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
1472 case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
1473 case '7': case '8': case '9': case '.'
1477 * loal_from_file:
1478 * @param filename the file containing a loals text representation.
1480 * Given a filename it will attempt to load a loal containing a copy of
1481 * the avpls represented in the file.
1483 * Return value: if successful a pointer to the new populated loal, else NULL.
1486 extern LoAL* loal_from_file(char* filename) {
1487 FILE *fp = NULL;
1488 char c;
1489 int i = 0;
1490 uint32_t linenum = 1;
1491 char *linenum_buf;
1492 char *name;
1493 char *value;
1494 char op = '?';
1495 LoAL *loal_error, *loal = new_loal(filename);
1496 AVPL* curr = NULL;
1497 AVP* avp;
1499 enum _load_loal_states {
1500 START,
1501 BEFORE_NAME,
1502 IN_NAME,
1503 IN_VALUE,
1504 MY_IGNORE
1505 } state;
1507 linenum_buf = (char*)g_malloc(MAX_ITEM_LEN);
1508 name = (char*)g_malloc(MAX_ITEM_LEN);
1509 value = (char*)g_malloc(MAX_ITEM_LEN);
1510 #ifndef _WIN32
1511 if (! getuid()) {
1512 loal_error = load_loal_error(fp,loal,curr,linenum,"MATE Will not run as root");
1513 goto error;
1515 #endif
1517 state = START;
1519 if (( fp = ws_fopen(filename,"r") )) {
1520 while(( c = (char) fgetc(fp) )){
1522 if ( feof(fp) ) {
1523 if ( ferror(fp) ) {
1524 report_read_failure(filename,errno);
1525 loal_error = load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename);
1526 goto error;
1528 break;
1531 if ( c == '\n' ) {
1532 linenum++;
1535 if ( i >= MAX_ITEM_LEN - 1 ) {
1536 loal_error = load_loal_error(fp,loal,curr,linenum,"Maximum item length exceeded");
1537 goto error;
1540 switch(state) {
1541 case MY_IGNORE:
1542 switch (c) {
1543 case '\n':
1544 state = START;
1545 i = 0;
1546 continue;
1547 default:
1548 continue;
1550 case START:
1551 switch (c) {
1552 case ' ': case '\t':
1553 /* ignore whitespace at line start */
1554 continue;
1555 case '\n':
1556 /* ignore empty lines */
1557 i = 0;
1558 continue;
1559 case AVP_NAME_CHAR:
1560 state = IN_NAME;
1561 i = 0;
1562 name[i++] = c;
1563 name[i] = '\0';
1564 snprintf(linenum_buf,MAX_ITEM_LEN,"%s:%u",filename,linenum);
1565 curr = new_avpl(linenum_buf);
1566 continue;
1567 case '#':
1568 state = MY_IGNORE;
1569 continue;
1570 default:
1571 loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1572 goto error;
1574 case BEFORE_NAME:
1575 i = 0;
1576 name[0] = '\0';
1577 switch (c) {
1578 case '\\':
1579 c = (char) fgetc(fp);
1580 if (c != '\n') ungetc(c,fp);
1581 continue;
1582 case ' ':
1583 case '\t':
1584 continue;
1585 case AVP_NAME_CHAR:
1586 state = IN_NAME;
1588 name[i++] = c;
1589 name[i] = '\0';
1590 continue;
1591 case '\n':
1592 loal_append(loal,curr);
1593 state = START;
1594 continue;
1595 default:
1596 loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1597 goto error;
1599 case IN_NAME:
1600 switch (c) {
1601 case ';':
1602 state = BEFORE_NAME;
1604 op = '?';
1605 name[i] = '\0';
1606 value[0] = '\0';
1607 i = 0;
1609 avp = new_avp(name,value,op);
1611 if (! insert_avp(curr,avp) ) {
1612 delete_avp(avp);
1615 continue;
1616 case AVP_OP_CHAR:
1617 name[i] = '\0';
1618 i = 0;
1619 op = c;
1620 state = IN_VALUE;
1621 continue;
1622 case AVP_NAME_CHAR:
1623 name[i++] = c;
1624 continue;
1625 case '\n':
1626 loal_error = load_loal_error(fp,loal,curr,linenum,"operator expected found new line");
1627 goto error;
1628 default:
1629 loal_error = load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c);
1630 goto error;
1632 case IN_VALUE:
1633 switch (c) {
1634 case '\\':
1635 value[i++] = (char) fgetc(fp);
1636 continue;
1637 case ';':
1638 state = BEFORE_NAME;
1640 value[i] = '\0';
1641 i = 0;
1643 avp = new_avp(name,value,op);
1645 if (! insert_avp(curr,avp) ) {
1646 delete_avp(avp);
1648 continue;
1649 case '\n':
1650 loal_error = load_loal_error(fp,loal,curr,linenum,"';' expected found new line");
1651 goto error;
1652 default:
1653 value[i++] = c;
1654 continue;
1658 fclose (fp);
1660 g_free(linenum_buf);
1661 g_free(name);
1662 g_free(value);
1664 return loal;
1666 } else {
1667 report_open_failure(filename,errno,false);
1668 loal_error = load_loal_error(NULL,loal,NULL,0,"Cannot Open file '%s'",filename);
1671 error:
1672 g_free(linenum_buf);
1673 g_free(name);
1674 g_free(value);
1676 return loal_error;
1680 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1682 * Local variables:
1683 * c-basic-offset: 8
1684 * tab-width: 8
1685 * indent-tabs-mode: t
1686 * End:
1688 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1689 * :indentSize=8:tabSize=8:noTabs=false: