From 74cb453fce44dc92d0315883d407391c32186ab3 Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Apr 07 2021 22:24:58 +0000 Subject: Refactor: functionize numeric comparisons of strings This moves the guts of sort_node_uname() from libpe_status to a new function, pcmk_numeric_strcasecmp(), in libcrmcommon, so it can be used with strings and not just pe_node_t objects. --- diff --git a/include/crm/common/util.h b/include/crm/common/util.h index 22ac8eb..f2ea944 100644 --- a/include/crm/common/util.h +++ b/include/crm/common/util.h @@ -61,6 +61,7 @@ guint g_str_hash_traditional(gconstpointer v); char *crm_strdup_printf(char const *format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end); gboolean pcmk__str_in_list(GList *lst, const gchar *s); +int pcmk_numeric_strcasecmp(const char *s1, const char *s2); # define safe_str_eq(a, b) crm_str_eq(a, b, FALSE) # define crm_str_hash g_str_hash_traditional diff --git a/lib/common/strings.c b/lib/common/strings.c index a2e17ae..fa6558f 100644 --- a/lib/common/strings.c +++ b/lib/common/strings.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -669,3 +670,67 @@ pcmk__str_in_list(GList *lst, const gchar *s) return g_list_find_custom(lst, s, (GCompareFunc) strcmp) != NULL; } + +/* + * \brief Sort strings, with numeric portions sorted numerically + * + * Sort two strings case-insensitively like strcasecmp(), but with any numeric + * portions of the string sorted numerically. This is particularly useful for + * node names (for example, "node10" will sort higher than "node9" but lower + * than "remotenode9"). + * + * \param[in] s1 First string to compare (must not be NULL) + * \param[in] s2 Second string to compare (must not be NULL) + * + * \retval -1 \p s1 comes before \p s2 + * \retval 0 \p s1 and \p s2 are equal + * \retval 1 \p s1 comes after \p s2 + */ +int +pcmk_numeric_strcasecmp(const char *s1, const char *s2) +{ + while (*s1 && *s2) { + if (isdigit(*s1) && isdigit(*s2)) { + // If node names contain a number, sort numerically + + char *end1 = NULL; + char *end2 = NULL; + long num1 = strtol(s1, &end1, 10); + long num2 = strtol(s2, &end2, 10); + + // allow ordering e.g. 007 > 7 + size_t len1 = end1 - s1; + size_t len2 = end2 - s2; + + if (num1 < num2) { + return -1; + } else if (num1 > num2) { + return 1; + } else if (len1 < len2) { + return -1; + } else if (len1 > len2) { + return 1; + } + s1 = end1; + s2 = end2; + } else { + // Compare non-digits case-insensitively + int lower1 = tolower(*s1); + int lower2 = tolower(*s2); + + if (lower1 < lower2) { + return -1; + } else if (lower1 > lower2) { + return 1; + } + ++s1; + ++s2; + } + } + if (!*s1 && *s2) { + return -1; + } else if (*s1 && !*s2) { + return 1; + } + return 0; +} diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index f8b631a..ccbf3a0 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.c @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -214,53 +213,8 @@ pe__node_list2table(GList *list) gint sort_node_uname(gconstpointer a, gconstpointer b) { - const char *name_a = ((const pe_node_t *) a)->details->uname; - const char *name_b = ((const pe_node_t *) b)->details->uname; - - while (*name_a && *name_b) { - if (isdigit(*name_a) && isdigit(*name_b)) { - // If node names contain a number, sort numerically - - char *end_a = NULL; - char *end_b = NULL; - long num_a = strtol(name_a, &end_a, 10); - long num_b = strtol(name_b, &end_b, 10); - - // allow ordering e.g. 007 > 7 - size_t len_a = end_a - name_a; - size_t len_b = end_b - name_b; - - if (num_a < num_b) { - return -1; - } else if (num_a > num_b) { - return 1; - } else if (len_a < len_b) { - return -1; - } else if (len_a > len_b) { - return 1; - } - name_a = end_a; - name_b = end_b; - } else { - // Compare non-digits case-insensitively - int lower_a = tolower(*name_a); - int lower_b = tolower(*name_b); - - if (lower_a < lower_b) { - return -1; - } else if (lower_a > lower_b) { - return 1; - } - ++name_a; - ++name_b; - } - } - if (!*name_a && *name_b) { - return -1; - } else if (*name_a && !*name_b) { - return 1; - } - return 0; + return pcmk_numeric_strcasecmp(((const pe_node_t *) a)->details->uname, + ((const pe_node_t *) b)->details->uname); } /*!