summaryrefslogtreecommitdiffstats
path: root/include/crm/common/strings_internal.h
blob: a34a9f8fe424bc06db643876cc4ba29a9d52fedb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
 * Copyright 2015-2024 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.
 */

#ifndef PCMK__STRINGS_INTERNAL__H
#define PCMK__STRINGS_INTERNAL__H

#include <stdbool.h>            // bool

#include <glib.h>               // guint, GList, GHashTable

/* internal constants for generic string functions (from strings.c) */

#define PCMK__PARSE_INT_DEFAULT -1
#define PCMK__PARSE_DBL_DEFAULT -1.0

/* internal generic string functions (from strings.c) */

enum pcmk__str_flags {
    pcmk__str_none          = 0,
    pcmk__str_casei         = 1 << 0,
    pcmk__str_null_matches  = 1 << 1,
    pcmk__str_regex         = 1 << 2,
    pcmk__str_star_matches  = 1 << 3,
};

int pcmk__scan_double(const char *text, double *result,
                      const char *default_text, char **end_text);
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val,
                          guint *result);
bool pcmk__starts_with(const char *str, const char *prefix);
bool pcmk__ends_with(const char *s, const char *match);
bool pcmk__ends_with_ext(const char *s, const char *match);
char *pcmk__trim(char *str);
void pcmk__add_separated_word(GString **list, size_t init_size,
                              const char *word, const char *separator);
int pcmk__compress(const char *data, unsigned int length, unsigned int max,
                   char **result, unsigned int *result_len);

int pcmk__scan_ll(const char *text, long long *result, long long default_value);
int pcmk__scan_min_int(const char *text, int *result, int minimum);
int pcmk__scan_port(const char *text, int *port);
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end);

GHashTable *pcmk__strkey_table(GDestroyNotify key_destroy_func,
                               GDestroyNotify value_destroy_func);
GHashTable *pcmk__strikey_table(GDestroyNotify key_destroy_func,
                                GDestroyNotify value_destroy_func);
GHashTable *pcmk__str_table_dup(GHashTable *old_table);
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value);

/*!
 * \internal
 * \brief Get a string value with a default if NULL
 *
 * \param[in] s              String to return if non-NULL
 * \param[in] default_value  String (or NULL) to return if \p s is NULL
 *
 * \return \p s if \p s is non-NULL, otherwise \p default_value
 */
static inline const char *
pcmk__s(const char *s, const char *default_value)
{
    return (s == NULL)? default_value : s;
}

/*!
 * \internal
 * \brief Create a hash table with integer keys
 *
 * \param[in] value_destroy_func  Function to free a value
 *
 * \return Newly allocated hash table
 * \note It is the caller's responsibility to free the result, using
 *       g_hash_table_destroy().
 */
static inline GHashTable *
pcmk__intkey_table(GDestroyNotify value_destroy_func)
{
    return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
                                 value_destroy_func);
}

/*!
 * \internal
 * \brief Insert a value into a hash table with integer keys
 *
 * \param[in,out] hash_table  Table to insert into
 * \param[in]     key         Integer key to insert
 * \param[in]     value       Value to insert
 *
 * \return Whether the key/value was already in the table
 * \note This has the same semantics as g_hash_table_insert(). If the key
 *       already exists in the table, the old value is freed and replaced.
 */
static inline gboolean
pcmk__intkey_table_insert(GHashTable *hash_table, int key, gpointer value)
{
    return g_hash_table_insert(hash_table, GINT_TO_POINTER(key), value);
}

/*!
 * \internal
 * \brief Look up a value in a hash table with integer keys
 *
 * \param[in] hash_table  Table to check
 * \param[in] key         Integer key to look for
 *
 * \return Value in table for \key (or NULL if not found)
 */
static inline gpointer
pcmk__intkey_table_lookup(GHashTable *hash_table, int key)
{
    return g_hash_table_lookup(hash_table, GINT_TO_POINTER(key));
}

/*!
 * \internal
 * \brief Remove a key/value from a hash table with integer keys
 *
 * \param[in,out] hash_table  Table to modify
 * \param[in]     key         Integer key of entry to remove
 *
 * \return Whether \p key was found and removed from \p hash_table
 */
static inline gboolean
pcmk__intkey_table_remove(GHashTable *hash_table, int key)
{
    return g_hash_table_remove(hash_table, GINT_TO_POINTER(key));
}

gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags);

bool pcmk__strcase_any_of(const char *s, ...) G_GNUC_NULL_TERMINATED;
bool pcmk__str_any_of(const char *s, ...) G_GNUC_NULL_TERMINATED;
bool pcmk__char_in_any_str(int ch, ...) G_GNUC_NULL_TERMINATED;

int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags);
int pcmk__numeric_strcasecmp(const char *s1, const char *s2);

char *pcmk__str_copy_as(const char *file, const char *function, uint32_t line,
                        const char *str);

/*!
 * \internal
 * \brief Copy a string, asserting on failure
 *
 * \param[in] str  String to copy (can be \c NULL)
 *
 * \return Newly allocated copy of \p str, or \c NULL if \p str is \c NULL
 *
 * \note The caller is responsible for freeing the return value using \c free().
 */
#define pcmk__str_copy(str) pcmk__str_copy_as(__FILE__, __func__, __LINE__, str)

void pcmk__str_update(char **str, const char *value);

void pcmk__g_strcat(GString *buffer, ...) G_GNUC_NULL_TERMINATED;

static inline bool
pcmk__str_eq(const char *s1, const char *s2, uint32_t flags)
{
    return pcmk__strcmp(s1, s2, flags) == 0;
}

// Like pcmk__add_separated_word() but using a space as separator
static inline void
pcmk__add_word(GString **list, size_t init_size, const char *word)
{
    return pcmk__add_separated_word(list, init_size, word, " ");
}

/* Correctly displaying singular or plural is complicated; consider "1 node has"
 * vs. "2 nodes have". A flexible solution is to pluralize entire strings, e.g.
 *
 * if (a == 1) {
 *     crm_info("singular message"):
 * } else {
 *     crm_info("plural message");
 * }
 *
 * though even that's not sufficient for all languages besides English (if we
 * ever desire to do translations of output and log messages). But the following
 * convenience macros are "good enough" and more concise for many cases.
 */

/* Example:
 * crm_info("Found %d %s", nentries,
 *          pcmk__plural_alt(nentries, "entry", "entries"));
 */
#define pcmk__plural_alt(i, s1, s2) (((i) == 1)? (s1) : (s2))

// Example: crm_info("Found %d node%s", nnodes, pcmk__plural_s(nnodes));
#define pcmk__plural_s(i) pcmk__plural_alt(i, "", "s")

static inline int
pcmk__str_empty(const char *s)
{
    return (s == NULL) || (s[0] == '\0');
}

static inline char *
pcmk__itoa(int an_int)
{
    return crm_strdup_printf("%d", an_int);
}

static inline char *
pcmk__ftoa(double a_float)
{
    return crm_strdup_printf("%f", a_float);
}

static inline char *
pcmk__ttoa(time_t epoch_time)
{
    return crm_strdup_printf("%lld", (long long) epoch_time);
}

// note this returns const not allocated
static inline const char *
pcmk__btoa(bool condition)
{
    return condition? PCMK_VALUE_TRUE : PCMK_VALUE_FALSE;
}

#endif /* PCMK__STRINGS_INTERNAL__H */