| File: | plugins/epan/mate/mate_util.c |
| Warning: | line 1583, column 23 File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 | * Wireshark - Network traffic analyzer | |||
| 8 | * By Gerald Combs <gerald@wireshark.org> | |||
| 9 | * Copyright 1998 Gerald Combs | |||
| 10 | * | |||
| 11 | * SPDX-License-Identifier: GPL-2.0-or-later | |||
| 12 | */ | |||
| 13 | ||||
| 14 | #include "config.h" | |||
| 15 | ||||
| 16 | #include "mate.h" | |||
| 17 | #include "mate_util.h" | |||
| 18 | ||||
| 19 | #include <errno(*__errno_location ()).h> | |||
| 20 | #include <wsutil/file_util.h> | |||
| 21 | ||||
| 22 | ||||
| 23 | /*************************************************************************** | |||
| 24 | * dbg_print | |||
| 25 | *************************************************************************** | |||
| 26 | * This is the debug facility of the thing. | |||
| 27 | ***************************************************************************/ | |||
| 28 | ||||
| 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 | |||
| 34 | */ | |||
| 35 | ||||
| 36 | void dbg_print(const int* which, int how, FILE* where, const char* fmt, ... ) { | |||
| 37 | static char debug_buffer[DEBUG_BUFFER_SIZE4096]; | |||
| 38 | va_list list; | |||
| 39 | ||||
| 40 | if ( ! which || *which < how ) return; | |||
| 41 | ||||
| 42 | va_start( list, fmt )__builtin_va_start(list, fmt); | |||
| 43 | vsnprintf(debug_buffer,DEBUG_BUFFER_SIZE4096,fmt,list); | |||
| 44 | va_end( list )__builtin_va_end(list); | |||
| 45 | ||||
| 46 | if (! where) { | |||
| 47 | ws_message("%s", debug_buffer)do { if (1) { ws_log_full("MATE", LOG_LEVEL_MESSAGE, ((void*) 0), -1, ((void*)0), "%s", debug_buffer); } } while (0); | |||
| 48 | } else { | |||
| 49 | fputs(debug_buffer,where); | |||
| 50 | fputs("\n",where); | |||
| 51 | } | |||
| 52 | ||||
| 53 | } | |||
| 54 | ||||
| 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 | ***************************************************************************/ | |||
| 62 | ||||
| 63 | /** | |||
| 64 | * scs_init: | |||
| 65 | * | |||
| 66 | * Initializes the scs hash. | |||
| 67 | **/ | |||
| 68 | ||||
| 69 | struct _scs_collection { | |||
| 70 | GHashTable* hash; /* key: a string value: unsigned number of subscribers */ | |||
| 71 | }; | |||
| 72 | ||||
| 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. | |||
| 79 | */ | |||
| 80 | static void destroy_scs_collection(SCS_collection* c) { | |||
| 81 | if (c->hash) g_hash_table_destroy(c->hash); | |||
| 82 | } | |||
| 83 | ||||
| 84 | static SCS_collection* scs_init(void) { | |||
| 85 | SCS_collection* c = g_new(SCS_collection, 1)((SCS_collection *) g_malloc_n ((1), sizeof (SCS_collection)) ); | |||
| 86 | ||||
| 87 | c->hash = g_hash_table_new(g_str_hash,g_str_equal); | |||
| 88 | ||||
| 89 | return c; | |||
| 90 | } | |||
| 91 | ||||
| 92 | ||||
| 93 | /** | |||
| 94 | * subscribe: | |||
| 95 | * @param c the scs hash | |||
| 96 | * @param s a string | |||
| 97 | * | |||
| 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. | |||
| 102 | * | |||
| 103 | * Return value: a pointer to the subscribed string. | |||
| 104 | **/ | |||
| 105 | char* scs_subscribe(SCS_collection* c, const char* s) { | |||
| 106 | char* orig = NULL((void*)0); | |||
| 107 | unsigned* ip = NULL((void*)0); | |||
| 108 | size_t len = 0; | |||
| 109 | ||||
| 110 | g_hash_table_lookup_extended(c->hash,(const void *)s,(void * *)&orig,(void * *)&ip); | |||
| 111 | ||||
| 112 | if (ip) { | |||
| 113 | (*ip)++; | |||
| 114 | } else { | |||
| 115 | ip = g_slice_new(unsigned)((unsigned*) g_slice_alloc (sizeof (unsigned))); | |||
| 116 | *ip = 0; | |||
| 117 | ||||
| 118 | len = strlen(s) + 1; | |||
| 119 | ||||
| 120 | if (len <= SCS_SMALL_SIZE16) { | |||
| 121 | len = SCS_SMALL_SIZE16; | |||
| 122 | } else if (len <= SCS_MEDIUM_SIZE256) { | |||
| 123 | len = SCS_MEDIUM_SIZE256; | |||
| 124 | } else if (len <= SCS_LARGE_SIZE4096) { | |||
| 125 | len = SCS_LARGE_SIZE4096; | |||
| 126 | } else if (len < SCS_HUGE_SIZE65536) { | |||
| 127 | len = SCS_HUGE_SIZE65536; | |||
| 128 | } else { | |||
| 129 | len = SCS_HUGE_SIZE65536; | |||
| 130 | ws_warning("mate SCS: string truncated due to huge size")do { if (1) { ws_log_full("MATE", LOG_LEVEL_WARNING, "plugins/epan/mate/mate_util.c" , 130, __func__, "mate SCS: string truncated due to huge size" ); } } while (0); | |||
| 131 | } | |||
| 132 | ||||
| 133 | orig = (char *)g_slice_alloc(len); | |||
| 134 | (void) g_strlcpy(orig,s,len); | |||
| 135 | ||||
| 136 | g_hash_table_insert(c->hash,orig,ip); | |||
| 137 | } | |||
| 138 | ||||
| 139 | return orig; | |||
| 140 | } | |||
| 141 | ||||
| 142 | /** | |||
| 143 | * unsubscribe: | |||
| 144 | * @param c the scs hash | |||
| 145 | * @param s a string. | |||
| 146 | * | |||
| 147 | * decreases the count of subscribers, if zero frees the internal copy of | |||
| 148 | * the string. | |||
| 149 | **/ | |||
| 150 | void scs_unsubscribe(SCS_collection* c, char* s) { | |||
| 151 | char* orig = NULL((void*)0); | |||
| 152 | unsigned* ip = NULL((void*)0); | |||
| 153 | size_t len = 0xffff; | |||
| 154 | ||||
| 155 | g_hash_table_lookup_extended(c->hash,(const void *)s,(void * *)&orig,(void * *)&ip); | |||
| 156 | ||||
| 157 | if (ip) { | |||
| 158 | if (*ip == 0) { | |||
| 159 | g_hash_table_remove(c->hash,orig); | |||
| 160 | ||||
| 161 | len = strlen(orig); | |||
| 162 | ||||
| 163 | if (len < SCS_SMALL_SIZE16) { | |||
| 164 | len = SCS_SMALL_SIZE16; | |||
| 165 | } else if (len < SCS_MEDIUM_SIZE256) { | |||
| 166 | len = SCS_MEDIUM_SIZE256; | |||
| 167 | } else if (len < SCS_LARGE_SIZE4096) { | |||
| 168 | len = SCS_LARGE_SIZE4096; | |||
| 169 | } else { | |||
| 170 | len = SCS_HUGE_SIZE65536; | |||
| 171 | } | |||
| 172 | ||||
| 173 | g_slice_free1(len, orig); | |||
| 174 | g_slice_free(unsigned,ip)do { if (1) g_slice_free1 (sizeof (unsigned), (ip)); else (void ) ((unsigned*) 0 == (ip)); } while (0); | |||
| 175 | } | |||
| 176 | else { | |||
| 177 | (*ip)--; | |||
| 178 | } | |||
| 179 | } else { | |||
| 180 | ws_warning("unsubscribe: not subscribed")do { if (1) { ws_log_full("MATE", LOG_LEVEL_WARNING, "plugins/epan/mate/mate_util.c" , 180, __func__, "unsubscribe: not subscribed"); } } while (0 ); | |||
| 181 | } | |||
| 182 | } | |||
| 183 | ||||
| 184 | /** | |||
| 185 | * scs_subscribe_printf: | |||
| 186 | * @param fmt a format string ... | |||
| 187 | * | |||
| 188 | * Formats the input and subscribes it. | |||
| 189 | * | |||
| 190 | * Return value: the stored copy of the formatted string. | |||
| 191 | * | |||
| 192 | **/ | |||
| 193 | char* scs_subscribe_printf(SCS_collection* c, char* fmt, ...) { | |||
| 194 | va_list list; | |||
| 195 | static char buf[SCS_HUGE_SIZE65536]; | |||
| 196 | ||||
| 197 | va_start( list, fmt )__builtin_va_start(list, fmt); | |||
| 198 | vsnprintf(buf, SCS_HUGE_SIZE65536, fmt, list); | |||
| 199 | va_end( list )__builtin_va_end(list); | |||
| 200 | ||||
| 201 | return scs_subscribe(c,buf); | |||
| 202 | } | |||
| 203 | ||||
| 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 | |||
| 211 | * | |||
| 212 | * | |||
| 213 | ***************************************************************************/ | |||
| 214 | ||||
| 215 | ||||
| 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; | |||
| 223 | ||||
| 224 | ||||
| 225 | static SCS_collection* avp_strings; | |||
| 226 | ||||
| 227 | #ifdef _AVP_DEBUGGING | |||
| 228 | static FILE* dbg_fp; | |||
| 229 | ||||
| 230 | static int dbg_level; | |||
| 231 | static int* dbg = &dbg_level; | |||
| 232 | ||||
| 233 | static int dbg_avp_level; | |||
| 234 | static int* dbg_avp = &dbg_avp_level; | |||
| 235 | ||||
| 236 | static int dbg_avp_op_level; | |||
| 237 | static int* dbg_avp_op = &dbg_avp_op_level; | |||
| 238 | ||||
| 239 | static int dbg_avpl_level; | |||
| 240 | static int* dbg_avpl = &dbg_avpl_level; | |||
| 241 | ||||
| 242 | static int dbg_avpl_op_level; | |||
| 243 | static int* dbg_avpl_op = &dbg_avpl_op_level; | |||
| 244 | ||||
| 245 | /** | |||
| 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" | |||
| 253 | * | |||
| 254 | * If enabled sets up the debug facilities for the avp library. | |||
| 255 | * | |||
| 256 | **/ | |||
| 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; | |||
| 264 | } | |||
| 265 | ||||
| 266 | #endif /* _AVP_DEBUGGING */ | |||
| 267 | ||||
| 268 | /** | |||
| 269 | * avp_init: | |||
| 270 | * | |||
| 271 | * (Re)Initializes the avp library. | |||
| 272 | * | |||
| 273 | **/ | |||
| 274 | extern void avp_init(void) { | |||
| 275 | ||||
| 276 | if (avp_strings) destroy_scs_collection(avp_strings); | |||
| 277 | avp_strings = scs_init(); | |||
| 278 | ||||
| 279 | } | |||
| 280 | ||||
| 281 | /** | |||
| 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. | |||
| 285 | * | |||
| 286 | * Creates an avp from a field_info record. | |||
| 287 | * | |||
| 288 | * Return value: a pointer to the newly created avp. | |||
| 289 | * | |||
| 290 | **/ | |||
| 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)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type))); | |||
| 293 | char* value; | |||
| 294 | char* repr; | |||
| 295 | ||||
| 296 | new_avp_val->n = scs_subscribe(avp_strings, name); | |||
| 297 | ||||
| 298 | repr = fvalue_to_string_repr(NULL((void*)0), finfo->value, FTREPR_DISPLAY, finfo->hfinfo->display); | |||
| 299 | ||||
| 300 | if (repr) { | |||
| 301 | value = scs_subscribe(avp_strings, repr); | |||
| 302 | wmem_free(NULL((void*)0), 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, ""); | |||
| 311 | } | |||
| 312 | ||||
| 313 | new_avp_val->v = value; | |||
| 314 | ||||
| 315 | new_avp_val->o = '='; | |||
| 316 | ||||
| 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 | |||
| 320 | ||||
| 321 | return new_avp_val; | |||
| 322 | } | |||
| 323 | ||||
| 324 | ||||
| 325 | /** | |||
| 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. | |||
| 330 | * | |||
| 331 | * Creates an avp given every parameter. | |||
| 332 | * | |||
| 333 | * Return value: a pointer to the newly created avp. | |||
| 334 | * | |||
| 335 | **/ | |||
| 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)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type))); | |||
| 338 | ||||
| 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; | |||
| 342 | ||||
| 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; | |||
| 347 | } | |||
| 348 | ||||
| 349 | ||||
| 350 | /** | |||
| 351 | * delete_avp: | |||
| 352 | * @param avp the avp to delete. | |||
| 353 | * | |||
| 354 | * Destroys an avp and releases the resources it uses. | |||
| 355 | * | |||
| 356 | **/ | |||
| 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 | |||
| 361 | ||||
| 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)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)avp)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)avp )); } while (0); | |||
| 365 | } | |||
| 366 | ||||
| 367 | ||||
| 368 | /** | |||
| 369 | * avp_copy: | |||
| 370 | * @param from the avp to be copied. | |||
| 371 | * | |||
| 372 | * Creates an avp whose name op and value are copies of the given one. | |||
| 373 | * | |||
| 374 | * Return value: a pointer to the newly created avp. | |||
| 375 | * | |||
| 376 | **/ | |||
| 377 | extern AVP* avp_copy(AVP* from) { | |||
| 378 | AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type))); | |||
| 379 | ||||
| 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; | |||
| 383 | ||||
| 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 | |||
| 387 | ||||
| 388 | return new_avp_val; | |||
| 389 | } | |||
| 390 | ||||
| 391 | /** | |||
| 392 | * new_avpl: | |||
| 393 | * @param name the name the avpl will have. | |||
| 394 | * | |||
| 395 | * Creates an empty avpl. | |||
| 396 | * | |||
| 397 | * Return value: a pointer to the newly created avpl. | |||
| 398 | * | |||
| 399 | **/ | |||
| 400 | extern AVPL* new_avpl(const char* name) { | |||
| 401 | AVPL* new_avpl_p = (AVPL*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type))); | |||
| 402 | ||||
| 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 | |||
| 406 | ||||
| 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((void*)0); | |||
| 410 | new_avpl_p->null.next = &new_avpl_p->null; | |||
| 411 | new_avpl_p->null.prev = &new_avpl_p->null; | |||
| 412 | ||||
| 413 | ||||
| 414 | return new_avpl_p; | |||
| 415 | } | |||
| 416 | ||||
| 417 | extern void rename_avpl(AVPL* avpl, char* name) { | |||
| 418 | scs_unsubscribe(avp_strings,avpl->name); | |||
| 419 | avpl->name = scs_subscribe(avp_strings,name); | |||
| 420 | } | |||
| 421 | ||||
| 422 | /** | |||
| 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. | |||
| 428 | * | |||
| 429 | * Pre-condition: the avp is sorted before before_avp and does not already exist | |||
| 430 | * in the avpl. | |||
| 431 | */ | |||
| 432 | static void insert_avp_before_node(AVPL* avpl, AVPN* next_node, AVP *avp, bool_Bool copy_avp) { | |||
| 433 | AVPN* new_avp_val = (AVPN*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type))); | |||
| 434 | ||||
| 435 | new_avp_val->avp = copy_avp ? avp_copy(avp) : avp; | |||
| 436 | ||||
| 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 | |||
| 441 | ||||
| 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; | |||
| 446 | ||||
| 447 | avpl->len++; | |||
| 448 | ||||
| 449 | #ifdef _AVP_DEBUGGING | |||
| 450 | dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len); | |||
| 451 | #endif | |||
| 452 | } | |||
| 453 | ||||
| 454 | /** | |||
| 455 | * insert_avp: | |||
| 456 | * @param avpl the avpl in which to insert. | |||
| 457 | * @param avp the avp to be inserted. | |||
| 458 | * | |||
| 459 | * Inserts the given AVP into the given AVPL if an identical one isn't yet there. | |||
| 460 | * | |||
| 461 | * Return value: whether it was inserted or not. | |||
| 462 | * | |||
| 463 | * BEWARE: Check the return value, you might need to delete the avp if | |||
| 464 | * it is not inserted. | |||
| 465 | **/ | |||
| 466 | extern bool_Bool insert_avp(AVPL* avpl, AVP* avp) { | |||
| 467 | AVPN* c; | |||
| 468 | ||||
| 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 | |||
| 472 | ||||
| 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); | |||
| 476 | ||||
| 477 | if (name_diff == 0) { | |||
| 478 | int value_diff = strcmp(avp->v, c->avp->v); | |||
| 479 | ||||
| 480 | if (value_diff < 0) { | |||
| 481 | break; | |||
| 482 | } | |||
| 483 | ||||
| 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 false0; | |||
| 491 | } | |||
| 492 | } | |||
| 493 | } | |||
| 494 | ||||
| 495 | if (name_diff < 0) { | |||
| 496 | break; | |||
| 497 | } | |||
| 498 | } | |||
| 499 | ||||
| 500 | insert_avp_before_node(avpl, c, avp, false0); | |||
| 501 | ||||
| 502 | return true1; | |||
| 503 | } | |||
| 504 | ||||
| 505 | /** | |||
| 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. | |||
| 510 | * | |||
| 511 | * Gets pointer to the next avp whose name is given; uses cookie to store its | |||
| 512 | * state between calls. | |||
| 513 | * | |||
| 514 | * Return value: a pointer to the next matching avp if there's one, else NULL. | |||
| 515 | * | |||
| 516 | **/ | |||
| 517 | extern AVP* get_avp_by_name(AVPL* avpl, char* name, void** cookie) { | |||
| 518 | AVPN* curr; | |||
| 519 | AVPN* start = (AVPN*) *cookie; | |||
| 520 | ||||
| 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 | |||
| 524 | ||||
| 525 | name = scs_subscribe(avp_strings, name); | |||
| 526 | ||||
| 527 | if (!start) start = avpl->null.next; | |||
| 528 | ||||
| 529 | for ( curr = start; curr->avp; curr = curr->next ) { | |||
| 530 | if ( curr->avp->n == name ) { | |||
| 531 | break; | |||
| 532 | } | |||
| 533 | } | |||
| 534 | ||||
| 535 | *cookie = curr; | |||
| 536 | ||||
| 537 | #ifdef _AVP_DEBUGGING | |||
| 538 | dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %p",curr); | |||
| 539 | #endif | |||
| 540 | ||||
| 541 | scs_unsubscribe(avp_strings, name); | |||
| 542 | ||||
| 543 | return curr->avp; | |||
| 544 | } | |||
| 545 | ||||
| 546 | /** | |||
| 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. | |||
| 550 | * | |||
| 551 | * Extracts from the avpl the next avp whose name is given; | |||
| 552 | * | |||
| 553 | * Return value: a pointer to extracted avp if there's one, else NULL. | |||
| 554 | * | |||
| 555 | **/ | |||
| 556 | extern AVP* extract_avp_by_name(AVPL* avpl, char* name) { | |||
| 557 | AVPN* curr; | |||
| 558 | AVP* avp = NULL((void*)0); | |||
| 559 | ||||
| 560 | #ifdef _AVP_DEBUGGING | |||
| 561 | dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %p %s",avpl,name); | |||
| 562 | #endif | |||
| 563 | ||||
| 564 | name = scs_subscribe(avp_strings, name); | |||
| 565 | ||||
| 566 | for ( curr = avpl->null.next; curr->avp; curr = curr->next ) { | |||
| 567 | if ( curr->avp->n == name ) { | |||
| 568 | break; | |||
| 569 | } | |||
| 570 | } | |||
| 571 | ||||
| 572 | scs_unsubscribe(avp_strings, name); | |||
| 573 | ||||
| 574 | if( ! curr->avp ) return NULL((void*)0); | |||
| 575 | ||||
| 576 | curr->next->prev = curr->prev; | |||
| 577 | curr->prev->next = curr->next; | |||
| 578 | ||||
| 579 | avp = curr->avp; | |||
| 580 | ||||
| 581 | g_slice_free(any_avp_type,(any_avp_type*)curr)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)curr)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)curr )); } while (0); | |||
| 582 | ||||
| 583 | (avpl->len)--; | |||
| 584 | ||||
| 585 | #ifdef _AVP_DEBUGGING | |||
| 586 | dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len); | |||
| 587 | #endif | |||
| 588 | ||||
| 589 | #ifdef _AVP_DEBUGGING | |||
| 590 | dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %p",avp); | |||
| 591 | #endif | |||
| 592 | ||||
| 593 | return avp; | |||
| 594 | } | |||
| 595 | ||||
| 596 | ||||
| 597 | /** | |||
| 598 | * extract_first_avp: | |||
| 599 | * @param avpl the avpl from which to try to extract the avp. | |||
| 600 | * | |||
| 601 | * Extracts the first avp from the avpl. | |||
| 602 | * | |||
| 603 | * Return value: a pointer to extracted avp if there's one, else NULL. | |||
| 604 | * | |||
| 605 | **/ | |||
| 606 | extern AVP* extract_first_avp(AVPL* avpl) { | |||
| 607 | AVP* avp; | |||
| 608 | AVPN* node; | |||
| 609 | ||||
| 610 | #ifdef _AVP_DEBUGGING | |||
| 611 | dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %p",avpl); | |||
| 612 | #endif | |||
| 613 | ||||
| 614 | node = avpl->null.next; | |||
| 615 | ||||
| 616 | avpl->null.next->prev = &avpl->null; | |||
| 617 | avpl->null.next = node->next; | |||
| 618 | ||||
| 619 | avp = node->avp; | |||
| 620 | ||||
| 621 | if (avp) { | |||
| 622 | g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node )); } while (0); | |||
| 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 | |||
| 627 | } | |||
| 628 | ||||
| 629 | #ifdef _AVP_DEBUGGING | |||
| 630 | dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %p",avp); | |||
| 631 | #endif | |||
| 632 | ||||
| 633 | return avp; | |||
| 634 | ||||
| 635 | } | |||
| 636 | ||||
| 637 | ||||
| 638 | /** | |||
| 639 | * extract_last_avp: | |||
| 640 | * @param avpl the avpl from which to try to extract the avp. | |||
| 641 | * | |||
| 642 | * Extracts the last avp from the avpl. | |||
| 643 | * | |||
| 644 | * Return value: a pointer to extracted avp if there's one, else NULL. | |||
| 645 | * | |||
| 646 | **/ | |||
| 647 | extern AVP* extract_last_avp(AVPL* avpl) { | |||
| 648 | AVP* avp; | |||
| 649 | AVPN* node; | |||
| 650 | ||||
| 651 | node = avpl->null.prev; | |||
| 652 | ||||
| 653 | avpl->null.prev->next = &avpl->null; | |||
| 654 | avpl->null.prev = node->prev; | |||
| 655 | ||||
| 656 | avp = node->avp; | |||
| 657 | ||||
| 658 | if (avp) { | |||
| 659 | g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node )); } while (0); | |||
| 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 | |||
| 664 | } | |||
| 665 | ||||
| 666 | #ifdef _AVP_DEBUGGING | |||
| 667 | dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",avp); | |||
| 668 | #endif | |||
| 669 | ||||
| 670 | return avp; | |||
| 671 | ||||
| 672 | } | |||
| 673 | ||||
| 674 | ||||
| 675 | /** | |||
| 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. | |||
| 679 | * | |||
| 680 | * Destroys an avpl and releases the resources it uses. If told to do | |||
| 681 | * so releases the avps as well. | |||
| 682 | * | |||
| 683 | **/ | |||
| 684 | extern void delete_avpl(AVPL* avpl, bool_Bool avps_too) { | |||
| 685 | AVP* avp; | |||
| 686 | #ifdef _AVP_DEBUGGING | |||
| 687 | dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %p",avpl); | |||
| 688 | #endif | |||
| 689 | ||||
| 690 | while(( avp = extract_last_avp(avpl))) { | |||
| 691 | if (avps_too) { | |||
| 692 | delete_avp(avp); | |||
| 693 | } | |||
| 694 | } | |||
| 695 | ||||
| 696 | scs_unsubscribe(avp_strings,avpl->name); | |||
| 697 | g_slice_free(any_avp_type,(any_avp_type*)avpl)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)avpl)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)avpl )); } while (0); | |||
| 698 | } | |||
| 699 | ||||
| 700 | ||||
| 701 | ||||
| 702 | /** | |||
| 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. | |||
| 706 | * | |||
| 707 | * Iterates on an avpl to get its avps. | |||
| 708 | * | |||
| 709 | * Return value: a pointer to the next avp if there's one, else NULL. | |||
| 710 | * | |||
| 711 | **/ | |||
| 712 | extern AVP* get_next_avp(AVPL* avpl, void** cookie) { | |||
| 713 | AVPN* node; | |||
| 714 | ||||
| 715 | #ifdef _AVP_DEBUGGING | |||
| 716 | dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %p avpn: %p",avpl,*cookie); | |||
| 717 | #endif | |||
| 718 | ||||
| 719 | if (*cookie) { | |||
| 720 | node = (AVPN*) *cookie; | |||
| 721 | } else { | |||
| 722 | node = avpl->null.next; | |||
| 723 | } | |||
| 724 | ||||
| 725 | *cookie = node->next; | |||
| 726 | ||||
| 727 | #ifdef _AVP_DEBUGGING | |||
| 728 | dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",node->avp); | |||
| 729 | #endif | |||
| 730 | ||||
| 731 | return node->avp; | |||
| 732 | } | |||
| 733 | ||||
| 734 | /** | |||
| 735 | * avpl_to_str: | |||
| 736 | * @param avpl the avpl to represent. | |||
| 737 | * | |||
| 738 | * Creates a newly allocated string containing a representation of an avpl. | |||
| 739 | * | |||
| 740 | * Return value: a pointer to the newly allocated string. | |||
| 741 | * | |||
| 742 | **/ | |||
| 743 | char* avpl_to_str(AVPL* avpl) { | |||
| 744 | AVPN* c; | |||
| 745 | GString* s = g_string_new(""); | |||
| 746 | char* avp_s; | |||
| 747 | char* r; | |||
| 748 | ||||
| 749 | for(c=avpl->null.next; c->avp; c = c->next) { | |||
| 750 | avp_s = avp_to_str(c->avp)(wmem_strdup_printf(((void*)0), "%s%c%s",c->avp->n,c-> avp->o,c->avp->v)); | |||
| 751 | g_string_append_printf(s," %s;",avp_s); | |||
| 752 | g_free(avp_s); | |||
| 753 | } | |||
| 754 | ||||
| 755 | r = g_string_free(s,FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((s), ( (0))) : g_string_free_and_steal (s)) : (g_string_free) ((s), ( (0)))); | |||
| 756 | ||||
| 757 | /* g_strchug(r); ? */ | |||
| 758 | return r; | |||
| 759 | } | |||
| 760 | ||||
| 761 | extern char* avpl_to_dotstr(AVPL* avpl) { | |||
| 762 | AVPN* c; | |||
| 763 | GString* s = g_string_new(""); | |||
| 764 | char* avp_s; | |||
| 765 | char* r; | |||
| 766 | ||||
| 767 | for(c=avpl->null.next; c->avp; c = c->next) { | |||
| 768 | avp_s = avp_to_str(c->avp)(wmem_strdup_printf(((void*)0), "%s%c%s",c->avp->n,c-> avp->o,c->avp->v)); | |||
| 769 | g_string_append_printf(s," .%s;",avp_s); | |||
| 770 | g_free(avp_s); | |||
| 771 | } | |||
| 772 | ||||
| 773 | r = g_string_free(s,FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((s), ( (0))) : g_string_free_and_steal (s)) : (g_string_free) ((s), ( (0)))); | |||
| 774 | ||||
| 775 | /* g_strchug(r); ? */ | |||
| 776 | return r; | |||
| 777 | } | |||
| 778 | ||||
| 779 | /** | |||
| 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. | |||
| 784 | * | |||
| 785 | * Adds the avps of src that are not existent in dst into dst. | |||
| 786 | * | |||
| 787 | **/ | |||
| 788 | extern void merge_avpl(AVPL* dst, AVPL* src, bool_Bool copy_avps) { | |||
| 789 | AVPN* cd = NULL((void*)0); | |||
| 790 | AVPN* cs = NULL((void*)0); | |||
| 791 | ||||
| 792 | #ifdef _AVP_DEBUGGING | |||
| 793 | dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %p %p",dst,src); | |||
| 794 | #endif | |||
| 795 | ||||
| 796 | cs = src->null.next; | |||
| 797 | cd = dst->null.next; | |||
| 798 | ||||
| 799 | while (cs->avp && cd->avp) { | |||
| 800 | ||||
| 801 | int name_diff = strcmp(cd->avp->n, cs->avp->n); | |||
| 802 | ||||
| 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); | |||
| 813 | ||||
| 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; | |||
| 824 | } | |||
| 825 | } | |||
| 826 | } | |||
| 827 | ||||
| 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; | |||
| 834 | } | |||
| 835 | ||||
| 836 | #ifdef _AVP_DEBUGGING | |||
| 837 | dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done"); | |||
| 838 | #endif | |||
| 839 | ||||
| 840 | return; | |||
| 841 | } | |||
| 842 | ||||
| 843 | ||||
| 844 | /** | |||
| 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. | |||
| 849 | * | |||
| 850 | * Creates a new avpl containing the same avps as the given avpl | |||
| 851 | * It will either reference or copy the avps. | |||
| 852 | * | |||
| 853 | * Return value: a pointer to the newly allocated string. | |||
| 854 | * | |||
| 855 | **/ | |||
| 856 | extern AVPL* new_avpl_from_avpl(const char* name, AVPL* avpl, bool_Bool copy_avps) { | |||
| 857 | AVPL* newavpl = new_avpl(name); | |||
| 858 | void* cookie = NULL((void*)0); | |||
| 859 | AVP* avp; | |||
| 860 | AVP* copy; | |||
| 861 | ||||
| 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 | |||
| 865 | ||||
| 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); | |||
| 871 | } | |||
| 872 | } else { | |||
| 873 | insert_avp(newavpl,avp); | |||
| 874 | } | |||
| 875 | } | |||
| 876 | ||||
| 877 | #ifdef _AVP_DEBUGGING | |||
| 878 | dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done"); | |||
| 879 | #endif | |||
| 880 | ||||
| 881 | return newavpl; | |||
| 882 | } | |||
| 883 | ||||
| 884 | /** | |||
| 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 | |||
| 888 | * | |||
| 889 | * Checks whether or not two avp's match. | |||
| 890 | * | |||
| 891 | * Return value: a pointer to the src avp if there's a match. | |||
| 892 | * | |||
| 893 | **/ | |||
| 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_Bool lower = false0; | |||
| 903 | ||||
| 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 | |||
| 907 | ||||
| 908 | if ( src->n != op->n ) { | |||
| 909 | return NULL((void*)0); | |||
| 910 | } | |||
| 911 | ||||
| 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((void*)0); | |||
| 917 | case AVP_OP_NOTEQUAL'!': | |||
| 918 | return !( src->v == op->v) ? src : NULL((void*)0); | |||
| 919 | case AVP_OP_STARTS'^': | |||
| 920 | return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL((void*)0); | |||
| 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)(strcmp ((const char *) (splited[i]), (const char *) (src-> v)) == 0)) { | |||
| 926 | g_strfreev(splited); | |||
| 927 | return src; | |||
| 928 | } | |||
| 929 | } | |||
| 930 | g_strfreev(splited); | |||
| 931 | } | |||
| 932 | return NULL((void*)0); | |||
| 933 | ||||
| 934 | case AVP_OP_LOWER'<': | |||
| 935 | lower = true1; | |||
| 936 | /* FALLTHRU */ | |||
| 937 | case AVP_OP_HIGHER'>': | |||
| 938 | ||||
| 939 | fs = g_ascii_strtod(src->v, NULL((void*)0)); | |||
| 940 | fo = g_ascii_strtod(op->v, NULL((void*)0)); | |||
| 941 | ||||
| 942 | if (lower) { | |||
| 943 | if (fs<fo) return src; | |||
| 944 | else return NULL((void*)0); | |||
| 945 | } else { | |||
| 946 | if (fs>fo) return src; | |||
| 947 | else return NULL((void*)0); | |||
| 948 | } | |||
| 949 | case AVP_OP_ENDS'$': | |||
| 950 | /* does this work? */ | |||
| 951 | ls = (unsigned) strlen(src->v); | |||
| 952 | lo = (unsigned) strlen(op->v); | |||
| 953 | ||||
| 954 | if ( ls < lo ) { | |||
| 955 | return NULL((void*)0); | |||
| 956 | } else { | |||
| 957 | p = src->v + ( ls - lo ); | |||
| 958 | return g_str_equal(p,op->v)(strcmp ((const char *) (p), (const char *) (op->v)) == 0) ? src : NULL((void*)0); | |||
| 959 | } | |||
| 960 | ||||
| 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((void*)0); | |||
| 965 | } | |||
| 966 | /* will never get here */ | |||
| 967 | return NULL((void*)0); | |||
| 968 | } | |||
| 969 | ||||
| 970 | ||||
| 971 | ||||
| 972 | /** | |||
| 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 | |||
| 978 | * | |||
| 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. | |||
| 981 | * | |||
| 982 | * Note: Loose will always be considered a successful match, it matches zero or | |||
| 983 | * more conditions. | |||
| 984 | */ | |||
| 985 | extern AVPL* new_avpl_loose_match(const char* name, | |||
| 986 | AVPL* src, | |||
| 987 | AVPL* op, | |||
| 988 | bool_Bool copy_avps) { | |||
| 989 | ||||
| 990 | AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name)); | |||
| 991 | AVPN* co = NULL((void*)0); | |||
| 992 | AVPN* cs = NULL((void*)0); | |||
| 993 | ||||
| 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 | |||
| 997 | ||||
| 998 | ||||
| 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); | |||
| 1003 | ||||
| 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; | |||
| 1017 | } | |||
| 1018 | cond = cond->next; | |||
| 1019 | } while (cond->avp && cond->avp->n == cs->avp->n); | |||
| 1020 | cs = cs->next; | |||
| 1021 | } | |||
| 1022 | } | |||
| 1023 | ||||
| 1024 | // return matches (possible none) | |||
| 1025 | return newavpl; | |||
| 1026 | } | |||
| 1027 | ||||
| 1028 | /** | |||
| 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 | |||
| 1037 | * | |||
| 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). | |||
| 1043 | * | |||
| 1044 | * Return value: a pointer to the newly created avpl containing the | |||
| 1045 | * matching avps or NULL if there is no match. | |||
| 1046 | */ | |||
| 1047 | extern AVPL* new_avpl_pairs_match(const char* name, AVPL* src, AVPL* op, bool_Bool strict, bool_Bool copy_avps) { | |||
| 1048 | AVPL* newavpl; | |||
| 1049 | AVPN* co = NULL((void*)0); | |||
| 1050 | AVPN* cs = NULL((void*)0); | |||
| 1051 | const char *last_match = NULL((void*)0); | |||
| 1052 | bool_Bool matched = true1; | |||
| 1053 | ||||
| 1054 | newavpl = new_avpl(scs_subscribe(avp_strings, name)); | |||
| 1055 | ||||
| 1056 | #ifdef _AVP_DEBUGGING | |||
| 1057 | dbg_print(dbg_avpl_op,3,dbg_fp,"%s: %p src=%p op=%p name='%s'",G_STRFUNC((const char*) (__func__)),newavpl,src,op,name); | |||
| 1058 | #endif | |||
| 1059 | ||||
| 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((void*)0); | |||
| 1065 | ||||
| 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; | |||
| 1081 | } | |||
| 1082 | co = co->next; | |||
| 1083 | } | |||
| 1084 | ||||
| 1085 | // condition did not match, check if we can continue matching. | |||
| 1086 | if (failed_match) { | |||
| 1087 | if (strict) { | |||
| 1088 | matched = false0; | |||
| 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 = false0; | |||
| 1094 | break; | |||
| 1095 | } | |||
| 1096 | } | |||
| 1097 | } | |||
| 1098 | } | |||
| 1099 | ||||
| 1100 | // if there are any conditions remaining, then those could not be matched | |||
| 1101 | if (matched && strict && co->avp) { | |||
| 1102 | matched = false0; | |||
| 1103 | } | |||
| 1104 | ||||
| 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((void*)0); | |||
| 1112 | } | |||
| 1113 | } | |||
| 1114 | ||||
| 1115 | ||||
| 1116 | /** | |||
| 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 | |||
| 1122 | * | |||
| 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. | |||
| 1126 | */ | |||
| 1127 | extern AVPL* new_avpl_from_match(avpl_match_mode mode, const char* name,AVPL* src, AVPL* op, bool_Bool copy_avps) { | |||
| 1128 | AVPL* avpl = NULL((void*)0); | |||
| 1129 | ||||
| 1130 | switch (mode) { | |||
| 1131 | case AVPL_STRICT: | |||
| 1132 | avpl = new_avpl_pairs_match(name, src, op, true1, 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, false0, 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; | |||
| 1145 | } | |||
| 1146 | ||||
| 1147 | return avpl; | |||
| 1148 | } | |||
| 1149 | ||||
| 1150 | /** | |||
| 1151 | * delete_avpl_transform: | |||
| 1152 | * @param op a pointer to the avpl transformation object | |||
| 1153 | * | |||
| 1154 | * Destroys an avpl transformation object and releases all the resources it | |||
| 1155 | * uses. | |||
| 1156 | * | |||
| 1157 | **/ | |||
| 1158 | extern void delete_avpl_transform(AVPL_Transf* op) { | |||
| 1159 | AVPL_Transf* next; | |||
| 1160 | ||||
| 1161 | for (; op ; op = next) { | |||
| 1162 | next = op->next; | |||
| 1163 | ||||
| 1164 | g_free(op->name); | |||
| 1165 | ||||
| 1166 | if (op->match) { | |||
| 1167 | delete_avpl(op->match,true1); | |||
| 1168 | } | |||
| 1169 | ||||
| 1170 | if (op->replace) { | |||
| 1171 | delete_avpl(op->replace,true1); | |||
| 1172 | } | |||
| 1173 | ||||
| 1174 | g_free(op); | |||
| 1175 | } | |||
| 1176 | ||||
| 1177 | } | |||
| 1178 | ||||
| 1179 | ||||
| 1180 | /** | |||
| 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. | |||
| 1184 | * | |||
| 1185 | * Applies the "op" transformation to an avpl, matches it and eventually | |||
| 1186 | * replaces or inserts the transformed avps. | |||
| 1187 | * | |||
| 1188 | * Return value: whether the transformation was performed or not. | |||
| 1189 | **/ | |||
| 1190 | extern void avpl_transform(AVPL* src, AVPL_Transf* op) { | |||
| 1191 | AVPL* avpl = NULL((void*)0); | |||
| 1192 | AVPN* cs; | |||
| 1193 | AVPN* cm; | |||
| 1194 | AVPN* n; | |||
| 1195 | ||||
| 1196 | #ifdef _AVP_DEBUGGING | |||
| 1197 | dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%p op=%p",src,op); | |||
| 1198 | #endif | |||
| 1199 | ||||
| 1200 | for ( ; op ; op = op->next) { | |||
| 1201 | ||||
| 1202 | avpl = new_avpl_from_match(op->match_mode, src->name,src, op->match, true1); | |||
| 1203 | ||||
| 1204 | if (avpl) { | |||
| 1205 | switch (op->replace_mode) { | |||
| 1206 | case AVPL_NO_REPLACE: | |||
| 1207 | delete_avpl(avpl,true1); | |||
| 1208 | return; | |||
| 1209 | case AVPL_INSERT: | |||
| 1210 | merge_avpl(src,op->replace,true1); | |||
| 1211 | delete_avpl(avpl,true1); | |||
| 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; | |||
| 1221 | ||||
| 1222 | cs->prev->next = cs->next; | |||
| 1223 | cs->next->prev = cs->prev; | |||
| 1224 | ||||
| 1225 | if (cs->avp) | |||
| 1226 | delete_avp(cs->avp); | |||
| 1227 | g_slice_free(any_avp_type,(any_avp_type*)cs)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)cs)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)cs) ); } while (0); | |||
| 1228 | ||||
| 1229 | cs = n; | |||
| 1230 | cm = cm->next; | |||
| 1231 | } else { | |||
| 1232 | // Current matched AVP is not equal to the current | |||
| 1233 | // source AVP. Since there must be a source AVP for | |||
| 1234 | // each matched AVP, advance current source and not | |||
| 1235 | // the match AVP. | |||
| 1236 | cs = cs->next; | |||
| 1237 | } | |||
| 1238 | } | |||
| 1239 | ||||
| 1240 | merge_avpl(src,op->replace,true1); | |||
| 1241 | delete_avpl(avpl,true1); | |||
| 1242 | return; | |||
| 1243 | } | |||
| 1244 | } | |||
| 1245 | } | |||
| 1246 | } | |||
| 1247 | ||||
| 1248 | ||||
| 1249 | /** | |||
| 1250 | * new_loal: | |||
| 1251 | * @param name the name the loal will take. | |||
| 1252 | * | |||
| 1253 | * Creates an empty list of avp lists. | |||
| 1254 | * | |||
| 1255 | * Return value: a pointer to the newly created loal. | |||
| 1256 | **/ | |||
| 1257 | extern LoAL* new_loal(const char* name) { | |||
| 1258 | LoAL* new_loal_p = (LoAL*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type))); | |||
| 1259 | ||||
| 1260 | if (! name) { | |||
| 1261 | name = "anonymous"; | |||
| 1262 | } | |||
| 1263 | ||||
| 1264 | #ifdef _AVP_DEBUGGING | |||
| 1265 | dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_p: %p name=%s",new_loal_p,name); | |||
| 1266 | #endif | |||
| 1267 | ||||
| 1268 | new_loal_p->name = scs_subscribe(avp_strings,name); | |||
| 1269 | new_loal_p->null.avpl = NULL((void*)0); | |||
| 1270 | new_loal_p->null.next = &new_loal_p->null; | |||
| 1271 | new_loal_p->null.prev = &new_loal_p->null; | |||
| 1272 | new_loal_p->len = 0; | |||
| 1273 | return new_loal_p; | |||
| 1274 | } | |||
| 1275 | ||||
| 1276 | /** | |||
| 1277 | * loal_append: | |||
| 1278 | * @param loal the loal on which to operate. | |||
| 1279 | * @param avpl the avpl to append. | |||
| 1280 | * | |||
| 1281 | * Appends an avpl to a loal. | |||
| 1282 | * | |||
| 1283 | **/ | |||
| 1284 | extern void loal_append(LoAL* loal, AVPL* avpl) { | |||
| 1285 | LoALnode* node = (LoALnode*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type))); | |||
| 1286 | ||||
| 1287 | #ifdef _AVP_DEBUGGING | |||
| 1288 | dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %p",node); | |||
| 1289 | #endif | |||
| 1290 | ||||
| 1291 | node->avpl = avpl; | |||
| 1292 | node->next = &loal->null; | |||
| 1293 | node->prev = loal->null.prev; | |||
| 1294 | ||||
| 1295 | loal->null.prev->next = node; | |||
| 1296 | loal->null.prev = node; | |||
| 1297 | loal->len++; | |||
| 1298 | } | |||
| 1299 | ||||
| 1300 | ||||
| 1301 | /** | |||
| 1302 | * extract_first_avpl: | |||
| 1303 | * @param loal the loal on which to operate. | |||
| 1304 | * | |||
| 1305 | * Extracts the first avpl contained in a loal. | |||
| 1306 | * | |||
| 1307 | * Return value: a pointer to the extracted avpl. | |||
| 1308 | * | |||
| 1309 | **/ | |||
| 1310 | extern AVPL* extract_first_avpl(LoAL* loal) { | |||
| 1311 | LoALnode* node; | |||
| 1312 | AVPL* avpl; | |||
| 1313 | ||||
| 1314 | #ifdef _AVP_DEBUGGING | |||
| 1315 | dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name); | |||
| 1316 | #endif | |||
| 1317 | ||||
| 1318 | node = loal->null.next; | |||
| 1319 | ||||
| 1320 | loal->null.next->next->prev = &loal->null; | |||
| 1321 | loal->null.next = node->next; | |||
| 1322 | ||||
| 1323 | loal->len--; | |||
| 1324 | ||||
| 1325 | avpl = node->avpl; | |||
| 1326 | ||||
| 1327 | if ( avpl ) { | |||
| 1328 | g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node )); } while (0); | |||
| 1329 | ||||
| 1330 | #ifdef _AVP_DEBUGGING | |||
| 1331 | dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name); | |||
| 1332 | dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node); | |||
| 1333 | #endif | |||
| 1334 | } | |||
| 1335 | ||||
| 1336 | return avpl; | |||
| 1337 | } | |||
| 1338 | ||||
| 1339 | /** | |||
| 1340 | * extract_first_avpl: | |||
| 1341 | * @param loal the loal on which to operate. | |||
| 1342 | * | |||
| 1343 | * Extracts the last avpl contained in a loal. | |||
| 1344 | * | |||
| 1345 | * Return value: a pointer to the extracted avpl. | |||
| 1346 | * | |||
| 1347 | **/ | |||
| 1348 | extern AVPL* extract_last_avpl(LoAL* loal){ | |||
| 1349 | LoALnode* node; | |||
| 1350 | AVPL* avpl; | |||
| 1351 | ||||
| 1352 | node = loal->null.prev; | |||
| 1353 | ||||
| 1354 | loal->null.prev->prev->next = &loal->null; | |||
| 1355 | loal->null.prev = node->prev; | |||
| 1356 | ||||
| 1357 | loal->len--; | |||
| 1358 | ||||
| 1359 | avpl = node->avpl; | |||
| 1360 | ||||
| 1361 | if ( avpl ) { | |||
| 1362 | g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node )); } while (0); | |||
| 1363 | #ifdef _AVP_DEBUGGING | |||
| 1364 | dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node); | |||
| 1365 | #endif | |||
| 1366 | } | |||
| 1367 | ||||
| 1368 | return avpl; | |||
| 1369 | } | |||
| 1370 | ||||
| 1371 | /** | |||
| 1372 | * extract_first_avpl: | |||
| 1373 | * @param loal the loal on which to operate. | |||
| 1374 | * @param cookie pointer to the pointer variable to contain the state between calls | |||
| 1375 | * | |||
| 1376 | * At each call will return the following avpl from a loal. The given cookie | |||
| 1377 | * will be used to manatain the state between calls. | |||
| 1378 | * | |||
| 1379 | * Return value: a pointer to the next avpl. | |||
| 1380 | * | |||
| 1381 | **/ | |||
| 1382 | extern AVPL* get_next_avpl(LoAL* loal,void** cookie) { | |||
| 1383 | LoALnode* node; | |||
| 1384 | ||||
| 1385 | #ifdef _AVP_DEBUGGING | |||
| 1386 | dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%p node=%p",loal,*cookie); | |||
| 1387 | #endif | |||
| 1388 | ||||
| 1389 | if (*cookie) { | |||
| 1390 | node = (LoALnode*) *cookie; | |||
| 1391 | } else { | |||
| 1392 | node = loal->null.next; | |||
| 1393 | } | |||
| 1394 | ||||
| 1395 | *cookie = node->next; | |||
| 1396 | ||||
| 1397 | return node->avpl; | |||
| 1398 | } | |||
| 1399 | ||||
| 1400 | /** | |||
| 1401 | * delete_loal: | |||
| 1402 | * @param loal the loal to be deleted. | |||
| 1403 | * @param avpls_too whether avpls contained by the loal should be deleted as well | |||
| 1404 | * @param avps_too whether avps contained by the avpls should be also deleted | |||
| 1405 | * | |||
| 1406 | * Destroys a loal and eventually desstroys avpls and avps. | |||
| 1407 | * | |||
| 1408 | **/ | |||
| 1409 | extern void delete_loal(LoAL* loal, bool_Bool avpls_too, bool_Bool avps_too) { | |||
| 1410 | AVPL* avpl; | |||
| 1411 | ||||
| 1412 | #ifdef _AVP_DEBUGGING | |||
| 1413 | dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %p",loal); | |||
| 1414 | #endif | |||
| 1415 | ||||
| 1416 | while(( avpl = extract_last_avpl(loal) )) { | |||
| 1417 | if (avpls_too) { | |||
| 1418 | delete_avpl(avpl,avps_too); | |||
| 1419 | } | |||
| 1420 | } | |||
| 1421 | ||||
| 1422 | scs_unsubscribe(avp_strings,loal->name); | |||
| 1423 | g_slice_free(any_avp_type,(any_avp_type*)loal)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)loal)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)loal )); } while (0); | |||
| 1424 | } | |||
| 1425 | ||||
| 1426 | ||||
| 1427 | ||||
| 1428 | /**************************************************************************** | |||
| 1429 | ******************* the following are used in load_loal_from_file | |||
| 1430 | ****************************************************************************/ | |||
| 1431 | ||||
| 1432 | /** | |||
| 1433 | * load_loal_error: | |||
| 1434 | * Used by loal_from_file to handle errors while loading. | |||
| 1435 | **/ | |||
| 1436 | static LoAL* load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, const char* fmt, ...) { | |||
| 1437 | va_list list; | |||
| 1438 | char* desc; | |||
| 1439 | LoAL* ret = NULL((void*)0); | |||
| 1440 | char* err; | |||
| 1441 | ||||
| 1442 | va_start( list, fmt )__builtin_va_start(list, fmt); | |||
| 1443 | desc = ws_strdup_vprintf(fmt, list)wmem_strdup_vprintf(((void*)0), fmt, list); | |||
| 1444 | va_end( list )__builtin_va_end(list); | |||
| 1445 | ||||
| 1446 | if (loal) { | |||
| 1447 | err = ws_strdup_printf("Error Loading LoAL from file: in %s at line: %i, %s",loal->name,linenum,desc)wmem_strdup_printf(((void*)0), "Error Loading LoAL from file: in %s at line: %i, %s" ,loal->name,linenum,desc); | |||
| 1448 | } else { | |||
| 1449 | err = ws_strdup_printf("Error Loading LoAL at line: %i, %s",linenum,desc)wmem_strdup_printf(((void*)0), "Error Loading LoAL at line: %i, %s" ,linenum,desc); | |||
| 1450 | } | |||
| 1451 | ret = new_loal(err); | |||
| 1452 | ||||
| 1453 | g_free(desc); | |||
| 1454 | g_free(err); | |||
| 1455 | ||||
| 1456 | if (fp) fclose(fp); | |||
| 1457 | if (loal) delete_loal(loal,true1,true1); | |||
| 1458 | if (curr) delete_avpl(curr,true1); | |||
| 1459 | ||||
| 1460 | return ret; | |||
| 1461 | } | |||
| 1462 | ||||
| 1463 | ||||
| 1464 | /* the maximum length allowed for a line */ | |||
| 1465 | #define MAX_ITEM_LEN8192 8192 | |||
| 1466 | ||||
| 1467 | /* this two ugly things are used for tokenizing */ | |||
| 1468 | #define AVP_OP_CHAR'=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!' '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!' | |||
| 1469 | ||||
| 1470 | #define AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G' : case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T' :case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g' : case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z' : case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':case '7': case '8': case '9': case '.' 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\ | |||
| 1471 | case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\ | |||
| 1472 | case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\ | |||
| 1473 | case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\ | |||
| 1474 | case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\ | |||
| 1475 | case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\ | |||
| 1476 | case '7': case '8': case '9': case '.' | |||
| 1477 | ||||
| 1478 | ||||
| 1479 | /** | |||
| 1480 | * loal_from_file: | |||
| 1481 | * @param filename the file containing a loals text representation. | |||
| 1482 | * | |||
| 1483 | * Given a filename it will attempt to load a loal containing a copy of | |||
| 1484 | * the avpls represented in the file. | |||
| 1485 | * | |||
| 1486 | * Return value: if successful a pointer to the new populated loal, else NULL. | |||
| 1487 | * | |||
| 1488 | **/ | |||
| 1489 | extern LoAL* loal_from_file(char* filename) { | |||
| 1490 | FILE *fp = NULL((void*)0); | |||
| 1491 | char c; | |||
| 1492 | int i = 0; | |||
| 1493 | uint32_t linenum = 1; | |||
| 1494 | char *linenum_buf; | |||
| 1495 | char *name; | |||
| 1496 | char *value; | |||
| 1497 | char op = '?'; | |||
| 1498 | LoAL *loal_error, *loal = new_loal(filename); | |||
| 1499 | AVPL* curr = NULL((void*)0); | |||
| 1500 | AVP* avp; | |||
| 1501 | ||||
| 1502 | enum _load_loal_states { | |||
| 1503 | START, | |||
| 1504 | BEFORE_NAME, | |||
| 1505 | IN_NAME, | |||
| 1506 | IN_VALUE, | |||
| 1507 | MY_IGNORE | |||
| 1508 | } state; | |||
| 1509 | ||||
| 1510 | linenum_buf = (char*)g_malloc(MAX_ITEM_LEN8192); | |||
| 1511 | name = (char*)g_malloc(MAX_ITEM_LEN8192); | |||
| 1512 | value = (char*)g_malloc(MAX_ITEM_LEN8192); | |||
| 1513 | #ifndef _WIN32 | |||
| 1514 | if (! getuid()) { | |||
| ||||
| 1515 | loal_error = load_loal_error(fp,loal,curr,linenum,"MATE Will not run as root"); | |||
| 1516 | goto error; | |||
| 1517 | } | |||
| 1518 | #endif | |||
| 1519 | ||||
| 1520 | state = START; | |||
| 1521 | ||||
| 1522 | if (( fp = ws_fopenfopen(filename,"r") )) { | |||
| 1523 | while(( c = (char) fgetc(fp) )){ | |||
| 1524 | ||||
| 1525 | if ( feof(fp) ) { | |||
| 1526 | if ( ferror(fp) ) { | |||
| 1527 | report_read_failure(filename,errno(*__errno_location ())); | |||
| 1528 | loal_error = load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename); | |||
| 1529 | goto error; | |||
| 1530 | } | |||
| 1531 | break; | |||
| 1532 | } | |||
| 1533 | ||||
| 1534 | if ( c == '\n' ) { | |||
| 1535 | linenum++; | |||
| 1536 | } | |||
| 1537 | ||||
| 1538 | if ( i >= MAX_ITEM_LEN8192 - 1 ) { | |||
| 1539 | loal_error = load_loal_error(fp,loal,curr,linenum,"Maximum item length exceeded"); | |||
| 1540 | goto error; | |||
| 1541 | } | |||
| 1542 | ||||
| 1543 | switch(state) { | |||
| 1544 | case MY_IGNORE: | |||
| 1545 | switch (c) { | |||
| 1546 | case '\n': | |||
| 1547 | state = START; | |||
| 1548 | i = 0; | |||
| 1549 | continue; | |||
| 1550 | default: | |||
| 1551 | continue; | |||
| 1552 | } | |||
| 1553 | case START: | |||
| 1554 | switch (c) { | |||
| 1555 | case ' ': case '\t': | |||
| 1556 | /* ignore whitespace at line start */ | |||
| 1557 | continue; | |||
| 1558 | case '\n': | |||
| 1559 | /* ignore empty lines */ | |||
| 1560 | i = 0; | |||
| 1561 | continue; | |||
| 1562 | case AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G' : case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T' :case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g' : case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z' : case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':case '7': case '8': case '9': case '.': | |||
| 1563 | state = IN_NAME; | |||
| 1564 | i = 0; | |||
| 1565 | name[i++] = c; | |||
| 1566 | name[i] = '\0'; | |||
| 1567 | snprintf(linenum_buf,MAX_ITEM_LEN8192,"%s:%u",filename,linenum); | |||
| 1568 | curr = new_avpl(linenum_buf); | |||
| 1569 | continue; | |||
| 1570 | case '#': | |||
| 1571 | state = MY_IGNORE; | |||
| 1572 | continue; | |||
| 1573 | default: | |||
| 1574 | loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c); | |||
| 1575 | goto error; | |||
| 1576 | } | |||
| 1577 | case BEFORE_NAME: | |||
| 1578 | i = 0; | |||
| 1579 | name[0] = '\0'; | |||
| 1580 | switch (c) { | |||
| 1581 | case '\\': | |||
| 1582 | c = (char) fgetc(fp); | |||
| 1583 | if (c != '\n') ungetc(c,fp); | |||
| ||||
| 1584 | continue; | |||
| 1585 | case ' ': | |||
| 1586 | case '\t': | |||
| 1587 | continue; | |||
| 1588 | case AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G' : case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T' :case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g' : case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z' : case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':case '7': case '8': case '9': case '.': | |||
| 1589 | state = IN_NAME; | |||
| 1590 | ||||
| 1591 | name[i++] = c; | |||
| 1592 | name[i] = '\0'; | |||
| 1593 | continue; | |||
| 1594 | case '\n': | |||
| 1595 | loal_append(loal,curr); | |||
| 1596 | curr = NULL((void*)0); | |||
| 1597 | state = START; | |||
| 1598 | continue; | |||
| 1599 | default: | |||
| 1600 | loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c); | |||
| 1601 | goto error; | |||
| 1602 | } | |||
| 1603 | case IN_NAME: | |||
| 1604 | switch (c) { | |||
| 1605 | case ';': | |||
| 1606 | state = BEFORE_NAME; | |||
| 1607 | ||||
| 1608 | op = '?'; | |||
| 1609 | name[i] = '\0'; | |||
| 1610 | value[0] = '\0'; | |||
| 1611 | i = 0; | |||
| 1612 | ||||
| 1613 | avp = new_avp(name,value,op); | |||
| 1614 | ||||
| 1615 | if (! insert_avp(curr,avp) ) { | |||
| 1616 | delete_avp(avp); | |||
| 1617 | } | |||
| 1618 | ||||
| 1619 | continue; | |||
| 1620 | case AVP_OP_CHAR'=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!': | |||
| 1621 | name[i] = '\0'; | |||
| 1622 | i = 0; | |||
| 1623 | op = c; | |||
| 1624 | state = IN_VALUE; | |||
| 1625 | continue; | |||
| 1626 | case AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G' : case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T' :case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g' : case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z' : case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':case '7': case '8': case '9': case '.': | |||
| 1627 | name[i++] = c; | |||
| 1628 | continue; | |||
| 1629 | case '\n': | |||
| 1630 | loal_error = load_loal_error(fp,loal,curr,linenum,"operator expected found new line"); | |||
| 1631 | goto error; | |||
| 1632 | default: | |||
| 1633 | loal_error = load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c); | |||
| 1634 | goto error; | |||
| 1635 | } | |||
| 1636 | case IN_VALUE: | |||
| 1637 | switch (c) { | |||
| 1638 | case '\\': | |||
| 1639 | value[i++] = (char) fgetc(fp); | |||
| 1640 | continue; | |||
| 1641 | case ';': | |||
| 1642 | state = BEFORE_NAME; | |||
| 1643 | ||||
| 1644 | value[i] = '\0'; | |||
| 1645 | i = 0; | |||
| 1646 | ||||
| 1647 | avp = new_avp(name,value,op); | |||
| 1648 | ||||
| 1649 | if (! insert_avp(curr,avp) ) { | |||
| 1650 | delete_avp(avp); | |||
| 1651 | } | |||
| 1652 | continue; | |||
| 1653 | case '\n': | |||
| 1654 | loal_error = load_loal_error(fp,loal,curr,linenum,"';' expected found new line"); | |||
| 1655 | goto error; | |||
| 1656 | default: | |||
| 1657 | value[i++] = c; | |||
| 1658 | continue; | |||
| 1659 | } | |||
| 1660 | } | |||
| 1661 | } | |||
| 1662 | fclose (fp); | |||
| 1663 | ||||
| 1664 | if (curr) { | |||
| 1665 | // Premature EOF? It could just be a file that doesn't | |||
| 1666 | // end in a newline, but hard to say without checking | |||
| 1667 | // state. Error, discard, add to existing loal? | |||
| 1668 | delete_avpl(curr,true1); | |||
| 1669 | } | |||
| 1670 | ||||
| 1671 | g_free(linenum_buf); | |||
| 1672 | g_free(name); | |||
| 1673 | g_free(value); | |||
| 1674 | ||||
| 1675 | return loal; | |||
| 1676 | ||||
| 1677 | } else { | |||
| 1678 | report_open_failure(filename,errno(*__errno_location ()),false0); | |||
| 1679 | loal_error = load_loal_error(NULL((void*)0),loal,NULL((void*)0),0,"Cannot Open file '%s'",filename); | |||
| 1680 | } | |||
| 1681 | ||||
| 1682 | error: | |||
| 1683 | g_free(linenum_buf); | |||
| 1684 | g_free(name); | |||
| 1685 | g_free(value); | |||
| 1686 | ||||
| 1687 | return loal_error; | |||
| 1688 | } |