1 /*
2  * Copyright 2015-2021 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #ifndef PCMK__STRINGS_INTERNAL__H
11 #define PCMK__STRINGS_INTERNAL__H
12 
13 #include <stdbool.h>            // bool
14 
15 #include <glib.h>               // guint, GList, GHashTable
16 
17 /* internal constants for generic string functions (from strings.c) */
18 
19 #define PCMK__PARSE_INT_DEFAULT -1
20 #define PCMK__PARSE_DBL_DEFAULT -1.0
21 
22 /* internal generic string functions (from strings.c) */
23 
24 enum pcmk__str_flags {
25     pcmk__str_none          = 0,
26     pcmk__str_casei         = 1 << 0,
27     pcmk__str_null_matches  = 1 << 1,
28     pcmk__str_regex         = 1 << 2
29 };
30 
31 int pcmk__scan_double(const char *text, double *result,
32                       const char *default_text, char **end_text);
33 int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val,
34                           guint *result);
35 bool pcmk__starts_with(const char *str, const char *prefix);
36 bool pcmk__ends_with(const char *s, const char *match);
37 bool pcmk__ends_with_ext(const char *s, const char *match);
38 char *pcmk__trim(char *str);
39 void pcmk__add_separated_word(char **list, size_t *len, const char *word,
40                               const char *separator);
41 int pcmk__compress(const char *data, unsigned int length, unsigned int max,
42                    char **result, unsigned int *result_len);
43 
44 int pcmk__scan_ll(const char *text, long long *result, long long default_value);
45 int pcmk__scan_min_int(const char *text, int *result, int minimum);
46 int pcmk__scan_port(const char *text, int *port);
47 int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end);
48 
49 GHashTable *pcmk__strkey_table(GDestroyNotify key_destroy_func,
50                                GDestroyNotify value_destroy_func);
51 GHashTable *pcmk__strikey_table(GDestroyNotify key_destroy_func,
52                                 GDestroyNotify value_destroy_func);
53 GHashTable *pcmk__str_table_dup(GHashTable *old_table);
54 
55 /*!
56  * \internal
57  * \brief Create a hash table with integer keys
58  *
59  * \param[in] value_destroy_func  Function to free a value
60  *
61  * \return Newly allocated hash table
62  * \note It is the caller's responsibility to free the result, using
63  *       g_hash_table_destroy().
64  */
65 static inline GHashTable *
pcmk__intkey_table(GDestroyNotify value_destroy_func)66 pcmk__intkey_table(GDestroyNotify value_destroy_func)
67 {
68     return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
69                                  value_destroy_func);
70 }
71 
72 /*!
73  * \internal
74  * \brief Insert a value into a hash table with integer keys
75  *
76  * \param[in,out] hash_table  Table to insert into
77  * \param[in]     key         Integer key to insert
78  * \param[in]     value       Value to insert
79  *
80  * \return Whether the key/value was already in the table
81  * \note This has the same semantics as g_hash_table_insert(). If the key
82  *       already exists in the table, the old value is freed and replaced.
83  */
84 static inline gboolean
pcmk__intkey_table_insert(GHashTable * hash_table,int key,gpointer value)85 pcmk__intkey_table_insert(GHashTable *hash_table, int key, gpointer value)
86 {
87     return g_hash_table_insert(hash_table, GINT_TO_POINTER(key), value);
88 }
89 
90 /*!
91  * \internal
92  * \brief Look up a value in a hash table with integer keys
93  *
94  * \param[in] hash_table  Table to check
95  * \param[in] key         Integer key to look for
96  *
97  * \return Value in table for \key (or NULL if not found)
98  */
99 static inline gpointer
pcmk__intkey_table_lookup(GHashTable * hash_table,int key)100 pcmk__intkey_table_lookup(GHashTable *hash_table, int key)
101 {
102     return g_hash_table_lookup(hash_table, GINT_TO_POINTER(key));
103 }
104 
105 /*!
106  * \internal
107  * \brief Remove a key/value from a hash table with integer keys
108  *
109  * \param[in] hash_table  Table to modify
110  * \param[in] key         Integer key of entry to remove
111  *
112  * \return Whether \p key was found and removed from \p hash_table
113  */
114 static inline gboolean
pcmk__intkey_table_remove(GHashTable * hash_table,int key)115 pcmk__intkey_table_remove(GHashTable *hash_table, int key)
116 {
117     return g_hash_table_remove(hash_table, GINT_TO_POINTER(key));
118 }
119 
120 gboolean pcmk__str_in_list(GList *lst, const gchar *s, uint32_t flags);
121 
122 bool pcmk__strcase_any_of(const char *s, ...) G_GNUC_NULL_TERMINATED;
123 bool pcmk__str_any_of(const char *s, ...) G_GNUC_NULL_TERMINATED;
124 bool pcmk__char_in_any_str(int ch, ...) G_GNUC_NULL_TERMINATED;
125 
126 int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags);
127 int pcmk__numeric_strcasecmp(const char *s1, const char *s2);
128 
129 static inline bool
pcmk__str_eq(const char * s1,const char * s2,uint32_t flags)130 pcmk__str_eq(const char *s1, const char *s2, uint32_t flags)
131 {
132     return pcmk__strcmp(s1, s2, flags) == 0;
133 }
134 
135 // Like pcmk__add_separated_word() but using a space as separator
136 static inline void
pcmk__add_word(char ** list,size_t * len,const char * word)137 pcmk__add_word(char **list, size_t *len, const char *word)
138 {
139     return pcmk__add_separated_word(list, len, word, " ");
140 }
141 
142 /* Correctly displaying singular or plural is complicated; consider "1 node has"
143  * vs. "2 nodes have". A flexible solution is to pluralize entire strings, e.g.
144  *
145  * if (a == 1) {
146  *     crm_info("singular message"):
147  * } else {
148  *     crm_info("plural message");
149  * }
150  *
151  * though even that's not sufficient for all languages besides English (if we
152  * ever desire to do translations of output and log messages). But the following
153  * convenience macros are "good enough" and more concise for many cases.
154  */
155 
156 /* Example:
157  * crm_info("Found %d %s", nentries,
158  *          pcmk__plural_alt(nentries, "entry", "entries"));
159  */
160 #define pcmk__plural_alt(i, s1, s2) (((i) == 1)? (s1) : (s2))
161 
162 // Example: crm_info("Found %d node%s", nnodes, pcmk__plural_s(nnodes));
163 #define pcmk__plural_s(i) pcmk__plural_alt(i, "", "s")
164 
165 static inline int
pcmk__str_empty(const char * s)166 pcmk__str_empty(const char *s)
167 {
168     return (s == NULL) || (s[0] == '\0');
169 }
170 
171 static inline char *
pcmk__itoa(int an_int)172 pcmk__itoa(int an_int)
173 {
174     return crm_strdup_printf("%d", an_int);
175 }
176 
177 static inline char *
pcmk__ftoa(double a_float)178 pcmk__ftoa(double a_float)
179 {
180     return crm_strdup_printf("%f", a_float);
181 }
182 
183 static inline char *
pcmk__ttoa(time_t epoch_time)184 pcmk__ttoa(time_t epoch_time)
185 {
186     return crm_strdup_printf("%lld", (long long) epoch_time);
187 }
188 
189 // note this returns const not allocated
190 static inline const char *
pcmk__btoa(bool condition)191 pcmk__btoa(bool condition)
192 {
193     return condition? "true" : "false";
194 }
195 
196 #endif /* PCMK__STRINGS_INTERNAL__H */
197