diff --git a/lib/common/scores.c b/lib/common/scores.c index 5c36fc7037..d0ea65b7f5 100644 --- a/lib/common/scores.c +++ b/lib/common/scores.c @@ -1,200 +1,204 @@ /* - * Copyright 2004-2024 the Pacemaker project contributors + * Copyright 2004-2025 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include // snprintf(), NULL #include // strcpy(), strdup() #include // size_t int pcmk__score_red = 0; int pcmk__score_green = 0; int pcmk__score_yellow = 0; /*! * \brief Parse an integer score from a string * * Parse an integer score from a string. This accepts infinity strings as well * as red, yellow, and green, and bounds the result to +/-INFINITY. * * \param[in] score_s Score as string * \param[out] score Where to store integer value corresponding to * \p score_s (may be NULL to only check validity) * \param[in] default_score Value to use if \p score_s is NULL or invalid * * \return Standard Pacemaker return code */ int pcmk_parse_score(const char *score_s, int *score, int default_score) { int rc = pcmk_rc_ok; int local_score = 0; // Ensure default score is in bounds default_score = QB_MIN(default_score, PCMK_SCORE_INFINITY); default_score = QB_MAX(default_score, -PCMK_SCORE_INFINITY); local_score = default_score; if (score_s == NULL) { } else if (pcmk_str_is_minus_infinity(score_s)) { local_score = -PCMK_SCORE_INFINITY; } else if (pcmk_str_is_infinity(score_s)) { local_score = PCMK_SCORE_INFINITY; } else if (pcmk__str_eq(score_s, PCMK_VALUE_RED, pcmk__str_casei)) { local_score = pcmk__score_red; } else if (pcmk__str_eq(score_s, PCMK_VALUE_YELLOW, pcmk__str_casei)) { local_score = pcmk__score_yellow; } else if (pcmk__str_eq(score_s, PCMK_VALUE_GREEN, pcmk__str_casei)) { local_score = pcmk__score_green; } else { long long score_ll = 0LL; rc = pcmk__scan_ll(score_s, &score_ll, default_score); if (rc == ERANGE) { rc = pcmk_rc_ok; } if (rc != pcmk_rc_ok) { local_score = default_score; } else if (score_ll > PCMK_SCORE_INFINITY) { local_score = PCMK_SCORE_INFINITY; } else if (score_ll < -PCMK_SCORE_INFINITY) { local_score = -PCMK_SCORE_INFINITY; } else { local_score = (int) score_ll; } } if (score != NULL) { *score = local_score; } return rc; } /*! * \brief Return a displayable static string for a score value * * Given a score value, return a pointer to a static string representation of * the score suitable for log messages, output, etc. * * \param[in] score Score to display * * \return Pointer to static memory containing string representation of \p score - * \note Subsequent calls to this function will overwrite the returned value, so + * \note Subsequent calls to this function may overwrite the returned value, so * it should be used only in a local context such as a printf()-style * statement. */ const char * pcmk_readable_score(int score) { - // The longest possible result is "-INFINITY" - static char score_s[sizeof(PCMK_VALUE_MINUS_INFINITY)]; - if (score >= PCMK_SCORE_INFINITY) { - strcpy(score_s, PCMK_VALUE_INFINITY); + return PCMK_VALUE_INFINITY; } else if (score <= -PCMK_SCORE_INFINITY) { - strcpy(score_s, PCMK_VALUE_MINUS_INFINITY); + return PCMK_VALUE_MINUS_INFINITY; } else { - // Range is limited to +/-1000000, so no chance of overflow - snprintf(score_s, sizeof(score_s), "%d", score); - } + // https://gcc.gnu.org/onlinedocs/gcc-15.1.0/cpp/Stringizing.html +#define xstr(s) str(s) +#define str(s) #s - return score_s; + // Result must be shorter than stringized -PCMK_SCORE_INFINITY + static char score_s[sizeof(xstr(-PCMK_SCORE_INFINITY))]; +#undef xstr +#undef str + + pcmk__assert(snprintf(score_s, sizeof(score_s), "%d", score) >= 0); + return score_s; + } } /*! * \internal * \brief Check whether a string represents an infinite value * * \param[in] s String to check * * \return \c true if \p s is "INFINITY" or "+INFINITY", otherwise \c false */ bool pcmk_str_is_infinity(const char *s) { return pcmk__str_any_of(s, PCMK_VALUE_INFINITY, PCMK_VALUE_PLUS_INFINITY, NULL); } /*! * \internal * \brief Check whether a string represents an negatively infinite value * * \param[in] s String to check * * \return \c true if \p s is "-INFINITY", otherwise \c false */ bool pcmk_str_is_minus_infinity(const char *s) { return pcmk__str_eq(s, PCMK_VALUE_MINUS_INFINITY, pcmk__str_none); } /*! * \internal * \brief Add two scores, bounding to +/-INFINITY * * \param[in] score1 First score to add * \param[in] score2 Second score to add * * \note This function does not have context about what the scores mean, so it * does not log any messages. */ int pcmk__add_scores(int score1, int score2) { /* As long as PCMK_SCORE_INFINITY is less than half of the maximum integer, * we can ignore the possibility of integer overflow. */ int result = score1 + score2; // First handle the cases where one or both is infinite if ((score1 <= -PCMK_SCORE_INFINITY) || (score2 <= -PCMK_SCORE_INFINITY)) { return -PCMK_SCORE_INFINITY; } if ((score1 >= PCMK_SCORE_INFINITY) || (score2 >= PCMK_SCORE_INFINITY)) { return PCMK_SCORE_INFINITY; } // Bound result to infinity. if (result >= PCMK_SCORE_INFINITY) { return PCMK_SCORE_INFINITY; } if (result <= -PCMK_SCORE_INFINITY) { return -PCMK_SCORE_INFINITY; } return result; } // Deprecated functions kept only for backward API compatibility // LCOV_EXCL_START #include int char2score(const char *score) { int result = 0; (void) pcmk_parse_score(score, &result, 0); return result; } // LCOV_EXCL_STOP // End deprecated API