/src/pacemaker/lib/common/strings.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2004-2025 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 | | #include <crm_internal.h> |
11 | | |
12 | | #include <regex.h> |
13 | | #include <stdarg.h> // va_list, etc. |
14 | | #include <stdio.h> |
15 | | #include <string.h> |
16 | | #include <stdlib.h> |
17 | | #include <ctype.h> |
18 | | #include <float.h> // DBL_MIN |
19 | | #include <limits.h> |
20 | | #include <bzlib.h> |
21 | | #include <sys/types.h> |
22 | | |
23 | | /*! |
24 | | * \internal |
25 | | * \brief Scan a long long integer from a string |
26 | | * |
27 | | * \param[in] text String to scan |
28 | | * \param[out] result If not NULL, where to store scanned value |
29 | | * \param[in] default_value Value to use if text is NULL or invalid |
30 | | * \param[out] end_text If not NULL, where to store pointer to first |
31 | | * non-integer character |
32 | | * |
33 | | * \return Standard Pacemaker return code (\c pcmk_rc_ok on success, |
34 | | * \c pcmk_rc_bad_input on failed string conversion due to invalid |
35 | | * input, or \c ERANGE if outside long long range) |
36 | | * \note Sets \c errno on error |
37 | | */ |
38 | | static int |
39 | | scan_ll(const char *text, long long *result, long long default_value, |
40 | | char **end_text) |
41 | 1.67k | { |
42 | 1.67k | long long local_result = default_value; |
43 | 1.67k | char *local_end_text = NULL; |
44 | 1.67k | int rc = pcmk_rc_ok; |
45 | | |
46 | 1.67k | errno = 0; |
47 | 1.67k | if (text != NULL) { |
48 | 1.67k | local_result = strtoll(text, &local_end_text, 10); |
49 | 1.67k | if (errno == ERANGE) { |
50 | 4 | rc = errno; |
51 | 4 | crm_debug("Integer parsed from '%s' was clipped to %lld", |
52 | 4 | text, local_result); |
53 | | |
54 | 1.67k | } else if (local_end_text == text) { |
55 | 959 | rc = pcmk_rc_bad_input; |
56 | 959 | local_result = default_value; |
57 | 959 | crm_debug("Could not parse integer from '%s' (using %lld instead): " |
58 | 959 | "No digits found", text, default_value); |
59 | | |
60 | 959 | } else if (errno != 0) { |
61 | 0 | rc = errno; |
62 | 0 | local_result = default_value; |
63 | 0 | crm_debug("Could not parse integer from '%s' (using %lld instead): " |
64 | 0 | "%s", text, default_value, pcmk_rc_str(rc)); |
65 | 0 | } |
66 | | |
67 | 1.67k | if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) { |
68 | 0 | crm_debug("Characters left over after parsing '%s': '%s'", |
69 | 0 | text, local_end_text); |
70 | 0 | } |
71 | 1.67k | errno = rc; |
72 | 1.67k | } |
73 | 1.67k | if (end_text != NULL) { |
74 | 1.67k | *end_text = local_end_text; |
75 | 1.67k | } |
76 | 1.67k | if (result != NULL) { |
77 | 1.67k | *result = local_result; |
78 | 1.67k | } |
79 | 1.67k | return rc; |
80 | 1.67k | } |
81 | | |
82 | | /*! |
83 | | * \internal |
84 | | * \brief Scan a long long integer value from a string |
85 | | * |
86 | | * \param[in] text The string to scan (may be NULL) |
87 | | * \param[out] result Where to store result (or NULL to ignore) |
88 | | * \param[in] default_value Value to use if text is NULL or invalid |
89 | | * |
90 | | * \return Standard Pacemaker return code |
91 | | */ |
92 | | int |
93 | | pcmk__scan_ll(const char *text, long long *result, long long default_value) |
94 | 0 | { |
95 | 0 | long long local_result = default_value; |
96 | 0 | int rc = scan_ll(text, &local_result, default_value, NULL); |
97 | |
|
98 | 0 | if (result != NULL) { |
99 | 0 | *result = local_result; |
100 | 0 | } |
101 | 0 | return rc; |
102 | 0 | } |
103 | | |
104 | | /*! |
105 | | * \internal |
106 | | * \brief Scan an integer value from a string, constrained to a minimum |
107 | | * |
108 | | * \param[in] text The string to scan (may be NULL) |
109 | | * \param[out] result Where to store result (or NULL to ignore) |
110 | | * \param[in] minimum Value to use as default and minimum |
111 | | * |
112 | | * \return Standard Pacemaker return code |
113 | | * \note If the value is larger than the maximum integer, EOVERFLOW will be |
114 | | * returned and \p result will be set to the maximum integer. |
115 | | */ |
116 | | int |
117 | | pcmk__scan_min_int(const char *text, int *result, int minimum) |
118 | 0 | { |
119 | 0 | int rc; |
120 | 0 | long long result_ll; |
121 | |
|
122 | 0 | rc = pcmk__scan_ll(text, &result_ll, (long long) minimum); |
123 | |
|
124 | 0 | if (result_ll < (long long) minimum) { |
125 | 0 | crm_warn("Clipped '%s' to minimum acceptable value %d", text, minimum); |
126 | 0 | result_ll = (long long) minimum; |
127 | |
|
128 | 0 | } else if (result_ll > INT_MAX) { |
129 | 0 | crm_warn("Clipped '%s' to maximum integer %d", text, INT_MAX); |
130 | 0 | result_ll = (long long) INT_MAX; |
131 | 0 | rc = EOVERFLOW; |
132 | 0 | } |
133 | |
|
134 | 0 | if (result != NULL) { |
135 | 0 | *result = (int) result_ll; |
136 | 0 | } |
137 | 0 | return rc; |
138 | 0 | } |
139 | | |
140 | | /*! |
141 | | * \internal |
142 | | * \brief Scan a TCP port number from a string |
143 | | * |
144 | | * \param[in] text The string to scan |
145 | | * \param[out] port Where to store result (or NULL to ignore) |
146 | | * |
147 | | * \return Standard Pacemaker return code |
148 | | * \note \p port will be -1 if \p text is NULL or invalid |
149 | | */ |
150 | | int |
151 | | pcmk__scan_port(const char *text, int *port) |
152 | 0 | { |
153 | 0 | long long port_ll; |
154 | 0 | int rc = pcmk__scan_ll(text, &port_ll, -1LL); |
155 | |
|
156 | 0 | if (rc != pcmk_rc_ok) { |
157 | 0 | crm_warn("'%s' is not a valid port: %s", text, pcmk_rc_str(rc)); |
158 | |
|
159 | 0 | } else if ((text != NULL) // wasn't default or invalid |
160 | 0 | && ((port_ll < 0LL) || (port_ll > 65535LL))) { |
161 | 0 | crm_warn("Ignoring port specification '%s' " |
162 | 0 | "not in valid range (0-65535)", text); |
163 | 0 | rc = (port_ll < 0LL)? pcmk_rc_before_range : pcmk_rc_after_range; |
164 | 0 | port_ll = -1LL; |
165 | 0 | } |
166 | 0 | if (port != NULL) { |
167 | 0 | *port = (int) port_ll; |
168 | 0 | } |
169 | 0 | return rc; |
170 | 0 | } |
171 | | |
172 | | /*! |
173 | | * \internal |
174 | | * \brief Scan a double-precision floating-point value from a string |
175 | | * |
176 | | * \param[in] text The string to parse |
177 | | * \param[out] result Parsed value on success, or |
178 | | * \c PCMK__PARSE_DBL_DEFAULT on error |
179 | | * \param[in] default_text Default string to parse if \p text is |
180 | | * \c NULL |
181 | | * \param[out] end_text If not \c NULL, where to store a pointer |
182 | | * to the position immediately after the |
183 | | * value |
184 | | * |
185 | | * \return Standard Pacemaker return code (\c pcmk_rc_ok on success, |
186 | | * \c EINVAL on failed string conversion due to invalid input, |
187 | | * \c EOVERFLOW on arithmetic overflow, \c pcmk_rc_underflow |
188 | | * on arithmetic underflow, or \c errno from \c strtod() on |
189 | | * other parse errors) |
190 | | */ |
191 | | int |
192 | | pcmk__scan_double(const char *text, double *result, const char *default_text, |
193 | | char **end_text) |
194 | 0 | { |
195 | 0 | int rc = pcmk_rc_ok; |
196 | 0 | char *local_end_text = NULL; |
197 | |
|
198 | 0 | pcmk__assert(result != NULL); |
199 | 0 | *result = PCMK__PARSE_DBL_DEFAULT; |
200 | |
|
201 | 0 | text = (text != NULL) ? text : default_text; |
202 | |
|
203 | 0 | if (text == NULL) { |
204 | 0 | rc = EINVAL; |
205 | 0 | crm_debug("No text and no default conversion value supplied"); |
206 | |
|
207 | 0 | } else { |
208 | 0 | errno = 0; |
209 | 0 | *result = strtod(text, &local_end_text); |
210 | |
|
211 | 0 | if (errno == ERANGE) { |
212 | | /* |
213 | | * Overflow: strtod() returns +/- HUGE_VAL and sets errno to |
214 | | * ERANGE |
215 | | * |
216 | | * Underflow: strtod() returns "a value whose magnitude is |
217 | | * no greater than the smallest normalized |
218 | | * positive" double. Whether ERANGE is set is |
219 | | * implementation-defined. |
220 | | */ |
221 | 0 | const char *over_under; |
222 | |
|
223 | 0 | if (QB_ABS(*result) > DBL_MIN) { |
224 | 0 | rc = EOVERFLOW; |
225 | 0 | over_under = "over"; |
226 | 0 | } else { |
227 | 0 | rc = pcmk_rc_underflow; |
228 | 0 | over_under = "under"; |
229 | 0 | } |
230 | |
|
231 | 0 | crm_debug("Floating-point value parsed from '%s' would %sflow " |
232 | 0 | "(using %g instead)", text, over_under, *result); |
233 | |
|
234 | 0 | } else if (errno != 0) { |
235 | 0 | rc = errno; |
236 | | // strtod() set *result = 0 on parse failure |
237 | 0 | *result = PCMK__PARSE_DBL_DEFAULT; |
238 | |
|
239 | 0 | crm_debug("Could not parse floating-point value from '%s' (using " |
240 | 0 | "%.1f instead): %s", text, PCMK__PARSE_DBL_DEFAULT, |
241 | 0 | pcmk_rc_str(rc)); |
242 | |
|
243 | 0 | } else if (local_end_text == text) { |
244 | | // errno == 0, but nothing was parsed |
245 | 0 | rc = EINVAL; |
246 | 0 | *result = PCMK__PARSE_DBL_DEFAULT; |
247 | |
|
248 | 0 | crm_debug("Could not parse floating-point value from '%s' (using " |
249 | 0 | "%.1f instead): No digits found", text, |
250 | 0 | PCMK__PARSE_DBL_DEFAULT); |
251 | |
|
252 | 0 | } else if (QB_ABS(*result) <= DBL_MIN) { |
253 | | /* |
254 | | * errno == 0 and text was parsed, but value might have |
255 | | * underflowed. |
256 | | * |
257 | | * ERANGE might not be set for underflow. Check magnitude |
258 | | * of *result, but also make sure the input number is not |
259 | | * actually zero (0 <= DBL_MIN is not underflow). |
260 | | * |
261 | | * This check must come last. A parse failure in strtod() |
262 | | * also sets *result == 0, so a parse failure would match |
263 | | * this test condition prematurely. |
264 | | */ |
265 | 0 | for (const char *p = text; p != local_end_text; p++) { |
266 | 0 | if (strchr("0.eE", *p) == NULL) { |
267 | 0 | rc = pcmk_rc_underflow; |
268 | 0 | crm_debug("Floating-point value parsed from '%s' would " |
269 | 0 | "underflow (using %g instead)", text, *result); |
270 | 0 | break; |
271 | 0 | } |
272 | 0 | } |
273 | |
|
274 | 0 | } else { |
275 | 0 | crm_trace("Floating-point value parsed successfully from " |
276 | 0 | "'%s': %g", text, *result); |
277 | 0 | } |
278 | | |
279 | 0 | if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) { |
280 | 0 | crm_debug("Characters left over after parsing '%s': '%s'", |
281 | 0 | text, local_end_text); |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | 0 | if (end_text != NULL) { |
286 | 0 | *end_text = local_end_text; |
287 | 0 | } |
288 | |
|
289 | 0 | return rc; |
290 | 0 | } |
291 | | |
292 | | /*! |
293 | | * \internal |
294 | | * \brief Parse a guint from a string stored in a hash table |
295 | | * |
296 | | * \param[in] table Hash table to search |
297 | | * \param[in] key Hash table key to use to retrieve string |
298 | | * \param[in] default_val What to use if key has no entry in table |
299 | | * \param[out] result If not NULL, where to store parsed integer |
300 | | * |
301 | | * \return Standard Pacemaker return code |
302 | | */ |
303 | | int |
304 | | pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, |
305 | | guint *result) |
306 | 0 | { |
307 | 0 | const char *value; |
308 | 0 | long long value_ll; |
309 | 0 | int rc = pcmk_rc_ok; |
310 | |
|
311 | 0 | CRM_CHECK((table != NULL) && (key != NULL), return EINVAL); |
312 | | |
313 | 0 | if (result != NULL) { |
314 | 0 | *result = default_val; |
315 | 0 | } |
316 | |
|
317 | 0 | value = g_hash_table_lookup(table, key); |
318 | 0 | if (value == NULL) { |
319 | 0 | return pcmk_rc_ok; |
320 | 0 | } |
321 | | |
322 | 0 | rc = pcmk__scan_ll(value, &value_ll, 0LL); |
323 | 0 | if (rc != pcmk_rc_ok) { |
324 | 0 | crm_warn("Using default (%u) for %s because '%s' is not a " |
325 | 0 | "valid integer: %s", default_val, key, value, pcmk_rc_str(rc)); |
326 | 0 | return rc; |
327 | 0 | } |
328 | | |
329 | 0 | if ((value_ll < 0) || (value_ll > G_MAXUINT)) { |
330 | 0 | crm_warn("Using default (%u) for %s because '%s' is not in valid range", |
331 | 0 | default_val, key, value); |
332 | 0 | return ERANGE; |
333 | 0 | } |
334 | | |
335 | 0 | if (result != NULL) { |
336 | 0 | *result = (guint) value_ll; |
337 | 0 | } |
338 | 0 | return pcmk_rc_ok; |
339 | 0 | } |
340 | | |
341 | | /*! |
342 | | * \brief Parse milliseconds from a Pacemaker interval specification |
343 | | * |
344 | | * \param[in] input Pacemaker time interval specification (a bare number |
345 | | * of seconds; a number with a unit, optionally with |
346 | | * whitespace before and/or after the number; or an ISO |
347 | | * 8601 duration) (can be \c NULL) |
348 | | * \param[out] result_ms Where to store milliseconds equivalent of \p input on |
349 | | * success (limited to the range of an unsigned integer), |
350 | | * or 0 if \p input is \c NULL or invalid |
351 | | * |
352 | | * \return Standard Pacemaker return code |
353 | | */ |
354 | | int |
355 | | pcmk_parse_interval_spec(const char *input, guint *result_ms) |
356 | 1.30k | { |
357 | 1.30k | long long msec = PCMK__PARSE_INT_DEFAULT; |
358 | 1.30k | int rc = pcmk_rc_ok; |
359 | | |
360 | 1.30k | if (input == NULL) { |
361 | 0 | msec = 0; |
362 | 0 | goto done; |
363 | 0 | } |
364 | | |
365 | 1.30k | if (input[0] == 'P') { |
366 | 927 | crm_time_t *period_s = crm_time_parse_duration(input); |
367 | | |
368 | 927 | if (period_s != NULL) { |
369 | 608 | msec = crm_time_get_seconds(period_s); |
370 | 608 | msec = QB_MIN(msec, G_MAXUINT / 1000) * 1000; |
371 | 608 | crm_time_free(period_s); |
372 | 608 | } |
373 | | |
374 | 927 | } else { |
375 | 375 | rc = pcmk__parse_ms(input, &msec); |
376 | 375 | } |
377 | | |
378 | 1.30k | if (msec < 0) { |
379 | 608 | crm_warn("Using 0 instead of invalid interval specification '%s'", |
380 | 608 | input); |
381 | 608 | msec = 0; |
382 | | |
383 | 608 | if (rc == pcmk_rc_ok) { |
384 | | // Preserve any error from pcmk__parse_ms() |
385 | 537 | rc = EINVAL; |
386 | 537 | } |
387 | 608 | } |
388 | | |
389 | 1.30k | done: |
390 | 1.30k | if (result_ms != NULL) { |
391 | 1.30k | *result_ms = (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec; |
392 | 1.30k | } |
393 | 1.30k | return rc; |
394 | 1.30k | } |
395 | | |
396 | | /*! |
397 | | * \internal |
398 | | * \brief Create a hash of a string suitable for use with GHashTable |
399 | | * |
400 | | * \param[in] v String to hash |
401 | | * |
402 | | * \return A hash of \p v compatible with g_str_hash() before glib 2.28 |
403 | | * \note glib changed their hash implementation: |
404 | | * |
405 | | * https://gitlab.gnome.org/GNOME/glib/commit/354d655ba8a54b754cb5a3efb42767327775696c |
406 | | * |
407 | | * Note that the new g_str_hash is presumably a *better* hash (it's actually |
408 | | * a correct implementation of DJB's hash), but we need to preserve existing |
409 | | * behaviour, because the hash key ultimately determines the "sort" order |
410 | | * when iterating through GHashTables, which affects allocation of scores to |
411 | | * clone instances when iterating through allowed nodes. It (somehow) also |
412 | | * appears to have some minor impact on the ordering of a few pseudo_event IDs |
413 | | * in the transition graph. |
414 | | */ |
415 | | static guint |
416 | | pcmk__str_hash(gconstpointer v) |
417 | 0 | { |
418 | 0 | const signed char *p; |
419 | 0 | guint32 h = 0; |
420 | |
|
421 | 0 | for (p = v; *p != '\0'; p++) |
422 | 0 | h = (h << 5) - h + *p; |
423 | |
|
424 | 0 | return h; |
425 | 0 | } |
426 | | |
427 | | /*! |
428 | | * \internal |
429 | | * \brief Create a hash table with case-sensitive strings as keys |
430 | | * |
431 | | * \param[in] key_destroy_func Function to free a key |
432 | | * \param[in] value_destroy_func Function to free a value |
433 | | * |
434 | | * \return Newly allocated hash table |
435 | | * \note It is the caller's responsibility to free the result, using |
436 | | * g_hash_table_destroy(). |
437 | | */ |
438 | | GHashTable * |
439 | | pcmk__strkey_table(GDestroyNotify key_destroy_func, |
440 | | GDestroyNotify value_destroy_func) |
441 | 0 | { |
442 | 0 | return g_hash_table_new_full(pcmk__str_hash, g_str_equal, |
443 | 0 | key_destroy_func, value_destroy_func); |
444 | 0 | } |
445 | | |
446 | | /*! |
447 | | * \internal |
448 | | * \brief Insert string copies into a hash table as key and value |
449 | | * |
450 | | * \param[in,out] table Hash table to add to |
451 | | * \param[in] name String to add a copy of as key |
452 | | * \param[in] value String to add a copy of as value |
453 | | * |
454 | | * \note This asserts on invalid arguments or memory allocation failure. |
455 | | */ |
456 | | void |
457 | | pcmk__insert_dup(GHashTable *table, const char *name, const char *value) |
458 | 0 | { |
459 | 0 | pcmk__assert((table != NULL) && (name != NULL)); |
460 | |
|
461 | 0 | g_hash_table_insert(table, pcmk__str_copy(name), pcmk__str_copy(value)); |
462 | 0 | } |
463 | | |
464 | | /* used with hash tables where case does not matter */ |
465 | | static gboolean |
466 | | pcmk__strcase_equal(gconstpointer a, gconstpointer b) |
467 | 0 | { |
468 | 0 | return pcmk__str_eq((const char *)a, (const char *)b, pcmk__str_casei); |
469 | 0 | } |
470 | | |
471 | | static guint |
472 | | pcmk__strcase_hash(gconstpointer v) |
473 | 0 | { |
474 | 0 | const signed char *p; |
475 | 0 | guint32 h = 0; |
476 | |
|
477 | 0 | for (p = v; *p != '\0'; p++) |
478 | 0 | h = (h << 5) - h + g_ascii_tolower(*p); |
479 | |
|
480 | 0 | return h; |
481 | 0 | } |
482 | | |
483 | | /*! |
484 | | * \internal |
485 | | * \brief Create a hash table with case-insensitive strings as keys |
486 | | * |
487 | | * \param[in] key_destroy_func Function to free a key |
488 | | * \param[in] value_destroy_func Function to free a value |
489 | | * |
490 | | * \return Newly allocated hash table |
491 | | * \note It is the caller's responsibility to free the result, using |
492 | | * g_hash_table_destroy(). |
493 | | */ |
494 | | GHashTable * |
495 | | pcmk__strikey_table(GDestroyNotify key_destroy_func, |
496 | | GDestroyNotify value_destroy_func) |
497 | 0 | { |
498 | 0 | return g_hash_table_new_full(pcmk__strcase_hash, pcmk__strcase_equal, |
499 | 0 | key_destroy_func, value_destroy_func); |
500 | 0 | } |
501 | | |
502 | | static void |
503 | | copy_str_table_entry(gpointer key, gpointer value, gpointer user_data) |
504 | 0 | { |
505 | 0 | if (key && value && user_data) { |
506 | 0 | pcmk__insert_dup((GHashTable *) user_data, |
507 | 0 | (const char *) key, (const char *) value); |
508 | 0 | } |
509 | 0 | } |
510 | | |
511 | | /*! |
512 | | * \internal |
513 | | * \brief Copy a hash table that uses dynamically allocated strings |
514 | | * |
515 | | * \param[in,out] old_table Hash table to duplicate |
516 | | * |
517 | | * \return New hash table with copies of everything in \p old_table |
518 | | * \note This assumes the hash table uses dynamically allocated strings -- that |
519 | | * is, both the key and value free functions are free(). |
520 | | */ |
521 | | GHashTable * |
522 | | pcmk__str_table_dup(GHashTable *old_table) |
523 | 0 | { |
524 | 0 | GHashTable *new_table = NULL; |
525 | |
|
526 | 0 | if (old_table) { |
527 | 0 | new_table = pcmk__strkey_table(free, free); |
528 | 0 | g_hash_table_foreach(old_table, copy_str_table_entry, new_table); |
529 | 0 | } |
530 | 0 | return new_table; |
531 | 0 | } |
532 | | |
533 | | /*! |
534 | | * \internal |
535 | | * \brief Add a word to a string list of words |
536 | | * |
537 | | * \param[in,out] list Pointer to current string list (may not be \p NULL) |
538 | | * \param[in] init_size \p list will be initialized to at least this size, |
539 | | * if it needs initialization (if 0, use GLib's default |
540 | | * initial string size) |
541 | | * \param[in] word String to add to \p list (\p list will be |
542 | | * unchanged if this is \p NULL or the empty string) |
543 | | * \param[in] separator String to separate words in \p list |
544 | | * |
545 | | * \note \p word may contain \p separator, though that would be a bad idea if |
546 | | * the string needs to be parsed later. |
547 | | */ |
548 | | void |
549 | | pcmk__add_separated_word(GString **list, size_t init_size, const char *word, |
550 | | const char *separator) |
551 | 0 | { |
552 | 0 | pcmk__assert((list != NULL) && (separator != NULL)); |
553 | |
|
554 | 0 | if (pcmk__str_empty(word)) { |
555 | 0 | return; |
556 | 0 | } |
557 | | |
558 | 0 | if (*list == NULL) { |
559 | 0 | if (init_size > 0) { |
560 | 0 | *list = g_string_sized_new(init_size); |
561 | 0 | } else { |
562 | 0 | *list = g_string_new(NULL); |
563 | 0 | } |
564 | 0 | } |
565 | |
|
566 | 0 | if ((*list)->len > 0) { |
567 | | // Don't add a separator before the first word in the list |
568 | 0 | g_string_append(*list, separator); |
569 | 0 | } |
570 | 0 | g_string_append(*list, word); |
571 | 0 | } |
572 | | |
573 | | /*! |
574 | | * \internal |
575 | | * \brief Compress data |
576 | | * |
577 | | * \param[in] data Data to compress |
578 | | * \param[in] length Number of characters of data to compress |
579 | | * \param[in] max Maximum size of compressed data (or 0 to estimate) |
580 | | * \param[out] result Where to store newly allocated compressed result |
581 | | * \param[out] result_len Where to store actual compressed length of result |
582 | | * |
583 | | * \return Standard Pacemaker return code |
584 | | */ |
585 | | int |
586 | | pcmk__compress(const char *data, unsigned int length, unsigned int max, |
587 | | char **result, unsigned int *result_len) |
588 | 0 | { |
589 | 0 | int rc; |
590 | 0 | char *compressed = NULL; |
591 | 0 | char *uncompressed = strdup(data); |
592 | 0 | #ifdef CLOCK_MONOTONIC |
593 | 0 | struct timespec after_t; |
594 | 0 | struct timespec before_t; |
595 | 0 | #endif |
596 | |
|
597 | 0 | if (max == 0) { |
598 | 0 | max = (length * 1.01) + 601; // Size guaranteed to hold result |
599 | 0 | } |
600 | |
|
601 | 0 | #ifdef CLOCK_MONOTONIC |
602 | 0 | clock_gettime(CLOCK_MONOTONIC, &before_t); |
603 | 0 | #endif |
604 | |
|
605 | 0 | compressed = pcmk__assert_alloc((size_t) max, sizeof(char)); |
606 | |
|
607 | 0 | *result_len = max; |
608 | 0 | rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, |
609 | 0 | PCMK__BZ2_BLOCKS, 0, PCMK__BZ2_WORK); |
610 | 0 | rc = pcmk__bzlib2rc(rc); |
611 | |
|
612 | 0 | free(uncompressed); |
613 | |
|
614 | 0 | if (rc != pcmk_rc_ok) { |
615 | 0 | crm_err("Compression of %d bytes failed: %s " QB_XS " rc=%d", |
616 | 0 | length, pcmk_rc_str(rc), rc); |
617 | 0 | free(compressed); |
618 | 0 | return rc; |
619 | 0 | } |
620 | | |
621 | 0 | #ifdef CLOCK_MONOTONIC |
622 | 0 | clock_gettime(CLOCK_MONOTONIC, &after_t); |
623 | |
|
624 | 0 | crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms", |
625 | 0 | length, *result_len, length / (*result_len), |
626 | 0 | (after_t.tv_sec - before_t.tv_sec) * 1000 + |
627 | 0 | (after_t.tv_nsec - before_t.tv_nsec) / 1e6); |
628 | | #else |
629 | | crm_trace("Compressed %d bytes into %d (ratio %d:1)", |
630 | | length, *result_len, length / (*result_len)); |
631 | | #endif |
632 | | |
633 | 0 | *result = compressed; |
634 | 0 | return pcmk_rc_ok; |
635 | 0 | } |
636 | | |
637 | | /*! |
638 | | * \internal |
639 | | * \brief Parse a boolean value from a string |
640 | | * |
641 | | * Valid input strings (case-insensitive) are as follows: |
642 | | * * \c PCMK_VALUE_TRUE, \c "on", \c "yes", \c "y", or \c "1" for \c true |
643 | | * * \c PCMK_VALUE_FALSE, \c PCMK_VALUE_OFF, \c "no", \c "n", or \c "0" for |
644 | | * \c false |
645 | | * |
646 | | * \param[in] input Input string |
647 | | * \param[out] result Where to store result (can be \c NULL; unchanged on |
648 | | * error) |
649 | | * |
650 | | * \retval Standard Pacemaker return code |
651 | | */ |
652 | | int |
653 | | pcmk__parse_bool(const char *input, bool *result) |
654 | 0 | { |
655 | 0 | bool local_result = false; |
656 | |
|
657 | 0 | CRM_CHECK(input != NULL, return EINVAL); |
658 | | |
659 | 0 | if (pcmk__strcase_any_of(input, PCMK_VALUE_TRUE, "on", "yes", "y", "1", |
660 | 0 | NULL)) { |
661 | 0 | local_result = true; |
662 | |
|
663 | 0 | } else if (pcmk__strcase_any_of(input, PCMK_VALUE_FALSE, PCMK_VALUE_OFF, |
664 | 0 | "no", "n", "0", NULL)) { |
665 | 0 | local_result = false; |
666 | |
|
667 | 0 | } else { |
668 | 0 | return pcmk_rc_bad_input; |
669 | 0 | } |
670 | | |
671 | 0 | if (result != NULL) { |
672 | 0 | *result = local_result; |
673 | 0 | } |
674 | 0 | return pcmk_rc_ok; |
675 | 0 | } |
676 | | |
677 | | /*! |
678 | | * \internal |
679 | | * \brief Parse a range specification string |
680 | | * |
681 | | * A valid range specification string can be in any of the following forms, |
682 | | * where \c "X", \c "Y", and \c "Z" are nonnegative integers that fit into a |
683 | | * <tt>long long</tt> variable: |
684 | | * * "X-Y" |
685 | | * * "X-" |
686 | | * * "-Y" |
687 | | * * "Z" |
688 | | * |
689 | | * In the list above, \c "X" is the start value and \c "Y" is the end value of |
690 | | * the range. Either the start value or the end value, but not both, can be |
691 | | * empty. \c "Z", a single integer with no \c '-' character, is both the start |
692 | | * value and the end value of its range. |
693 | | * |
694 | | * If the start value or end value is empty, then the parsed result stored in |
695 | | * \p *start or \p *end (respectively) is \c PCMK__PARSE_INT_DEFAULT after a |
696 | | * successful parse. |
697 | | * |
698 | | * If the specification string consists of only a single number, then the same |
699 | | * value is stored in both \p *start and \p *end on a successful parse. |
700 | | * |
701 | | * \param[in] text String to parse |
702 | | * \param[out] start Where to store start value (can be \c NULL) |
703 | | * \param[out] end Where to store end value (can be \c NULL) |
704 | | * |
705 | | * \return Standard Pacemaker return code |
706 | | * |
707 | | * \note The values stored in \p *start and \p *end are undefined if the return |
708 | | * value is not \c pcmk_rc_ok. |
709 | | */ |
710 | | int |
711 | | pcmk__parse_ll_range(const char *text, long long *start, long long *end) |
712 | 0 | { |
713 | 0 | int rc = pcmk_rc_ok; |
714 | 0 | long long local_start = 0; |
715 | 0 | long long local_end = 0; |
716 | 0 | gchar **split = NULL; |
717 | 0 | guint length = 0; |
718 | 0 | const gchar *start_s = NULL; |
719 | 0 | const gchar *end_s = NULL; |
720 | | |
721 | | // Do not free |
722 | 0 | char *remainder = NULL; |
723 | |
|
724 | 0 | if (start == NULL) { |
725 | 0 | start = &local_start; |
726 | 0 | } |
727 | 0 | if (end == NULL) { |
728 | 0 | end = &local_end; |
729 | 0 | } |
730 | 0 | *start = PCMK__PARSE_INT_DEFAULT; |
731 | 0 | *end = PCMK__PARSE_INT_DEFAULT; |
732 | |
|
733 | 0 | if (pcmk__str_empty(text)) { |
734 | 0 | rc = ENODATA; |
735 | 0 | goto done; |
736 | 0 | } |
737 | | |
738 | 0 | split = g_strsplit(text, "-", 2); |
739 | 0 | length = g_strv_length(split); |
740 | 0 | start_s = split[0]; |
741 | 0 | if (length == 2) { |
742 | 0 | end_s = split[1]; |
743 | 0 | } |
744 | |
|
745 | 0 | if (pcmk__str_empty(start_s) && pcmk__str_empty(end_s)) { |
746 | 0 | rc = pcmk_rc_bad_input; |
747 | 0 | goto done; |
748 | 0 | } |
749 | | |
750 | 0 | if (!pcmk__str_empty(start_s)) { |
751 | 0 | rc = scan_ll(start_s, start, PCMK__PARSE_INT_DEFAULT, &remainder); |
752 | 0 | if (rc != pcmk_rc_ok) { |
753 | 0 | goto done; |
754 | 0 | } |
755 | 0 | if (!pcmk__str_empty(remainder)) { |
756 | 0 | rc = pcmk_rc_bad_input; |
757 | 0 | goto done; |
758 | 0 | } |
759 | 0 | } |
760 | | |
761 | 0 | if (length == 1) { |
762 | | // String contains only a single number, which is both start and end |
763 | 0 | *end = *start; |
764 | 0 | goto done; |
765 | 0 | } |
766 | | |
767 | 0 | if (!pcmk__str_empty(end_s)) { |
768 | 0 | rc = scan_ll(end_s, end, PCMK__PARSE_INT_DEFAULT, &remainder); |
769 | |
|
770 | 0 | if ((rc == pcmk_rc_ok) && !pcmk__str_empty(remainder)) { |
771 | 0 | rc = pcmk_rc_bad_input; |
772 | 0 | } |
773 | 0 | } |
774 | |
|
775 | 0 | done: |
776 | 0 | g_strfreev(split); |
777 | 0 | return rc; |
778 | 0 | } |
779 | | |
780 | | /*! |
781 | | * \internal |
782 | | * \brief Get multiplier and divisor corresponding to given units string |
783 | | * |
784 | | * Multiplier and divisor convert from a number of seconds to an equivalent |
785 | | * number of the unit described by the units string. |
786 | | * |
787 | | * \param[in] units String describing a unit of time (may be empty, |
788 | | * \c "s", \c "sec", \c "ms", \c "msec", \c "us", |
789 | | * \c "usec", \c "m", \c "min", \c "h", or \c "hr") |
790 | | * \param[out] multiplier Number of units in one second, if unit is smaller |
791 | | * than one second, or 1 otherwise (unchanged on error) |
792 | | * \param[out] divisor Number of seconds in one unit, if unit is larger |
793 | | * than one second, or 1 otherwise (unchanged on error) |
794 | | * |
795 | | * \return Standard Pacemaker return code |
796 | | */ |
797 | | static int |
798 | | get_multiplier_divisor(const char *units, long long *multiplier, |
799 | | long long *divisor) |
800 | 718 | { |
801 | | /* @COMPAT Use exact comparisons. Currently, we match too liberally, and the |
802 | | * second strncasecmp() in each case is redundant. |
803 | | */ |
804 | 718 | if ((*units == '\0') |
805 | 612 | || (strncasecmp(units, "s", 1) == 0) |
806 | 590 | || (strncasecmp(units, "sec", 3) == 0)) { |
807 | 128 | *multiplier = 1000; |
808 | 128 | *divisor = 1; |
809 | | |
810 | 590 | } else if ((strncasecmp(units, "ms", 2) == 0) |
811 | 496 | || (strncasecmp(units, "msec", 4) == 0)) { |
812 | 94 | *multiplier = 1; |
813 | 94 | *divisor = 1; |
814 | | |
815 | 496 | } else if ((strncasecmp(units, "us", 2) == 0) |
816 | 474 | || (strncasecmp(units, "usec", 4) == 0)) { |
817 | 22 | *multiplier = 1; |
818 | 22 | *divisor = 1000; |
819 | | |
820 | 474 | } else if ((strncasecmp(units, "m", 1) == 0) |
821 | 432 | || (strncasecmp(units, "min", 3) == 0)) { |
822 | 42 | *multiplier = 60 * 1000; |
823 | 42 | *divisor = 1; |
824 | | |
825 | 432 | } else if ((strncasecmp(units, "h", 1) == 0) |
826 | 324 | || (strncasecmp(units, "hr", 2) == 0)) { |
827 | 324 | *multiplier = 60 * 60 * 1000; |
828 | 324 | *divisor = 1; |
829 | | |
830 | 324 | } else { |
831 | | // Invalid units |
832 | 108 | return pcmk_rc_bad_input; |
833 | 108 | } |
834 | | |
835 | 610 | return pcmk_rc_ok; |
836 | 718 | } |
837 | | |
838 | | /*! |
839 | | * \internal |
840 | | * \brief Parse a time and units string into a milliseconds value |
841 | | * |
842 | | * \param[in] input String with a nonnegative number and optional unit |
843 | | * (optionally with whitespace before and/or after the |
844 | | * number). If absent, the unit defaults to seconds. |
845 | | * \param[out] result Where to store result in milliseconds (unchanged on error |
846 | | * except \c ERANGE) |
847 | | * |
848 | | * \return Standard Pacemaker return code |
849 | | */ |
850 | | int |
851 | | pcmk__parse_ms(const char *input, long long *result) |
852 | 1.67k | { |
853 | 1.67k | long long local_result = 0; |
854 | 1.67k | char *units = NULL; // Do not free; will point to part of input |
855 | 1.67k | long long multiplier = 1000; |
856 | 1.67k | long long divisor = 1; |
857 | 1.67k | int rc = pcmk_rc_ok; |
858 | | |
859 | 1.67k | CRM_CHECK(input != NULL, return EINVAL); |
860 | | |
861 | 1.67k | rc = scan_ll(input, &local_result, 0, &units); |
862 | 1.67k | if ((rc == pcmk_rc_ok) || (rc == ERANGE)) { |
863 | 718 | int units_rc = pcmk_rc_ok; |
864 | | |
865 | | /* If the number is a decimal, scan_ll() reads only the integer part. |
866 | | * Skip any remaining digits or decimal characters. |
867 | | * |
868 | | * @COMPAT Well-formed and malformed decimals are both accepted inputs. |
869 | | * For example, "3.14 ms" and "3.1.4 ms" are treated the same as "3ms" |
870 | | * and parsed successfully. At a compatibility break, decide if this is |
871 | | * still desired. |
872 | | */ |
873 | 1.37k | for (; isdigit(*units) || (*units == '.'); units++); |
874 | | |
875 | | // Skip any additional whitespace after the number |
876 | 718 | for (; isspace(*units); units++); |
877 | | |
878 | | // Validate units and get conversion constants |
879 | 718 | units_rc = get_multiplier_divisor(units, &multiplier, &divisor); |
880 | 718 | if (units_rc != pcmk_rc_ok) { |
881 | 108 | rc = units_rc; |
882 | 108 | } |
883 | 718 | } |
884 | | |
885 | 1.67k | if (rc == ERANGE) { |
886 | 4 | crm_warn("'%s' will be clipped to %lld", input, local_result); |
887 | | |
888 | | /* Continue through rest of body before returning ERANGE |
889 | | * |
890 | | * @COMPAT Improve handling of overflow. Units won't necessarily be |
891 | | * respected right now, for one thing. |
892 | | */ |
893 | | |
894 | 1.67k | } else if (rc != pcmk_rc_ok) { |
895 | 1.06k | crm_warn("'%s' is not a valid time duration: %s", input, |
896 | 1.06k | pcmk_rc_str(rc)); |
897 | 1.06k | return rc; |
898 | 1.06k | } |
899 | | |
900 | 610 | if (result == NULL) { |
901 | 0 | return rc; |
902 | 0 | } |
903 | | |
904 | | // Apply units, capping at LLONG_MAX |
905 | 610 | if (local_result > (LLONG_MAX / multiplier)) { |
906 | 126 | *result = LLONG_MAX; |
907 | 484 | } else if (local_result < (LLONG_MIN / multiplier)) { |
908 | 4 | *result = LLONG_MIN; |
909 | 480 | } else { |
910 | 480 | *result = (local_result * multiplier) / divisor; |
911 | 480 | } |
912 | | |
913 | 610 | return rc; |
914 | 610 | } |
915 | | |
916 | | /*! |
917 | | * \internal |
918 | | * \brief Find a string in a list of strings |
919 | | * |
920 | | * \note This function takes the same flags and has the same behavior as |
921 | | * pcmk__str_eq(). |
922 | | * |
923 | | * \note No matter what input string or flags are provided, an empty |
924 | | * list will always return FALSE. |
925 | | * |
926 | | * \param[in] s String to search for |
927 | | * \param[in] lst List to search |
928 | | * \param[in] flags A bitfield of pcmk__str_flags to modify operation |
929 | | * |
930 | | * \return \c TRUE if \p s is in \p lst, or \c FALSE otherwise |
931 | | */ |
932 | | gboolean |
933 | | pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags) |
934 | 0 | { |
935 | 0 | for (const GList *ele = lst; ele != NULL; ele = ele->next) { |
936 | 0 | if (pcmk__str_eq(s, ele->data, flags)) { |
937 | 0 | return TRUE; |
938 | 0 | } |
939 | 0 | } |
940 | | |
941 | 0 | return FALSE; |
942 | 0 | } |
943 | | |
944 | | static bool |
945 | | str_any_of(const char *s, va_list args, uint32_t flags) |
946 | 176 | { |
947 | 176 | if (s == NULL) { |
948 | 0 | return false; |
949 | 0 | } |
950 | | |
951 | 524 | while (1) { |
952 | 524 | const char *ele = va_arg(args, const char *); |
953 | | |
954 | 524 | if (ele == NULL) { |
955 | 173 | break; |
956 | 351 | } else if (pcmk__str_eq(s, ele, flags)) { |
957 | 3 | return true; |
958 | 3 | } |
959 | 524 | } |
960 | | |
961 | 173 | return false; |
962 | 176 | } |
963 | | |
964 | | /*! |
965 | | * \internal |
966 | | * \brief Is a string a member of a list of strings? |
967 | | * |
968 | | * \param[in] s String to search for in \p ... |
969 | | * \param[in] ... Strings to compare \p s against. The final string |
970 | | * must be NULL. |
971 | | * |
972 | | * \note The comparison is done case-insensitively. The function name is |
973 | | * meant to be reminiscent of strcasecmp. |
974 | | * |
975 | | * \return \c true if \p s is in \p ..., or \c false otherwise |
976 | | */ |
977 | | bool |
978 | | pcmk__strcase_any_of(const char *s, ...) |
979 | 0 | { |
980 | 0 | va_list ap; |
981 | 0 | bool rc; |
982 | |
|
983 | 0 | va_start(ap, s); |
984 | 0 | rc = str_any_of(s, ap, pcmk__str_casei); |
985 | 0 | va_end(ap); |
986 | 0 | return rc; |
987 | 0 | } |
988 | | |
989 | | /*! |
990 | | * \internal |
991 | | * \brief Is a string a member of a list of strings? |
992 | | * |
993 | | * \param[in] s String to search for in \p ... |
994 | | * \param[in] ... Strings to compare \p s against. The final string |
995 | | * must be NULL. |
996 | | * |
997 | | * \note The comparison is done taking case into account. |
998 | | * |
999 | | * \return \c true if \p s is in \p ..., or \c false otherwise |
1000 | | */ |
1001 | | bool |
1002 | | pcmk__str_any_of(const char *s, ...) |
1003 | 176 | { |
1004 | 176 | va_list ap; |
1005 | 176 | bool rc; |
1006 | | |
1007 | 176 | va_start(ap, s); |
1008 | 176 | rc = str_any_of(s, ap, pcmk__str_none); |
1009 | 176 | va_end(ap); |
1010 | 176 | return rc; |
1011 | 176 | } |
1012 | | |
1013 | | /*! |
1014 | | * \internal |
1015 | | * \brief Sort strings, with numeric portions sorted numerically |
1016 | | * |
1017 | | * Sort two strings case-insensitively like strcasecmp(), but with any numeric |
1018 | | * portions of the string sorted numerically. This is particularly useful for |
1019 | | * node names (for example, "node10" will sort higher than "node9" but lower |
1020 | | * than "remotenode9"). |
1021 | | * |
1022 | | * \param[in] s1 First string to compare (must not be NULL) |
1023 | | * \param[in] s2 Second string to compare (must not be NULL) |
1024 | | * |
1025 | | * \retval -1 \p s1 comes before \p s2 |
1026 | | * \retval 0 \p s1 and \p s2 are equal |
1027 | | * \retval 1 \p s1 comes after \p s2 |
1028 | | */ |
1029 | | int |
1030 | | pcmk__numeric_strcasecmp(const char *s1, const char *s2) |
1031 | 1.30k | { |
1032 | 1.30k | pcmk__assert((s1 != NULL) && (s2 != NULL)); |
1033 | | |
1034 | 720k | while (*s1 && *s2) { |
1035 | 718k | if (isdigit(*s1) && isdigit(*s2)) { |
1036 | | // If node names contain a number, sort numerically |
1037 | | |
1038 | 108k | char *end1 = NULL; |
1039 | 108k | char *end2 = NULL; |
1040 | 108k | long num1 = strtol(s1, &end1, 10); |
1041 | 108k | long num2 = strtol(s2, &end2, 10); |
1042 | | |
1043 | | // allow ordering e.g. 007 > 7 |
1044 | 108k | size_t len1 = end1 - s1; |
1045 | 108k | size_t len2 = end2 - s2; |
1046 | | |
1047 | 108k | if (num1 < num2) { |
1048 | 0 | return -1; |
1049 | 108k | } else if (num1 > num2) { |
1050 | 0 | return 1; |
1051 | 108k | } else if (len1 < len2) { |
1052 | 0 | return -1; |
1053 | 108k | } else if (len1 > len2) { |
1054 | 0 | return 1; |
1055 | 0 | } |
1056 | 108k | s1 = end1; |
1057 | 108k | s2 = end2; |
1058 | 610k | } else { |
1059 | | // Compare non-digits case-insensitively |
1060 | 610k | int lower1 = tolower(*s1); |
1061 | 610k | int lower2 = tolower(*s2); |
1062 | | |
1063 | 610k | if (lower1 < lower2) { |
1064 | 0 | return -1; |
1065 | 610k | } else if (lower1 > lower2) { |
1066 | 0 | return 1; |
1067 | 0 | } |
1068 | 610k | ++s1; |
1069 | 610k | ++s2; |
1070 | 610k | } |
1071 | 718k | } |
1072 | 1.30k | if (!*s1 && *s2) { |
1073 | 0 | return -1; |
1074 | 1.30k | } else if (*s1 && !*s2) { |
1075 | 0 | return 1; |
1076 | 0 | } |
1077 | 1.30k | return 0; |
1078 | 1.30k | } |
1079 | | |
1080 | | /*! |
1081 | | * \internal |
1082 | | * \brief Sort strings. |
1083 | | * |
1084 | | * This is your one-stop function for string comparison. By default, this |
1085 | | * function works like \p g_strcmp0. That is, like \p strcmp but a \p NULL |
1086 | | * string sorts before a non-<tt>NULL</tt> string. |
1087 | | * |
1088 | | * The \p pcmk__str_none flag produces the default behavior. Behavior can be |
1089 | | * changed with various flags: |
1090 | | * |
1091 | | * - \p pcmk__str_regex - The second string is a regular expression that the |
1092 | | * first string will be matched against. |
1093 | | * - \p pcmk__str_casei - By default, comparisons are done taking case into |
1094 | | * account. This flag makes comparisons case- |
1095 | | * insensitive. This can be combined with |
1096 | | * \p pcmk__str_regex. |
1097 | | * - \p pcmk__str_null_matches - If one string is \p NULL and the other is not, |
1098 | | * still return \p 0. |
1099 | | * - \p pcmk__str_star_matches - If one string is \p "*" and the other is not, |
1100 | | * still return \p 0. |
1101 | | * |
1102 | | * \param[in] s1 First string to compare |
1103 | | * \param[in] s2 Second string to compare, or a regular expression to |
1104 | | * match if \p pcmk__str_regex is set |
1105 | | * \param[in] flags A bitfield of \p pcmk__str_flags to modify operation |
1106 | | * |
1107 | | * \retval negative \p s1 is \p NULL or comes before \p s2 |
1108 | | * \retval 0 \p s1 and \p s2 are equal, or \p s1 is found in \p s2 if |
1109 | | * \c pcmk__str_regex is set |
1110 | | * \retval positive \p s2 is \p NULL or \p s1 comes after \p s2, or \p s2 |
1111 | | * is an invalid regular expression, or \p s1 was not found |
1112 | | * in \p s2 if \p pcmk__str_regex is set. |
1113 | | */ |
1114 | | int |
1115 | | pcmk__strcmp(const char *s1, const char *s2, uint32_t flags) |
1116 | 527 | { |
1117 | | /* If this flag is set, the second string is a regex. */ |
1118 | 527 | if (pcmk__is_set(flags, pcmk__str_regex)) { |
1119 | 0 | regex_t r_patt; |
1120 | 0 | int reg_flags = REG_EXTENDED | REG_NOSUB; |
1121 | 0 | int regcomp_rc = 0; |
1122 | 0 | int rc = 0; |
1123 | |
|
1124 | 0 | if (s1 == NULL || s2 == NULL) { |
1125 | 0 | return 1; |
1126 | 0 | } |
1127 | | |
1128 | 0 | if (pcmk__is_set(flags, pcmk__str_casei)) { |
1129 | 0 | reg_flags |= REG_ICASE; |
1130 | 0 | } |
1131 | 0 | regcomp_rc = regcomp(&r_patt, s2, reg_flags); |
1132 | 0 | if (regcomp_rc != 0) { |
1133 | 0 | rc = 1; |
1134 | 0 | crm_err("Bad regex '%s' for update: %s", s2, strerror(regcomp_rc)); |
1135 | 0 | } else { |
1136 | 0 | rc = regexec(&r_patt, s1, 0, NULL, 0); |
1137 | 0 | regfree(&r_patt); |
1138 | 0 | if (rc != 0) { |
1139 | 0 | rc = 1; |
1140 | 0 | } |
1141 | 0 | } |
1142 | 0 | return rc; |
1143 | 0 | } |
1144 | | |
1145 | | /* If the strings are the same pointer, return 0 immediately. */ |
1146 | 527 | if (s1 == s2) { |
1147 | 0 | return 0; |
1148 | 0 | } |
1149 | | |
1150 | | /* If this flag is set, return 0 if either (or both) of the input strings |
1151 | | * are NULL. If neither one is NULL, we need to continue and compare |
1152 | | * them normally. |
1153 | | */ |
1154 | 527 | if (pcmk__is_set(flags, pcmk__str_null_matches)) { |
1155 | 0 | if (s1 == NULL || s2 == NULL) { |
1156 | 0 | return 0; |
1157 | 0 | } |
1158 | 0 | } |
1159 | | |
1160 | | /* Handle the cases where one is NULL and the str_null_matches flag is not set. |
1161 | | * A NULL string always sorts to the beginning. |
1162 | | */ |
1163 | 527 | if (s1 == NULL) { |
1164 | 0 | return -1; |
1165 | 527 | } else if (s2 == NULL) { |
1166 | 0 | return 1; |
1167 | 0 | } |
1168 | | |
1169 | | /* If this flag is set, return 0 if either (or both) of the input strings |
1170 | | * are "*". If neither one is, we need to continue and compare them |
1171 | | * normally. |
1172 | | */ |
1173 | 527 | if (pcmk__is_set(flags, pcmk__str_star_matches)) { |
1174 | 0 | if (strcmp(s1, "*") == 0 || strcmp(s2, "*") == 0) { |
1175 | 0 | return 0; |
1176 | 0 | } |
1177 | 0 | } |
1178 | | |
1179 | 527 | if (pcmk__is_set(flags, pcmk__str_casei)) { |
1180 | 0 | return strcasecmp(s1, s2); |
1181 | 527 | } else { |
1182 | 527 | return strcmp(s1, s2); |
1183 | 527 | } |
1184 | 527 | } |
1185 | | |
1186 | | /*! |
1187 | | * \internal |
1188 | | * \brief Copy a string, asserting on failure |
1189 | | * |
1190 | | * \param[in] file File where \p function is located |
1191 | | * \param[in] function Calling function |
1192 | | * \param[in] line Line within \p file |
1193 | | * \param[in] str String to copy (can be \c NULL) |
1194 | | * |
1195 | | * \return Newly allocated copy of \p str, or \c NULL if \p str is \c NULL |
1196 | | * |
1197 | | * \note The caller is responsible for freeing the return value using \c free(). |
1198 | | */ |
1199 | | char * |
1200 | | pcmk__str_copy_as(const char *file, const char *function, uint32_t line, |
1201 | | const char *str) |
1202 | 9.43k | { |
1203 | 9.43k | if (str != NULL) { |
1204 | 9.43k | char *result = strdup(str); |
1205 | | |
1206 | 9.43k | if (result == NULL) { |
1207 | 0 | crm_abort(file, function, line, "Out of memory", FALSE, TRUE); |
1208 | 0 | crm_exit(CRM_EX_OSERR); |
1209 | 0 | } |
1210 | 9.43k | return result; |
1211 | 9.43k | } |
1212 | 0 | return NULL; |
1213 | 9.43k | } |
1214 | | |
1215 | | /*! |
1216 | | * \internal |
1217 | | * \brief Update a dynamically allocated string with a new value |
1218 | | * |
1219 | | * Given a dynamically allocated string and a new value for it, if the string |
1220 | | * is different from the new value, free the string and replace it with either a |
1221 | | * newly allocated duplicate of the value or NULL as appropriate. |
1222 | | * |
1223 | | * \param[in,out] str Pointer to dynamically allocated string |
1224 | | * \param[in] value New value to duplicate (or NULL) |
1225 | | * |
1226 | | * \note The caller remains responsibile for freeing \p *str. |
1227 | | */ |
1228 | | void |
1229 | | pcmk__str_update(char **str, const char *value) |
1230 | 0 | { |
1231 | 0 | if ((str != NULL) && !pcmk__str_eq(*str, value, pcmk__str_none)) { |
1232 | 0 | free(*str); |
1233 | 0 | *str = pcmk__str_copy(value); |
1234 | 0 | } |
1235 | 0 | } |
1236 | | |
1237 | | /*! |
1238 | | * \internal |
1239 | | * \brief Print to an allocated string using \c printf()-style formatting |
1240 | | * |
1241 | | * This is like \c asprintf() but asserts on any error. The return value cannot |
1242 | | * be \c NULL, but it may be an empty string, depending on the format string and |
1243 | | * variadic arguments. |
1244 | | * |
1245 | | * \param[in] format \c printf() format string |
1246 | | * \param[in] ... \c printf() format arguments |
1247 | | * |
1248 | | * \return Newly allocated string (guaranteed not to be \c NULL). |
1249 | | * |
1250 | | * \note The caller is responsible for freeing the return value using \c free(). |
1251 | | */ |
1252 | | char * |
1253 | | pcmk__assert_asprintf(const char *format, ...) |
1254 | 0 | { |
1255 | 0 | char *result = NULL; |
1256 | 0 | va_list ap; |
1257 | |
|
1258 | 0 | va_start(ap, format); |
1259 | 0 | pcmk__assert(vasprintf(&result, format, ap) >= 0); |
1260 | 0 | va_end(ap); |
1261 | |
|
1262 | 0 | return result; |
1263 | 0 | } |
1264 | | |
1265 | | /*! |
1266 | | * \internal |
1267 | | * \brief Append a list of strings to a destination \p GString |
1268 | | * |
1269 | | * \param[in,out] buffer Where to append the strings (must not be \p NULL) |
1270 | | * \param[in] ... A <tt>NULL</tt>-terminated list of strings |
1271 | | * |
1272 | | * \note This tends to be more efficient than a single call to |
1273 | | * \p g_string_append_printf(). |
1274 | | */ |
1275 | | void |
1276 | | pcmk__g_strcat(GString *buffer, ...) |
1277 | 0 | { |
1278 | 0 | va_list ap; |
1279 | |
|
1280 | 0 | pcmk__assert(buffer != NULL); |
1281 | 0 | va_start(ap, buffer); |
1282 | |
|
1283 | 0 | while (true) { |
1284 | 0 | const char *ele = va_arg(ap, const char *); |
1285 | |
|
1286 | 0 | if (ele == NULL) { |
1287 | 0 | break; |
1288 | 0 | } |
1289 | 0 | g_string_append(buffer, ele); |
1290 | 0 | } |
1291 | 0 | va_end(ap); |
1292 | 0 | } |
1293 | | |
1294 | | // Deprecated functions kept only for backward API compatibility |
1295 | | // LCOV_EXCL_START |
1296 | | |
1297 | | #include <crm/common/strings_compat.h> |
1298 | | |
1299 | | long long |
1300 | | crm_get_msec(const char *input) |
1301 | 0 | { |
1302 | 0 | char *units = NULL; // Do not free; will point to part of input |
1303 | 0 | long long multiplier = 1000; |
1304 | 0 | long long divisor = 1; |
1305 | 0 | long long msec = PCMK__PARSE_INT_DEFAULT; |
1306 | 0 | int rc = pcmk_rc_ok; |
1307 | |
|
1308 | 0 | if (input == NULL) { |
1309 | 0 | return PCMK__PARSE_INT_DEFAULT; |
1310 | 0 | } |
1311 | | |
1312 | | // Skip initial whitespace |
1313 | 0 | while (isspace(*input)) { |
1314 | 0 | input++; |
1315 | 0 | } |
1316 | |
|
1317 | 0 | rc = scan_ll(input, &msec, PCMK__PARSE_INT_DEFAULT, &units); |
1318 | |
|
1319 | 0 | if ((rc == ERANGE) && (msec > 0)) { |
1320 | 0 | crm_warn("'%s' will be clipped to %lld", input, msec); |
1321 | |
|
1322 | 0 | } else if ((rc != pcmk_rc_ok) || (msec < 0)) { |
1323 | 0 | crm_warn("'%s' is not a valid time duration: %s", |
1324 | 0 | input, ((rc == pcmk_rc_ok)? "Negative" : pcmk_rc_str(rc))); |
1325 | 0 | return PCMK__PARSE_INT_DEFAULT; |
1326 | 0 | } |
1327 | | |
1328 | | /* If the number is a decimal, scan_ll() reads only the integer part. Skip |
1329 | | * any remaining digits or decimal characters. |
1330 | | * |
1331 | | * @COMPAT Well-formed and malformed decimals are both accepted inputs. For |
1332 | | * example, "3.14 ms" and "3.1.4 ms" are treated the same as "3ms" and |
1333 | | * parsed successfully. At a compatibility break, decide if this is still |
1334 | | * desired. |
1335 | | */ |
1336 | 0 | while (isdigit(*units) || (*units == '.')) { |
1337 | 0 | units++; |
1338 | 0 | } |
1339 | | |
1340 | | // Skip any additional whitespace after the number |
1341 | 0 | while (isspace(*units)) { |
1342 | 0 | units++; |
1343 | 0 | } |
1344 | | |
1345 | | /* @COMPAT Use exact comparisons. Currently, we match too liberally, and the |
1346 | | * second strncasecmp() in each case is redundant. |
1347 | | */ |
1348 | 0 | if ((*units == '\0') |
1349 | 0 | || (strncasecmp(units, "s", 1) == 0) |
1350 | 0 | || (strncasecmp(units, "sec", 3) == 0)) { |
1351 | 0 | multiplier = 1000; |
1352 | 0 | divisor = 1; |
1353 | |
|
1354 | 0 | } else if ((strncasecmp(units, "ms", 2) == 0) |
1355 | 0 | || (strncasecmp(units, "msec", 4) == 0)) { |
1356 | 0 | multiplier = 1; |
1357 | 0 | divisor = 1; |
1358 | |
|
1359 | 0 | } else if ((strncasecmp(units, "us", 2) == 0) |
1360 | 0 | || (strncasecmp(units, "usec", 4) == 0)) { |
1361 | 0 | multiplier = 1; |
1362 | 0 | divisor = 1000; |
1363 | |
|
1364 | 0 | } else if ((strncasecmp(units, "m", 1) == 0) |
1365 | 0 | || (strncasecmp(units, "min", 3) == 0)) { |
1366 | 0 | multiplier = 60 * 1000; |
1367 | 0 | divisor = 1; |
1368 | |
|
1369 | 0 | } else if ((strncasecmp(units, "h", 1) == 0) |
1370 | 0 | || (strncasecmp(units, "hr", 2) == 0)) { |
1371 | 0 | multiplier = 60 * 60 * 1000; |
1372 | 0 | divisor = 1; |
1373 | |
|
1374 | 0 | } else { |
1375 | | // Invalid units |
1376 | 0 | return PCMK__PARSE_INT_DEFAULT; |
1377 | 0 | } |
1378 | | |
1379 | | // Apply units, capping at LLONG_MAX |
1380 | 0 | if (msec > (LLONG_MAX / multiplier)) { |
1381 | 0 | return LLONG_MAX; |
1382 | 0 | } |
1383 | 0 | return (msec * multiplier) / divisor; |
1384 | 0 | } |
1385 | | |
1386 | | gboolean |
1387 | | crm_is_true(const char *s) |
1388 | 0 | { |
1389 | 0 | gboolean ret = FALSE; |
1390 | |
|
1391 | 0 | return (crm_str_to_boolean(s, &ret) < 0)? FALSE : ret; |
1392 | 0 | } |
1393 | | |
1394 | | int |
1395 | | crm_str_to_boolean(const char *s, int *ret) |
1396 | 0 | { |
1397 | 0 | if (s == NULL) { |
1398 | 0 | return -1; |
1399 | 0 | } |
1400 | | |
1401 | 0 | if (pcmk__strcase_any_of(s, PCMK_VALUE_TRUE, "on", "yes", "y", "1", NULL)) { |
1402 | 0 | if (ret != NULL) { |
1403 | 0 | *ret = TRUE; |
1404 | 0 | } |
1405 | 0 | return 1; |
1406 | 0 | } |
1407 | | |
1408 | 0 | if (pcmk__strcase_any_of(s, PCMK_VALUE_FALSE, PCMK_VALUE_OFF, "no", "n", |
1409 | 0 | "0", NULL)) { |
1410 | 0 | if (ret != NULL) { |
1411 | 0 | *ret = FALSE; |
1412 | 0 | } |
1413 | 0 | return 1; |
1414 | 0 | } |
1415 | 0 | return -1; |
1416 | 0 | } |
1417 | | |
1418 | | char * |
1419 | | crm_strdup_printf(char const *format, ...) |
1420 | 0 | { |
1421 | 0 | va_list ap; |
1422 | 0 | int len = 0; |
1423 | 0 | char *string = NULL; |
1424 | |
|
1425 | 0 | va_start(ap, format); |
1426 | 0 | len = vasprintf(&string, format, ap); |
1427 | 0 | pcmk__assert(len > 0); |
1428 | | va_end(ap); |
1429 | 0 | return string; |
1430 | 0 | } |
1431 | | |
1432 | | // LCOV_EXCL_STOP |
1433 | | // End deprecated API |