root/lib/common/strings.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. crm_concat
  2. crm_itoa_stack
  3. crm_itoa
  4. g_hash_destroy_str
  5. crm_int_helper
  6. crm_parse_int
  7. safe_str_neq
  8. crm_is_true
  9. crm_str_to_boolean
  10. crm_strip_trailing_newline
  11. crm_str_eq
  12. null2emptystr
  13. crm_starts_with
  14. crm_ends_with_internal
  15. crm_ends_with
  16. crm_ends_with_ext
  17. g_str_hash_traditional
  18. crm_strcase_hash
  19. copy_str_table_entry
  20. crm_str_table_dup
  21. add_list_element
  22. crm_compress_string
  23. crm_alpha_sort

   1 /*
   2  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  */
  18 
  19 #include <crm_internal.h>
  20 
  21 #ifndef _GNU_SOURCE
  22 #  define _GNU_SOURCE
  23 #endif
  24 
  25 #include <stdio.h>
  26 #include <string.h>
  27 #include <stdlib.h>
  28 #include <bzlib.h>
  29 #include <sys/types.h>
  30 
  31 char *
  32 crm_concat(const char *prefix, const char *suffix, char join)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34     int len = 0;
  35     char *new_str = NULL;
  36 
  37     CRM_ASSERT(prefix != NULL);
  38     CRM_ASSERT(suffix != NULL);
  39     len = strlen(prefix) + strlen(suffix) + 2;
  40 
  41     new_str = malloc(len);
  42     if(new_str) {
  43         sprintf(new_str, "%s%c%s", prefix, join, suffix);
  44         new_str[len - 1] = 0;
  45     }
  46     return new_str;
  47 }
  48 
  49 char *
  50 crm_itoa_stack(int an_int, char *buffer, size_t len)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52     if (buffer != NULL) {
  53         snprintf(buffer, len, "%d", an_int);
  54     }
  55 
  56     return buffer;
  57 }
  58 
  59 char *
  60 crm_itoa(int an_int)
     /* [previous][next][first][last][top][bottom][index][help] */
  61 {
  62     int len = 32;
  63     char *buffer = NULL;
  64 
  65     buffer = malloc(len + 1);
  66     if (buffer != NULL) {
  67         snprintf(buffer, len, "%d", an_int);
  68     }
  69 
  70     return buffer;
  71 }
  72 
  73 void
  74 g_hash_destroy_str(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  75 {
  76     free(data);
  77 }
  78 
  79 long long
  80 crm_int_helper(const char *text, char **end_text)
     /* [previous][next][first][last][top][bottom][index][help] */
  81 {
  82     long long result = -1;
  83     char *local_end_text = NULL;
  84     int saved_errno = 0;
  85 
  86     errno = 0;
  87 
  88     if (text != NULL) {
  89 #ifdef ANSI_ONLY
  90         if (end_text != NULL) {
  91             result = strtol(text, end_text, 10);
  92         } else {
  93             result = strtol(text, &local_end_text, 10);
  94         }
  95 #else
  96         if (end_text != NULL) {
  97             result = strtoll(text, end_text, 10);
  98         } else {
  99             result = strtoll(text, &local_end_text, 10);
 100         }
 101 #endif
 102 
 103         saved_errno = errno;
 104         if (errno == EINVAL) {
 105             crm_err("Conversion of %s failed", text);
 106             result = -1;
 107 
 108         } else if (errno == ERANGE) {
 109             crm_err("Conversion of %s was clipped: %lld", text, result);
 110 
 111         } else if (errno != 0) {
 112             crm_perror(LOG_ERR, "Conversion of %s failed", text);
 113         }
 114 
 115         if (local_end_text != NULL && local_end_text[0] != '\0') {
 116             crm_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
 117         }
 118 
 119         errno = saved_errno;
 120     }
 121     return result;
 122 }
 123 
 124 int
 125 crm_parse_int(const char *text, const char *default_text)
     /* [previous][next][first][last][top][bottom][index][help] */
 126 {
 127     int atoi_result = -1;
 128 
 129     if (text != NULL) {
 130         atoi_result = crm_int_helper(text, NULL);
 131         if (errno == 0) {
 132             return atoi_result;
 133         }
 134     }
 135 
 136     if (default_text != NULL) {
 137         atoi_result = crm_int_helper(default_text, NULL);
 138         if (errno == 0) {
 139             return atoi_result;
 140         }
 141 
 142     } else {
 143         crm_err("No default conversion value supplied");
 144     }
 145 
 146     return -1;
 147 }
 148 
 149 gboolean
 150 safe_str_neq(const char *a, const char *b)
     /* [previous][next][first][last][top][bottom][index][help] */
 151 {
 152     if (a == b) {
 153         return FALSE;
 154 
 155     } else if (a == NULL || b == NULL) {
 156         return TRUE;
 157 
 158     } else if (strcasecmp(a, b) == 0) {
 159         return FALSE;
 160     }
 161     return TRUE;
 162 }
 163 
 164 gboolean
 165 crm_is_true(const char *s)
     /* [previous][next][first][last][top][bottom][index][help] */
 166 {
 167     gboolean ret = FALSE;
 168 
 169     if (s != NULL) {
 170         crm_str_to_boolean(s, &ret);
 171     }
 172     return ret;
 173 }
 174 
 175 int
 176 crm_str_to_boolean(const char *s, int *ret)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178     if (s == NULL) {
 179         return -1;
 180 
 181     } else if (strcasecmp(s, "true") == 0
 182                || strcasecmp(s, "on") == 0
 183                || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
 184         *ret = TRUE;
 185         return 1;
 186 
 187     } else if (strcasecmp(s, "false") == 0
 188                || strcasecmp(s, "off") == 0
 189                || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
 190         *ret = FALSE;
 191         return 1;
 192     }
 193     return -1;
 194 }
 195 
 196 char *
 197 crm_strip_trailing_newline(char *str)
     /* [previous][next][first][last][top][bottom][index][help] */
 198 {
 199     int len;
 200 
 201     if (str == NULL) {
 202         return str;
 203     }
 204 
 205     for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
 206         str[len] = '\0';
 207     }
 208 
 209     return str;
 210 }
 211 
 212 gboolean
 213 crm_str_eq(const char *a, const char *b, gboolean use_case)
     /* [previous][next][first][last][top][bottom][index][help] */
 214 {
 215     if (use_case) {
 216         return g_strcmp0(a, b) == 0;
 217 
 218         /* TODO - Figure out which calls, if any, really need to be case independent */
 219     } else if (a == b) {
 220         return TRUE;
 221 
 222     } else if (a == NULL || b == NULL) {
 223         /* shouldn't be comparing NULLs */
 224         return FALSE;
 225 
 226     } else if (strcasecmp(a, b) == 0) {
 227         return TRUE;
 228     }
 229     return FALSE;
 230 }
 231 
 232 static inline const char * null2emptystr(const char *);
 233 static inline const char *
 234 null2emptystr(const char *input)
     /* [previous][next][first][last][top][bottom][index][help] */
 235 {
 236     return (input == NULL) ? "" : input;
 237 }
 238 
 239 /*!
 240  * \brief Check whether a string starts with a certain sequence
 241  *
 242  * \param[in] str    String to check
 243  * \param[in] match  Sequence to match against beginning of \p str
 244  *
 245  * \return \c TRUE if \p str begins with match, \c FALSE otherwise
 246  * \note This is equivalent to !strncmp(s, prefix, strlen(prefix))
 247  *       but is likely less efficient when prefix is a string literal
 248  *       if the compiler optimizes away the strlen() at compile time,
 249  *       and more efficient otherwise.
 250  */
 251 bool
 252 crm_starts_with(const char *str, const char *prefix)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254     const char *s = str;
 255     const char *p = prefix;
 256 
 257     if (!s || !p) {
 258         return FALSE;
 259     }
 260     while (*s && *p) {
 261         if (*s++ != *p++) {
 262             return FALSE;
 263         }
 264     }
 265     return (*p == 0);
 266 }
 267 
 268 static inline int crm_ends_with_internal(const char *, const char *, gboolean);
 269 static inline int
 270 crm_ends_with_internal(const char *s, const char *match, gboolean as_extension)
     /* [previous][next][first][last][top][bottom][index][help] */
 271 {
 272     if ((s == NULL) || (match == NULL)) {
 273         return 0;
 274     } else {
 275         size_t slen, mlen;
 276 
 277         if (match[0] != '\0'
 278             && (as_extension /* following commented out for inefficiency:
 279                 || strchr(&match[1], match[0]) == NULL */))
 280                 return !strcmp(null2emptystr(strrchr(s, match[0])), match);
 281 
 282         if ((mlen = strlen(match)) == 0)
 283             return 1;
 284         slen = strlen(s);
 285         return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
 286     }
 287 }
 288 
 289 /*!
 290  * \internal
 291  * \brief Check whether a string ends with a certain sequence
 292  *
 293  * \param[in] s      String to check
 294  * \param[in] match  Sequence to match against end of \p s
 295  *
 296  * \return \c TRUE if \p s ends (verbatim, i.e., case sensitively)
 297  *         with match (including empty string), \c FALSE otherwise
 298  *
 299  * \see crm_ends_with_ext()
 300  */
 301 gboolean
 302 crm_ends_with(const char *s, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
 303 {
 304     return crm_ends_with_internal(s, match, FALSE);
 305 }
 306 
 307 /*!
 308  * \internal
 309  * \brief Check whether a string ends with a certain "extension"
 310  *
 311  * \param[in] s      String to check
 312  * \param[in] match  Extension to match against end of \p s, that is,
 313  *                   its first character must not occur anywhere
 314  *                   in the rest of that very sequence (example: file
 315  *                   extension where the last dot is its delimiter,
 316  *                   e.g., ".html"); incorrect results may be
 317  *                   returned otherwise.
 318  *
 319  * \return \c TRUE if \p s ends (verbatim, i.e., case sensitively)
 320  *         with "extension" designated as \p match (including empty
 321  *         string), \c FALSE otherwise
 322  *
 323  * \note Main incentive to prefer this function over \c crm_ends_with
 324  *       where possible is the efficiency (at the cost of added
 325  *       restriction on \p match as stated; the complexity class
 326  *       remains the same, though: BigO(M+N) vs. BigO(M+2N)).
 327  *
 328  * \see crm_ends_with()
 329  */
 330 gboolean
 331 crm_ends_with_ext(const char *s, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
 332 {
 333     return crm_ends_with_internal(s, match, TRUE);
 334 }
 335 
 336 /*
 337  * This re-implements g_str_hash as it was prior to glib2-2.28:
 338  *
 339  *   http://git.gnome.org/browse/glib/commit/?id=354d655ba8a54b754cb5a3efb42767327775696c
 340  *
 341  * Note that the new g_str_hash is presumably a *better* hash (it's actually
 342  * a correct implementation of DJB's hash), but we need to preserve existing
 343  * behaviour, because the hash key ultimately determines the "sort" order
 344  * when iterating through GHashTables, which affects allocation of scores to
 345  * clone instances when iterating through rsc->allowed_nodes.  It (somehow)
 346  * also appears to have some minor impact on the ordering of a few
 347  * pseudo_event IDs in the transition graph.
 348  */
 349 guint
 350 g_str_hash_traditional(gconstpointer v)
     /* [previous][next][first][last][top][bottom][index][help] */
 351 {
 352     const signed char *p;
 353     guint32 h = 0;
 354 
 355     for (p = v; *p != '\0'; p++)
 356         h = (h << 5) - h + *p;
 357 
 358     return h;
 359 }
 360 
 361 guint
 362 crm_strcase_hash(gconstpointer v)
     /* [previous][next][first][last][top][bottom][index][help] */
 363 {
 364     const signed char *p;
 365     guint32 h = 0;
 366 
 367     for (p = v; *p != '\0'; p++)
 368         h = (h << 5) - h + g_ascii_tolower(*p);
 369 
 370     return h;
 371 }
 372 
 373 static void
 374 copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 375 {
 376     if (key && value && user_data) {
 377         g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
 378     }
 379 }
 380 
 381 GHashTable *
 382 crm_str_table_dup(GHashTable *old_table)
     /* [previous][next][first][last][top][bottom][index][help] */
 383 {
 384     GHashTable *new_table = NULL;
 385 
 386     if (old_table) {
 387         new_table = crm_str_table_new();
 388         g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
 389     }
 390     return new_table;
 391 }
 392 
 393 char *
 394 add_list_element(char *list, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 395 {
 396     int len = 0;
 397     int last = 0;
 398 
 399     if (value == NULL) {
 400         return list;
 401     }
 402     if (list) {
 403         last = strlen(list);
 404     }
 405     len = last + 2;             /* +1 space, +1 EOS */
 406     len += strlen(value);
 407     list = realloc_safe(list, len);
 408     sprintf(list + last, " %s", value);
 409     return list;
 410 }
 411 
 412 bool
 413 crm_compress_string(const char *data, int length, int max, char **result, unsigned int *result_len)
     /* [previous][next][first][last][top][bottom][index][help] */
 414 {
 415     int rc;
 416     char *compressed = NULL;
 417     char *uncompressed = strdup(data);
 418     struct timespec after_t;
 419     struct timespec before_t;
 420 
 421     if(max == 0) {
 422         max = (length * 1.1) + 600; /* recommended size */
 423     }
 424 
 425 #ifdef CLOCK_MONOTONIC
 426     clock_gettime(CLOCK_MONOTONIC, &before_t);
 427 #endif
 428 
 429     /* coverity[returned_null] Ignore */
 430     compressed = malloc(max);
 431 
 432     *result_len = max;
 433     rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length, CRM_BZ2_BLOCKS, 0,
 434                                   CRM_BZ2_WORK);
 435 
 436     free(uncompressed);
 437 
 438     if (rc != BZ_OK) {
 439         crm_err("Compression of %d bytes failed: %s (%d)", length, bz2_strerror(rc), rc);
 440         free(compressed);
 441         return FALSE;
 442     }
 443 
 444 #ifdef CLOCK_MONOTONIC
 445     clock_gettime(CLOCK_MONOTONIC, &after_t);
 446 
 447     crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
 448              length, *result_len, length / (*result_len),
 449              difftime (after_t.tv_sec, before_t.tv_sec) * 1000 +
 450              (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
 451 #else
 452     crm_trace("Compressed %d bytes into %d (ratio %d:1)",
 453              length, *result_len, length / (*result_len));
 454 #endif
 455 
 456     *result = compressed;
 457     return TRUE;
 458 }
 459 
 460 /*!
 461  * \brief Compare two strings alphabetically (case-insensitive)
 462  *
 463  * \param[in] a  First string to compare
 464  * \param[in] b  Second string to compare
 465  *
 466  * \return 0 if strings are equal, -1 if a < b, 1 if a > b
 467  *
 468  * \note Usable as a GCompareFunc with g_list_sort().
 469  *       NULL is considered less than non-NULL.
 470  */
 471 gint
 472 crm_alpha_sort(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 473 {
 474     if (!a && !b) {
 475         return 0;
 476     } else if (!a) {
 477         return -1;
 478     } else if (!b) {
 479         return 1;
 480     }
 481     return strcasecmp(a, b);
 482 }

/* [previous][next][first][last][top][bottom][index][help] */