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