1 /* 2 ** Copyright (C) 2005-2020 by Carnegie Mellon University. 3 ** 4 ** @OPENSOURCE_LICENSE_START@ 5 ** See license information in ../../LICENSE.txt 6 ** @OPENSOURCE_LICENSE_END@ 7 */ 8 9 /* 10 ** skstringmap.h 11 ** 12 ** An ordered list of name->id mappings to support named fields, 13 ** where name is a C-string; id is a uint32_t. 14 ** 15 */ 16 #ifndef _SKSTRINGMAP_H 17 #define _SKSTRINGMAP_H 18 #ifdef __cplusplus 19 extern "C" { 20 #endif 21 22 #include <silk/silk.h> 23 24 RCSIDENTVAR(rcsID_STRINGMAP_H, "$SiLK: skstringmap.h ef14e54179be 2020-04-14 21:57:45Z mthomas $"); 25 26 #include <silk/silk_types.h> 27 28 /** 29 * @file 30 * 31 * An ordered list of name->id mappings to support named fields, 32 * where name is a C-string; id is a uint32_t. 33 * 34 * This file is part of libsilk. 35 * 36 * 37 * Name matching in a string map is case-insensitive. 38 * 39 * The basic usage is to create a sk_stringmap_t and to add 40 * sk_stringmap_entry_t items to it, each of which is a name/value 41 * pair. 42 * 43 * Then, once processing begins, call skStringMapParse() with the 44 * user's string, and it will return either a valid result set (an 45 * sk_stringmap_iter_t), or a parse error. 46 * 47 * Sample Usage (does not check error cases) 48 * 49 * sk_stringmap_t *name_id_map; 50 * sk_stringmap_entry_t mappings[] = { 51 * { "foo", 1, "Description of foo", NULL }, 52 * { "bar", 2, "Description of bar", NULL }, 53 * }; 54 * char *user_input = "foo,baz"; 55 * sk_stringmap_iter_t *iter; 56 * sk_stringmap_status_t rv; 57 * sk_stringmap_id_t found_id; 58 * sk_stringmap_dupes_t dupes = SKSTRINGMAP_DUPES_KEEP; 59 * 60 * // Create map and add entries 61 * skStringMapCreate( &name_id_map ); 62 * skStringMapAddEntries( name_id_map, 2, mappings ); 63 * 64 * // Map user's input and process each entry 65 * rv = skStringMapParse( name_id_map, user_input, dupes, &iter, NULL ); 66 * if( rv == SKSTRINGMAP_OK ) { 67 * sk_stringmap_entry_t *entry; 68 * while (skStringMapIterNext(iter, &entry, NULL) == SK_ITERATOR_OK) { 69 * found_id = entry->id; 70 * // DO STUFF WITH THE IDS MATCHED 71 * } 72 * } 73 * 74 * // Clean up 75 * skStringMapIterDestroy( iter ); 76 * skStringMapDestroy( name_id_map ); 77 */ 78 79 80 /* typedef sk_dllist_t sk_stringmap_t; // silk_types.h */ 81 82 /** 83 * Type of the integer key for an entry in the string map. 84 */ 85 typedef uint32_t sk_stringmap_id_t; 86 87 /** 88 * Description of an entry in the string map. 89 */ 90 typedef struct sk_stringmap_entry_st { 91 /** string name key */ 92 const char *name; 93 /** unsigned integer id value */ 94 sk_stringmap_id_t id; 95 /** optional description of this entry */ 96 const char *description; 97 /** data pointer maintained by the caller */ 98 const void *userdata; 99 } sk_stringmap_entry_t; 100 101 102 /** 103 * Can be put at end of an array of sk_stringmap_entry_t to 104 * indicate no-more-entries. 105 */ 106 #define SK_STRINGMAP_SENTINEL {NULL, 0, NULL, NULL} 107 108 109 /** 110 * Iterator over the results of parsing 111 */ 112 typedef struct sk_stringmap_iter_st sk_stringmap_iter_t; 113 114 115 /** 116 * Function result status 117 */ 118 typedef enum { 119 /** Command was successful */ 120 SKSTRINGMAP_OK = 0, 121 122 /** Indicates bad input, e.g. NULL pointer */ 123 SKSTRINGMAP_ERR_INPUT = -127, 124 125 /** A memory allocation call failed */ 126 SKSTRINGMAP_ERR_MEM, 127 128 /** Some unexpected error occured in the linked list */ 129 SKSTRINGMAP_ERR_LIST, 130 131 /* The following values can be returned while adding a new 132 * key/value pair to the map. */ 133 134 /** The new key was found to be a duplicate of a key already in the 135 * map. */ 136 SKSTRINGMAP_ERR_DUPLICATE_ENTRY, 137 138 /** The new key was found to be the empty string. */ 139 SKSTRINGMAP_ERR_ZERO_LENGTH_ENTRY, 140 141 /** The key was found to start with a number but to contain 142 * non-numeric characters. */ 143 SKSTRINGMAP_ERR_NUMERIC_START_ENTRY, 144 145 /** The key was found to start with a non-alphanumeric 146 * character. */ 147 SKSTRINGMAP_ERR_ALPHANUM_START_ENTRY, 148 149 /** The parser encountered an unexpected error unrelated to the 150 * user's input */ 151 SKSTRINGMAP_ERR_PARSER, 152 153 /* The following values can be returned while parsing user input 154 * and finding entries in the map. */ 155 156 /** The user's input is not an exact match nor a prefix match for 157 * any key. */ 158 SKSTRINGMAP_PARSE_NO_MATCH, 159 160 /** The user's input matches matches no key exactly and is a prefix 161 * match for multiple keys. */ 162 SKSTRINGMAP_PARSE_AMBIGUOUS, 163 164 /** The user's input is not parsable or contains an invalid range 165 * e.g., 3-2 */ 166 SKSTRINGMAP_PARSE_UNPARSABLE, 167 168 /** Returned when parsing is successful but the 'handle_dupes' 169 * parameter was set to SKSTRINGMAP_DUPES_REMOVE_WARN. This 170 * indicates an warning message is in the error buffer. */ 171 SKSTRINGMAP_OK_DUPLICATE = 1 172 173 } sk_stringmap_status_t; 174 175 176 /** 177 * Parameter to pass to the parsing functions (skStringMapParse(), 178 * etc) that specify how they are to handle duplicate entries. 179 */ 180 typedef enum { 181 SKSTRINGMAP_DUPES_KEEP = 0, 182 SKSTRINGMAP_DUPES_REMOVE_SILENT, 183 SKSTRINGMAP_DUPES_REMOVE_WARN, 184 SKSTRINGMAP_DUPES_ERROR 185 } sk_stringmap_dupes_t; 186 187 188 189 /** 190 * Create a new string-map and put the result into 'out_str_map'. 191 * Return SKSTRINGMAP_OK on success, or SKSTRINGMAP_ERR_MEM if 192 * allocation fails. 193 * 194 */ 195 sk_stringmap_status_t 196 skStringMapCreate( 197 sk_stringmap_t **out_str_map); 198 199 200 /** 201 * Destroy the string-map 'str_map'. Do nothing if 'str_map' is 202 * NULL. Return SKSTRINGMAP_OK; 203 */ 204 sk_stringmap_status_t 205 skStringMapDestroy( 206 sk_stringmap_t *str_map); 207 208 209 /** 210 * Return 1 if the string-map 'str_map' is empty; 0 if it contains 211 * entries. 212 */ 213 #define skStringMapIsEmpty(str_map) skDLListIsEmpty(str_map) 214 215 216 /** 217 * Add multiple entries from the 'entryv' array to the StringMap 218 * 'str_map'. 219 * 220 * When 'entryc' is positive or zero, it is taken as the count of 221 * entries in 'entryv'. When 'entryc' is negative, 'entryv' is 222 * considered to be a NULL-terminated array of entries; i.e., all 223 * entries in 'entryv' are added until an entry that matches 224 * SK_STRINGMAP_SENTINEL is reached. 225 * 226 * Return SKSTRINGMAP_OK if all entries are successfully added. If 227 * an error occurs, some entries may have been added to the 228 * StringMap. Return SKSTRINGMAP_ERR_INPUT if 'str_map' or 229 * 'entryv' is NULL or if 'entryc' is positive and a "name" member 230 * within the first 'entryc' entries of 'entryv' is NULL. Return 231 * SKSTRINGMAP_ERR_MEM if allocation fails. If a "name" member is 232 * not a valid name, return SKSTRINGMAP_ZERO_LENGTH_ENTRY, 233 * SKSTRINGMAP_ERR_NUMERIC_START_ENTRY, or 234 * SKSTRINGMAP_ERR_ALPHANUM_START_ENTRY. Return 235 * SKSTRINGMAP_DUPLICATE_ENTRY if an entry having the same name 236 * already exists in the map. 237 */ 238 sk_stringmap_status_t 239 skStringMapAddEntries( 240 sk_stringmap_t *str_map, 241 int entryc, 242 const sk_stringmap_entry_t *entryv); 243 244 245 /** 246 * Remove the single entry from the StringMap 'str_map' whose name 247 * is 'name'. Return SKSTRINGMAP_ERR_INPUT if 'str_map' or 'name' 248 * is NULL, else return SKSTRINGMAP_OK. Return SKSTRINGMAP_OK even 249 * if an entry with the name 'name' does not exist in the map. 250 */ 251 sk_stringmap_status_t 252 skStringMapRemoveByName( 253 sk_stringmap_t *str_map, 254 const char *name); 255 256 257 /** 258 * Remove all entries from the StringMap 'str_map' whose ID is 259 * 'id'. Return SKSTRINGMAP_ERR_INPUT if 'str_map' is NULL, else 260 * return SKSTRINGMAP_OK. Return SKSTRINGMAP_OK even 261 * if an entry with the ID 'id' does not exist in the map. 262 */ 263 sk_stringmap_status_t 264 skStringMapRemoveByID( 265 sk_stringmap_t *str_map, 266 sk_stringmap_id_t id); 267 268 269 /** 270 * Remove multiple entries from the StringMap 'str_map' by calling 271 * skStringMapRemoveByName() for all entries in 'entryv'. 272 * 273 * When 'entryc' is positive or zero, it is taken as the count of 274 * entries in 'entryv'. When 'entryc' is negative, 'entryv' is 275 * considered to be a NULL-terminated array of entries; i.e., all 276 * entries in 'entryv' are added until an entry that matches 277 * SK_STRINGMAP_SENTINEL is reached. 278 * 279 * Return SKSTRINGMAP_ERR_INPUT if 'str_map' or 'entryv' is NULL or 280 * if 'entryc' is positive and a "name" member within the first 281 * 'entryc' entries of 'entryv' is NULL. Otherwise, return 282 * SKSTRINGMAP_OK, even if the StringMap was not modified. 283 */ 284 sk_stringmap_status_t 285 skStringMapRemoveEntries( 286 sk_stringmap_t *str_map, 287 int entryc, 288 const sk_stringmap_entry_t *entryv); 289 290 291 /** 292 * Take the user entered string, 'user_string' containing tokens 293 * separated by commas(,) and hyphens(-), parse it to get a list of 294 * keys, search the StringMap 'str_map' for those keys, allocate a 295 * sk_stringmap_iter_t to iterate over the entries, and store the 296 * location of the iterator at the memory location point to by 297 * 'iter'. 298 * 299 * If the 'user_string' contains duplicate entries, the 300 * corresponding sk_stringmap_entry_t will be added to the iterator 301 * multiple times. 302 * 303 * When non-NULL, 'bad_token' should contain a pointer to a 304 * C-string to hold the token that was being parsed when a parse 305 * error occurred. It is the caller's responsibility to free() 306 * this string. 307 * 308 * When the caller is finished with the iterator, she must call 309 * skStringMapIterDestroy() to free the resources used by the 310 * iterator. 311 */ 312 sk_stringmap_status_t 313 skStringMapMatch( 314 const sk_stringmap_t *str_map, 315 const char *user_string, 316 sk_stringmap_iter_t **iter, 317 char **bad_token); 318 319 320 /** 321 * Take the user entered string, 'user_string' containing tokens 322 * separated by commas(,) and hyphens(-), parse it to get a list of 323 * keys, search the StringMap 'str_map' for those keys, allocate a 324 * sk_stringmap_iter_t to iterate over the entries, and store the 325 * location of the iterator at the memory location point to by 326 * 'iter'. 327 * 328 * The value 'handle_dupes' specifies how to handle duplicate 329 * entries in the 'user_string'. The values are: 330 * 331 * SKSTRINGMAP_DUPES_KEEP - duplicate the sk_stringmap_entry_t 332 * in the 'out_vec' 333 * 334 * SKSTRINGMAP_DUPES_REMOVE_SILENT - completely ignore the 335 * duplicate entry 336 * 337 * SKSTRINGMAP_DUPES_REMOVE_WARN - do not store the duplicate, 338 * but inform the user of the issue 339 * 340 * SKSTRINGMAP_DUPES_ERROR - do not store the duplicate and 341 * return an error code 342 * 343 * If an error occurs and 'errmsg' is non-NULL, it will be set to a 344 * static buffer containing an error message that the caller can 345 * print. The static buffer remains valid until the next call to 346 * this function. 347 * 348 * When the caller is finished with the iterator, she must call 349 * skStringMapIterDestroy() to free the resources used by the 350 * iterator. 351 */ 352 sk_stringmap_status_t 353 skStringMapParse( 354 const sk_stringmap_t *str_map, 355 const char *user_input, 356 sk_stringmap_dupes_t handle_dupes, 357 sk_stringmap_iter_t **iter, 358 char **errmsg); 359 360 361 /** 362 * Parse the string 'user_string' in a manner similar to 363 * skStringMapParse(), but in addition store any attributes for the 364 * entries. 365 * 366 * Having different attributes for the same token does not make the 367 * tokens different as far as the checking for duplicates is 368 * concerned. For example, the user_string "foo:1,foo:2" will 369 * warn/error on the duplicate token "foo" when 'handle_dupes' is 370 * not set to SKSTRINGMAP_DUPES_KEEP. 371 */ 372 sk_stringmap_status_t 373 skStringMapParseWithAttributes( 374 const sk_stringmap_t *str_map, 375 const char *user_string, 376 sk_stringmap_dupes_t handle_dupes, 377 sk_stringmap_iter_t **iter, 378 char **errmsg); 379 380 381 /** 382 * Take the user entered string 'user_string' containing a SINGLE 383 * key and search the StringMap 'str_map' to find the 384 * sk_stringmap_entry_t that has that string as a key, setting 385 * 'out_entry' to point to that key and returning SKSTRINGMAP_OK. 386 * The caller should not modify nor free the returned entry. 387 * 388 * Return SKSTRINGMAP_PARSE_AMBIGUOUS if 'user_string' matches 389 * multiple entries, SKSTRINGMAP_PARSE_NO_MATCH if it matches no 390 * entries. 391 */ 392 sk_stringmap_status_t 393 skStringMapGetByName( 394 const sk_stringmap_t *str_map, 395 const char *user_string, 396 sk_stringmap_entry_t **out_entry); 397 398 399 /** 400 * Find the entry matching 'user_string' in a manner similar to 401 * skStringMapGetByName(), but in addition copy any attributes into 402 * the 'attributes' buffer, a character array of size 403 * 'attributes_len'. If there are no attributes for the entry, set 404 * 'attributes' to the empty string. 405 * 406 * In addition to the return values that skStringMapGetByName() 407 * returns, return SKSTRINGMAP_PARSE_UNPARSABLE if syntax 408 * designating the attribute is incorrect or if the attribute will 409 * not fit into the 'attributes' buffer. 410 */ 411 sk_stringmap_status_t 412 skStringMapGetByNameWithAttributes( 413 const sk_stringmap_t *str_map, 414 const char *user_string, 415 sk_stringmap_entry_t **out_entry, 416 char *attributes, 417 size_t attributes_len); 418 419 420 /** 421 * Given a numeric identifier 'id', allocate a sk_stringmap_iter_t 422 * to iterate over the entries with that identifier, and store the 423 * the iterator in the referent of 'iter'. 424 * 425 * When the caller is finished with the iterator, she must call 426 * skStringMapIterDestroy() to free the resources used by the 427 * iterator. 428 * 429 * Return SKSTRINGMAP_OK on success. Return SKSTRINGMAP_ERR_INPUT 430 * if a parameter is NULL, and return SKSTRINGMAP_ERR_MEM if an 431 * error occurs when creating or appending to the iterator. 432 * 433 * If only one entry is needed, use skStringMapGetFirstName(). 434 */ 435 sk_stringmap_status_t 436 skStringMapGetByID( 437 const sk_stringmap_t *str_map, 438 sk_stringmap_id_t id, 439 sk_stringmap_iter_t **iter); 440 441 442 /** 443 * Find the first entry in the StringMap 'str_map' whose ID is 'id' 444 * and return the name associated with the entry. Return NULL if 445 * 'id' is not in the StringMap. The caller should not modify nor 446 * free the returned value. 447 * 448 * To get all entries that have that ID, use skStringMapGetByID(). 449 */ 450 const char * 451 skStringMapGetFirstName( 452 const sk_stringmap_t *str_map, 453 sk_stringmap_id_t id); 454 455 456 /** 457 * Return the number of matches (entries) contained in the 458 * stringmap iterator 'iter'. 459 */ 460 size_t 461 skStringMapIterCountMatches( 462 sk_stringmap_iter_t *iter); 463 464 465 /** 466 * Destroy the iterator pointed at by 'iter'. Does nothing if 467 * 'iter' is NULL. 468 */ 469 void 470 skStringMapIterDestroy( 471 sk_stringmap_iter_t *iter); 472 473 474 /** 475 * If more entries are available in the stringmap iterator 'iter', 476 * set the referent of 'entry' to point to the next entry, set the 477 * referent of 'attr' to point at the next attribute string for 478 * that entry, and return SK_ITERATOR_OK; otherwise, return 479 * SK_ITERATOR_NO_MORE_ENTRIES. If there is no attribute for the 480 * entry, the memory at 'attr' is set to point to an empty string. 481 * 482 * If 'attr' is NULL, the attributes string is not returned. 483 * 484 * The caller should not modify nor free the returned entry or 485 * attributes. 486 */ 487 int 488 skStringMapIterNext( 489 sk_stringmap_iter_t *iter, 490 sk_stringmap_entry_t **entry, 491 const char **attr); 492 493 494 /** 495 * Reset the stringmap iterator 'iter' so it may loop over the 496 * matched entries again. 497 */ 498 void 499 skStringMapIterReset( 500 sk_stringmap_iter_t *iter); 501 502 503 504 /** 505 * Print the map keys and values to the given file stream. The map 506 * is printed in a human readable format of the form 507 * 508 * { "key1" : value1, "key2" : value2, ... } 509 * 510 * Arguments 511 * 512 * FILE *outstream - the output stream to which to print 513 * 514 * sk_stringmap_t *str_map - pointer to the StringMap to print 515 */ 516 sk_stringmap_status_t 517 skStringMapPrintMap( 518 const sk_stringmap_t *str_map, 519 FILE *outstream); 520 521 522 /** 523 * Print to the file handle 'fh' the names of the fields in the 524 * string map 'str_map' as a block of text indented by 'indent_len' 525 * spaces. 526 * 527 * The function assumes that names that the map to the same 528 * identifier occur consecutively in the map. 529 */ 530 void 531 skStringMapPrintUsage( 532 const sk_stringmap_t *str_map, 533 FILE *fh, 534 const int indent_len); 535 536 537 /** 538 * Print to the file handle 'fh' the names and descriptions of the 539 * fields in the string map 'str_map'. 540 * 541 * The function assumes that names that the map to the same 542 * identifier occur consecutively in the map. The first name is 543 * taken as the primary name; additional names are taken as 544 * aliases. 545 * 546 * Primary names occur in a single column. The descriptions and 547 * aliases occur in a second column, with line wrapping as needed. 548 * All text is indented from the left margin by a tab character. 549 * Code assumes the display width is 80 characters with a 8-space 550 * tabs. 551 */ 552 void 553 skStringMapPrintDetailedUsage( 554 const sk_stringmap_t *str_map, 555 FILE *fh); 556 557 558 /** 559 * Given the 'error_code' return a textual representation of it. 560 */ 561 const char * 562 skStringMapStrerror( 563 int error_code); 564 565 566 #ifdef __cplusplus 567 } 568 #endif 569 #endif /* _SKSTRINGMAP_H */ 570 571 /* 572 ** Local Variables: 573 ** mode:c 574 ** indent-tabs-mode:nil 575 ** c-basic-offset:4 576 ** End: 577 */ 578