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