1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Jeffrey Stedfast <fejj@ximian.com>
18  */
19 
20 #include "evolution-data-server-config.h"
21 
22 #include <string.h>
23 
24 #include "camel-string-utils.h"
25 
26 gint
camel_strcase_equal(gconstpointer a,gconstpointer b)27 camel_strcase_equal (gconstpointer a,
28                      gconstpointer b)
29 {
30 	return (g_ascii_strcasecmp ((const gchar *) a, (const gchar *) b) == 0);
31 }
32 
33 guint
camel_strcase_hash(gconstpointer v)34 camel_strcase_hash (gconstpointer v)
35 {
36 	const gchar *p = (gchar *) v;
37 	guint h = 0, g;
38 
39 	for (; *p != '\0'; p++) {
40 		h = (h << 4) + g_ascii_toupper (*p);
41 		if ((g = h & 0xf0000000)) {
42 			h = h ^ (g >> 24);
43 			h = h ^ g;
44 		}
45 	}
46 
47 	return h;
48 }
49 
50 gchar *
camel_strstrcase(const gchar * haystack,const gchar * needle)51 camel_strstrcase (const gchar *haystack,
52                   const gchar *needle)
53 {
54 	/* find the needle in the haystack neglecting case */
55 	const gchar *ptr;
56 	guint len;
57 
58 	g_return_val_if_fail (haystack != NULL, NULL);
59 	g_return_val_if_fail (needle != NULL, NULL);
60 
61 	len = strlen (needle);
62 	if (len > strlen (haystack))
63 		return NULL;
64 
65 	if (len == 0)
66 		return (gchar *) haystack;
67 
68 	for (ptr = haystack; *(ptr + len - 1) != '\0'; ptr++)
69 		if (!g_ascii_strncasecmp (ptr, needle, len))
70 			return (gchar *) ptr;
71 
72 	return NULL;
73 }
74 
75 const gchar *
camel_strdown(gchar * str)76 camel_strdown (gchar *str)
77 {
78 	register gchar *s = str;
79 
80 	while (*s) {
81 		if (*s >= 'A' && *s <= 'Z')
82 			*s += 0x20;
83 		s++;
84 	}
85 
86 	return str;
87 }
88 
89 /* working stuff for pstrings */
90 static GMutex string_pool_lock;
91 static GHashTable *string_pool = NULL;
92 
93 typedef struct _StringPoolNode StringPoolNode;
94 
95 struct _StringPoolNode {
96 	gchar *string;
97 	gulong ref_count;
98 };
99 
100 static StringPoolNode *
string_pool_node_new(gchar * string)101 string_pool_node_new (gchar *string)
102 {
103 	StringPoolNode *node;
104 
105 	node = g_slice_new (StringPoolNode);
106 	node->string = string;  /* takes ownership */
107 	node->ref_count = 1;
108 
109 	return node;
110 }
111 
112 static void
string_pool_node_free(StringPoolNode * node)113 string_pool_node_free (StringPoolNode *node)
114 {
115 	g_free (node->string);
116 
117 	g_slice_free (StringPoolNode, node);
118 }
119 
120 static guint
string_pool_node_hash(const StringPoolNode * node)121 string_pool_node_hash (const StringPoolNode *node)
122 {
123 	return g_str_hash (node->string);
124 }
125 
126 static gboolean
string_pool_node_equal(const StringPoolNode * node_a,const StringPoolNode * node_b)127 string_pool_node_equal (const StringPoolNode *node_a,
128                         const StringPoolNode *node_b)
129 {
130 	return g_str_equal (node_a->string, node_b->string);
131 }
132 
133 static void
string_pool_init(void)134 string_pool_init (void)
135 {
136 	if (G_UNLIKELY (string_pool == NULL))
137 		string_pool = g_hash_table_new_full (
138 			(GHashFunc) string_pool_node_hash,
139 			(GEqualFunc) string_pool_node_equal,
140 			(GDestroyNotify) string_pool_node_free,
141 			(GDestroyNotify) NULL);
142 }
143 
144 /**
145  * camel_pstring_add:
146  * @string: string to add to the string pool
147  * @own: whether the string pool will own the memory pointed to by
148  *       @string, if @string is not yet in the pool
149  *
150  * Add @string to the pool.
151  *
152  * The %NULL and empty strings are special cased to constant values.
153  *
154  * Unreference the returned string with camel_pstring_free().
155  *
156  * Returns: a canonicalized copy of @string
157  **/
158 const gchar *
camel_pstring_add(gchar * string,gboolean own)159 camel_pstring_add (gchar *string,
160                    gboolean own)
161 {
162 	StringPoolNode static_node = { string, };
163 	StringPoolNode *node;
164 	const gchar *interned;
165 
166 	if (string == NULL)
167 		return NULL;
168 
169 	if (*string == '\0') {
170 		if (own)
171 			g_free (string);
172 		return "";
173 	}
174 
175 	g_mutex_lock (&string_pool_lock);
176 
177 	string_pool_init ();
178 
179 	node = g_hash_table_lookup (string_pool, &static_node);
180 
181 	if (node != NULL) {
182 		node->ref_count++;
183 		if (own)
184 			g_free (string);
185 	} else {
186 		if (!own)
187 			string = g_strdup (string);
188 		node = string_pool_node_new (string);
189 		g_hash_table_add (string_pool, node);
190 	}
191 
192 	interned = node->string;
193 
194 	g_mutex_unlock (&string_pool_lock);
195 
196 	return interned;
197 }
198 
199 /**
200  * camel_pstring_peek:
201  * @string: string to fetch from the string pool
202  *
203  * Returns the canonicalized copy of @string without increasing its
204  * reference count in the string pool.  If necessary, @string is first
205  * added to the string pool.
206  *
207  * The %NULL and empty strings are special cased to constant values.
208  *
209  * Returns: a canonicalized copy of @string
210  *
211  * Since: 2.24
212  **/
213 const gchar *
camel_pstring_peek(const gchar * string)214 camel_pstring_peek (const gchar *string)
215 {
216 	StringPoolNode static_node = { (gchar *) string, };
217 	StringPoolNode *node;
218 	const gchar *interned;
219 
220 	if (string == NULL)
221 		return NULL;
222 
223 	if (*string == '\0')
224 		return "";
225 
226 	g_mutex_lock (&string_pool_lock);
227 
228 	string_pool_init ();
229 
230 	node = g_hash_table_lookup (string_pool, &static_node);
231 
232 	if (node == NULL) {
233 		node = string_pool_node_new (g_strdup (string));
234 		g_hash_table_add (string_pool, node);
235 	}
236 
237 	interned = node->string;
238 
239 	g_mutex_unlock (&string_pool_lock);
240 
241 	return interned;
242 }
243 
244 /**
245  * camel_pstring_contains:
246  * @string: string to look up in the string pool
247  *
248  * Returns whether the @string exists in the string pool.
249  *
250  * The %NULL and empty strings are special cased to constant values.
251  *
252  * Returns: Whether the @string exists in the string pool
253  *
254  * Since: 3.22
255  **/
256 gboolean
camel_pstring_contains(const gchar * string)257 camel_pstring_contains (const gchar *string)
258 {
259 	StringPoolNode static_node = { (gchar *) string, };
260 	gboolean contains;
261 
262 	if (string == NULL)
263 		return FALSE;
264 
265 	if (*string == '\0')
266 		return FALSE;
267 
268 	g_mutex_lock (&string_pool_lock);
269 
270 	string_pool_init ();
271 
272 	contains = g_hash_table_contains (string_pool, &static_node);
273 
274 	g_mutex_unlock (&string_pool_lock);
275 
276 	return contains;
277 }
278 
279 /**
280  * camel_pstring_strdup:
281  * @string: string to copy
282  *
283  * Create a new pooled string entry for @strings.  A pooled string
284  * is a table where common strings are canonicalized.  They are also
285  * reference counted and freed when no longer referenced.
286  *
287  * The %NULL and empty strings are special cased to constant values.
288  *
289  * Unreference the returned string with camel_pstring_free().
290  *
291  * Returns: a canonicalized copy of @string
292  **/
293 const gchar *
camel_pstring_strdup(const gchar * string)294 camel_pstring_strdup (const gchar *string)
295 {
296 	return camel_pstring_add ((gchar *) string, FALSE);
297 }
298 
299 /**
300  * camel_pstring_free:
301  * @string: string to free
302  *
303  * Unreferences a pooled string.  If the string's reference count drops to
304  * zero it will be deallocated.  %NULL and the empty string are special cased.
305  **/
306 void
camel_pstring_free(const gchar * string)307 camel_pstring_free (const gchar *string)
308 {
309 	StringPoolNode static_node = { (gchar *) string, };
310 	StringPoolNode *node;
311 
312 	if (string_pool == NULL)
313 		return;
314 
315 	if (string == NULL || *string == '\0')
316 		return;
317 
318 	g_mutex_lock (&string_pool_lock);
319 
320 	node = g_hash_table_lookup (string_pool, &static_node);
321 
322 	if (node == NULL) {
323 		g_warning ("%s: String not in pool: %s", G_STRFUNC, string);
324 	} else if (node->string != string) {
325 		g_warning ("%s: String is not ours: %s", G_STRFUNC, string);
326 	} else if (node->ref_count == 0) {
327 		g_warning ("%s: Orphaned pool node: %s", G_STRFUNC, string);
328 	} else {
329 		node->ref_count--;
330 		if (node->ref_count == 0)
331 			g_hash_table_remove (string_pool, node);
332 	}
333 
334 	g_mutex_unlock (&string_pool_lock);
335 }
336 
337 /**
338  * camel_pstring_dump_stat:
339  *
340  * Dumps to stdout memory statistic about the string pool.
341  *
342  * Since: 3.6
343  **/
344 void
camel_pstring_dump_stat(void)345 camel_pstring_dump_stat (void)
346 {
347 	g_mutex_lock (&string_pool_lock);
348 
349 	g_print ("   String Pool Statistics: ");
350 
351 	if (string_pool == NULL) {
352 		g_print ("Not used yet\n");
353 	} else {
354 		GHashTableIter iter;
355 		gchar *format_size;
356 		guint64 bytes = 0;
357 		gpointer key;
358 
359 		g_hash_table_iter_init (&iter, string_pool);
360 
361 		while (g_hash_table_iter_next (&iter, &key, NULL))
362 			bytes += strlen (((StringPoolNode *) key)->string);
363 
364 		format_size = g_format_size_full (
365 			bytes, G_FORMAT_SIZE_LONG_FORMAT);
366 
367 		g_print (
368 			"Holds %d strings totaling %s\n",
369 			g_hash_table_size (string_pool),
370 			format_size);
371 
372 		g_free (format_size);
373 	}
374 
375 	g_mutex_unlock (&string_pool_lock);
376 }
377 
378 /**
379  * camel_string_is_all_ascii:
380  * @str: (nullable): a string to check, or %NULL
381  *
382  * Returns: %TRUE, when the @str is %NULL, an empty string or when
383  *    it contains only ASCII letters.
384  *
385  * Since: 3.42.1
386  **/
387 gboolean
camel_string_is_all_ascii(const gchar * str)388 camel_string_is_all_ascii (const gchar *str)
389 {
390 	gint ii;
391 
392 	if (!str || !*str)
393 		return TRUE;
394 
395 	for (ii = 0; str[ii]; ii++) {
396 		if (str[ii] < 0)
397 			break;
398 	}
399 
400 	return str[ii] == '\0';
401 }
402