1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2016 Red Hat, Inc. (www.redhat.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
18 #include "evolution-data-server-config.h"
19
20 #include <stdio.h>
21 #include <string.h>
22
23 #include "camel-string-utils.h"
24
25 #include "camel-name-value-array.h"
26
27 G_DEFINE_BOXED_TYPE (CamelNameValueArray,
28 camel_name_value_array,
29 camel_name_value_array_copy,
30 camel_name_value_array_free)
31
32 typedef struct _CamelNameValuePair {
33 gchar *name;
34 gchar *value;
35 } CamelNameValuePair;
36
37 static void
free_name_value_content(gpointer ptr)38 free_name_value_content (gpointer ptr)
39 {
40 CamelNameValuePair *pair = ptr;
41
42 if (pair) {
43 g_free (pair->name);
44 g_free (pair->value);
45
46 pair->name = NULL;
47 pair->value = NULL;
48 }
49 }
50
51 /**
52 * camel_name_value_array_new:
53 *
54 * Creates a new #CamelNameValueArray. The returned pointer should be freed
55 * with camel_name_value_array_free() when no longer needed.
56 *
57 * Returns: (transfer full): A new #CamelNameValueArray.
58 *
59 * See: camel_name_value_array_new_sized, camel_name_value_array_copy
60 *
61 * Since: 3.24
62 **/
63 CamelNameValueArray *
camel_name_value_array_new(void)64 camel_name_value_array_new (void)
65 {
66 GArray *arr;
67
68 arr = g_array_new (FALSE, FALSE, sizeof (CamelNameValuePair));
69 g_array_set_clear_func (arr, free_name_value_content);
70
71 return (CamelNameValueArray *) arr;
72 }
73
74 /**
75 * camel_name_value_array_new_sized:
76 * @reserve_size: an array size to reserve
77 *
78 * Creates a new #CamelNameValueArray, which has reserved @reserve_size
79 * elements. This value doesn't influence the camel_name_value_array_get_length(),
80 * which returns zero on the array returned from this function. The returned
81 * pointer should be freed with camel_name_value_array_free() when no longer needed.
82 *
83 * Returns: (transfer full): A new #CamelNameValueArray.
84 *
85 * See: camel_name_value_array_new, camel_name_value_array_copy
86 *
87 * Since: 3.24
88 **/
89 CamelNameValueArray *
camel_name_value_array_new_sized(guint reserve_size)90 camel_name_value_array_new_sized (guint reserve_size)
91 {
92 GArray *arr;
93
94 arr = g_array_sized_new (FALSE, FALSE, sizeof (CamelNameValuePair), reserve_size);
95 g_array_set_clear_func (arr, free_name_value_content);
96
97 return (CamelNameValueArray *) arr;
98 }
99
100 /**
101 * camel_name_value_array_copy:
102 * @array: (nullable): a #CamelNameValueArray
103 *
104 * Creates a new copy of the @array. The returned pointer should be freed
105 * with camel_name_value_array_free() when no longer needed.
106 *
107 * Returns: (transfer full): A new copy of the @array.
108 *
109 * See: camel_name_value_array_new, camel_name_value_array_new_sized
110 *
111 * Since: 3.24
112 **/
113 CamelNameValueArray *
camel_name_value_array_copy(const CamelNameValueArray * array)114 camel_name_value_array_copy (const CamelNameValueArray *array)
115 {
116 CamelNameValueArray *copy;
117 guint ii, len;
118
119 if (!array)
120 return NULL;
121
122 len = camel_name_value_array_get_length (array);
123 copy = camel_name_value_array_new_sized (len);
124
125 for (ii = 0; ii < len; ii++) {
126 const gchar *name = NULL, *value = NULL;
127
128 if (camel_name_value_array_get (array, ii, &name, &value))
129 camel_name_value_array_append (copy, name, value);
130 }
131
132 return copy;
133 }
134
135 /**
136 * camel_name_value_array_free:
137 * @array: (nullable): a #CamelNameValueArray, or %NULL
138 *
139 * Frees the @array, previously allocated by camel_name_value_array_new(),
140 * camel_name_value_array_new_sized() or camel_name_value_array_copy().
141 * If the @array is %NULL, then does nothing.
142 *
143 * Since: 3.24
144 **/
145 void
camel_name_value_array_free(CamelNameValueArray * array)146 camel_name_value_array_free (CamelNameValueArray *array)
147 {
148 if (array)
149 g_array_free ((GArray *) array, TRUE);
150 }
151
152 /**
153 * camel_name_value_array_get_length:
154 * @array: (nullable): a #CamelNameValueArray
155 *
156 * Returns: Length of the @array, aka how many elements are stored in the @array.
157 *
158 * Since: 3.24
159 **/
160 guint
camel_name_value_array_get_length(const CamelNameValueArray * array)161 camel_name_value_array_get_length (const CamelNameValueArray *array)
162 {
163 GArray *arr = (GArray *) array;
164
165 if (!array)
166 return 0;
167
168 return arr->len;
169 }
170
171 /**
172 * camel_name_value_array_get:
173 * @array: a #CamelNameValueArray
174 * @index: an index
175 * @out_name: (out) (nullable): A place to store the name of the element, or %NULL
176 * @out_value: (out) (nullable): A place to store the value of the element, or %NULL
177 *
178 * Returns the name and the value of the element at index @index. Either
179 * of the @out_name and @out_value can be %NULL, to not return that part.
180 *
181 * Returns: %TRUE on success, %FALSE otherwise.
182 *
183 * See: camel_name_value_array_get_name, camel_name_value_array_get_value, camel_name_value_array_get_named
184 *
185 * Since: 3.24
186 **/
187 gboolean
camel_name_value_array_get(const CamelNameValueArray * array,guint index,const gchar ** out_name,const gchar ** out_value)188 camel_name_value_array_get (const CamelNameValueArray *array,
189 guint index,
190 const gchar **out_name,
191 const gchar **out_value)
192 {
193 GArray *arr = (GArray *) array;
194 CamelNameValuePair *pair;
195
196 g_return_val_if_fail (array != NULL, FALSE);
197
198 if (index >= camel_name_value_array_get_length (array))
199 return FALSE;
200
201 pair = &g_array_index (arr, CamelNameValuePair, index);
202
203 if (out_name)
204 *out_name = pair->name;
205 if (out_value)
206 *out_value= pair->value;
207
208 return TRUE;
209 }
210
211 static guint
camel_name_value_array_find_named(const CamelNameValueArray * array,CamelCompareType compare_type,const gchar * name)212 camel_name_value_array_find_named (const CamelNameValueArray *array,
213 CamelCompareType compare_type,
214 const gchar *name)
215 {
216 GArray *arr = (GArray *) array;
217 gboolean case_sensitive;
218 gint ii;
219
220 g_return_val_if_fail (array != NULL, (guint) -1);
221 g_return_val_if_fail (name != NULL, (guint) -1);
222
223 case_sensitive = compare_type == CAMEL_COMPARE_CASE_SENSITIVE;
224
225 for (ii = 0; ii < arr->len; ii++) {
226 CamelNameValuePair *pair = &g_array_index (arr, CamelNameValuePair, ii);
227
228 if ((case_sensitive && g_strcmp0 (name, pair->name) == 0) ||
229 (!case_sensitive && pair->name && camel_strcase_equal (name, pair->name))) {
230 return ii;
231 }
232 }
233
234 return (guint) -1;
235 }
236
237 /**
238 * camel_name_value_array_get_named:
239 * @array: a #CamelNameValueArray
240 * @compare_type: a compare type, one of #CamelCompareType
241 * @name: a name
242 *
243 * Returns the value of the first element named @name, or %NULL when there
244 * is no element of such @name in the @array. The @compare_type determines
245 * how to compare the names.
246 *
247 * Returns: (transfer none) (nullable): Value of the first element named @name, or %NULL.
248 *
249 * See: camel_name_value_array_get, camel_name_value_array_get_name
250 *
251 * Since: 3.24
252 **/
253 const gchar *
camel_name_value_array_get_named(const CamelNameValueArray * array,CamelCompareType compare_type,const gchar * name)254 camel_name_value_array_get_named (const CamelNameValueArray *array,
255 CamelCompareType compare_type,
256 const gchar *name)
257 {
258 guint index;
259
260 g_return_val_if_fail (array != NULL, NULL);
261 g_return_val_if_fail (name != NULL, NULL);
262
263 index = camel_name_value_array_find_named (array, compare_type, name);
264 if (index == (guint) -1)
265 return NULL;
266
267 return camel_name_value_array_get_value (array, index);
268 }
269
270 /**
271 * camel_name_value_array_get_name:
272 * @array: a #CamelNameValueArray
273 * @index: an index
274 *
275 * Returns the name of the element at index @index.
276 *
277 * Returns: (transfer none) (nullable): Name of the element at the given @index,
278 * or %NULL on error.
279 *
280 * See: camel_name_value_array_get, camel_name_value_array_get_value
281 *
282 * Since: 3.24
283 **/
284 const gchar *
camel_name_value_array_get_name(const CamelNameValueArray * array,guint index)285 camel_name_value_array_get_name (const CamelNameValueArray *array,
286 guint index)
287 {
288 const gchar *name = NULL;
289
290 g_return_val_if_fail (array != NULL, NULL);
291
292 if (!camel_name_value_array_get (array, index, &name, NULL))
293 return NULL;
294
295 return name;
296 }
297
298 /**
299 * camel_name_value_array_get_value:
300 * @array: a #CamelNameValueArray
301 * @index: an index
302 *
303 * Returns the value of the element at index @index.
304 *
305 * Returns: (transfer none) (nullable): Value of the element at the given @index,
306 * or %NULL on error.
307 *
308 * See: camel_name_value_array_get, camel_name_value_array_get_name
309 *
310 * Since: 3.24
311 **/
312 const gchar *
camel_name_value_array_get_value(const CamelNameValueArray * array,guint index)313 camel_name_value_array_get_value (const CamelNameValueArray *array,
314 guint index)
315 {
316 const gchar *value = NULL;
317
318 g_return_val_if_fail (array != NULL, NULL);
319
320 if (!camel_name_value_array_get (array, index, NULL, &value))
321 return NULL;
322
323 return value;
324 }
325
326 /**
327 * camel_name_value_array_append:
328 * @array: a #CamelNameValueArray
329 * @name: a name
330 * @value: a value
331 *
332 * Appends a new element of the name @name and the value @value
333 * at the end of @array.
334 *
335 * See: camel_name_value_array_set_named
336 *
337 * Since: 3.24
338 **/
339 void
camel_name_value_array_append(CamelNameValueArray * array,const gchar * name,const gchar * value)340 camel_name_value_array_append (CamelNameValueArray *array,
341 const gchar *name,
342 const gchar *value)
343 {
344 GArray *arr = (GArray *) array;
345 CamelNameValuePair pair;
346
347 g_return_if_fail (array != NULL);
348 g_return_if_fail (name != NULL);
349 g_return_if_fail (value != NULL);
350
351 pair.name = g_strdup (name);
352 pair.value = g_strdup (value);
353
354 g_array_append_val (arr, pair);
355 }
356
357 static gboolean
camel_name_value_array_set_internal(CamelNameValueArray * array,guint index,const gchar * name,const gchar * value)358 camel_name_value_array_set_internal (CamelNameValueArray *array,
359 guint index,
360 const gchar *name,
361 const gchar *value)
362 {
363 GArray *arr = (GArray *) array;
364 CamelNameValuePair *pair;
365 gboolean changed = FALSE;
366
367 g_return_val_if_fail (array != NULL, FALSE);
368 g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
369
370 pair = &g_array_index (arr, CamelNameValuePair, index);
371
372 if (name && g_strcmp0 (pair->name, name) != 0) {
373 g_free (pair->name);
374 pair->name = g_strdup (name);
375 changed = TRUE;
376 }
377
378 if (value && g_strcmp0 (pair->value, value) != 0) {
379 g_free (pair->value);
380 pair->value = g_strdup (value);
381 changed = TRUE;
382 }
383
384 return changed;
385 }
386
387 /**
388 * camel_name_value_array_set:
389 * @array: a #CamelNameValueArray
390 * @index: an index
391 * @name: a name
392 * @value: a value
393 *
394 * Sets both the @name and the @value of the element at index @index.
395 *
396 * Returns: Whether the @array changed.
397 *
398 * See: camel_name_value_array_append, camel_name_value_array_set_name, camel_name_value_array_set_value
399 *
400 * Since: 3.24
401 **/
402 gboolean
camel_name_value_array_set(CamelNameValueArray * array,guint index,const gchar * name,const gchar * value)403 camel_name_value_array_set (CamelNameValueArray *array,
404 guint index,
405 const gchar *name,
406 const gchar *value)
407 {
408 g_return_val_if_fail (array != NULL, FALSE);
409 g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
410 g_return_val_if_fail (name != NULL, FALSE);
411 g_return_val_if_fail (value != NULL, FALSE);
412
413 return camel_name_value_array_set_internal (array, index, name, value);
414 }
415
416 /**
417 * camel_name_value_array_set_name:
418 * @array: a #CamelNameValueArray
419 * @index: an index
420 * @name: a name
421 *
422 * Sets the @name of the element at index @index.
423 *
424 * Returns: Whether the @array changed.
425 *
426 * See: camel_name_value_array_set, camel_name_value_array_set_value
427 *
428 * Since: 3.24
429 **/
430 gboolean
camel_name_value_array_set_name(CamelNameValueArray * array,guint index,const gchar * name)431 camel_name_value_array_set_name (CamelNameValueArray *array,
432 guint index,
433 const gchar *name)
434 {
435 g_return_val_if_fail (array != NULL, FALSE);
436 g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
437 g_return_val_if_fail (name != NULL, FALSE);
438
439 return camel_name_value_array_set_internal (array, index, name, NULL);
440 }
441
442 /**
443 * camel_name_value_array_set_value:
444 * @array: a #CamelNameValueArray
445 * @index: an index
446 * @value: a value
447 *
448 * Sets the @value of the element at index @index.
449 *
450 * Returns: Whether the @array changed.
451 *
452 * See: camel_name_value_array_set, camel_name_value_array_set_name
453 *
454 * Since: 3.24
455 **/
456 gboolean
camel_name_value_array_set_value(CamelNameValueArray * array,guint index,const gchar * value)457 camel_name_value_array_set_value (CamelNameValueArray *array,
458 guint index,
459 const gchar *value)
460 {
461 g_return_val_if_fail (array != NULL, FALSE);
462 g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
463 g_return_val_if_fail (value != NULL, FALSE);
464
465 return camel_name_value_array_set_internal (array, index, NULL, value);
466 }
467
468 /**
469 * camel_name_value_array_set_named:
470 * @array: a #CamelNameValueArray
471 * @compare_type: a compare type, one of #CamelCompareType
472 * @name: a name
473 * @value: a value
474 *
475 * Finds an element named @name and sets its value to @value, or appends
476 * a new element, in case no such named element exists in the @array yet.
477 * In case there are more elements named with @name only the first
478 * occurrence is changed. The @compare_type determines how to compare
479 * the names.
480 *
481 * Returns: Whether the @array changed.
482 *
483 * See: camel_name_value_array_append, camel_name_value_array_set
484 *
485 * Since: 3.24
486 **/
487 gboolean
camel_name_value_array_set_named(CamelNameValueArray * array,CamelCompareType compare_type,const gchar * name,const gchar * value)488 camel_name_value_array_set_named (CamelNameValueArray *array,
489 CamelCompareType compare_type,
490 const gchar *name,
491 const gchar *value)
492 {
493 gboolean changed = FALSE;
494 guint index;
495
496 g_return_val_if_fail (array != NULL, FALSE);
497 g_return_val_if_fail (name != NULL, FALSE);
498 g_return_val_if_fail (value != NULL, FALSE);
499
500 index = camel_name_value_array_find_named (array, compare_type, name);
501 if (index == (guint) -1) {
502 camel_name_value_array_append (array, name, value);
503 changed = TRUE;
504 } else {
505 changed = camel_name_value_array_set_value (array, index, value);
506 }
507
508 return changed;
509 }
510
511 /**
512 * camel_name_value_array_remove:
513 * @array: a #CamelNameValueArray
514 * @index: an index to remove
515 *
516 * Removes element at index @index.
517 *
518 * Returns: Whether the element was removed.
519 *
520 * Since: 3.24
521 **/
522 gboolean
camel_name_value_array_remove(CamelNameValueArray * array,guint index)523 camel_name_value_array_remove (CamelNameValueArray *array,
524 guint index)
525 {
526 g_return_val_if_fail (array != NULL, FALSE);
527 g_return_val_if_fail (index < camel_name_value_array_get_length (array), FALSE);
528
529 g_array_remove_index ((GArray *) array, index);
530
531 return TRUE;
532 }
533
534 /**
535 * camel_name_value_array_remove_named:
536 * @array: a #CamelNameValueArray
537 * @compare_type: a compare type, one of #CamelCompareType
538 * @name: a name to remove
539 * @all_occurrences: whether to remove all occurrences of the @name
540 *
541 * Removes elements of the @array with the given @name.
542 * The @compare_type determines hot to compare the names.
543 * If the @all_occurrences is set to %TRUE, then every elements with the @name
544 * are removed, otherwise only the first occurrence is removed.
545 *
546 * Returns: How many elements had been removed.
547 *
548 * Since: 3.24
549 **/
550 guint
camel_name_value_array_remove_named(CamelNameValueArray * array,CamelCompareType compare_type,const gchar * name,gboolean all_occurrences)551 camel_name_value_array_remove_named (CamelNameValueArray *array,
552 CamelCompareType compare_type,
553 const gchar *name,
554 gboolean all_occurrences)
555 {
556 guint index, removed = 0;
557
558 g_return_val_if_fail (array != NULL, 0);
559 g_return_val_if_fail (name != NULL, 0);
560
561 while (index = camel_name_value_array_find_named (array, compare_type, name), index != (guint) -1) {
562 if (!camel_name_value_array_remove (array, index))
563 break;
564
565 removed++;
566
567 if (!all_occurrences)
568 break;
569 }
570
571 return removed;
572 }
573
574 /**
575 * camel_name_value_array_clear:
576 * @array: a #CamelNameValueArray
577 *
578 * Removes all elements of the @array.
579 *
580 * Since: 3.24
581 **/
582 void
camel_name_value_array_clear(CamelNameValueArray * array)583 camel_name_value_array_clear (CamelNameValueArray *array)
584 {
585 GArray *arr = (GArray *) array;
586
587 g_return_if_fail (array != NULL);
588
589 g_array_remove_range (arr, 0, arr->len);
590 }
591
592 /**
593 * camel_name_value_array_equal:
594 * @array_a: (nullable): the first #CamelNameValueArray
595 * @array_b: (nullable): the second #CamelNameValueArray
596 * @compare_type: a compare type, one of #CamelCompareType
597 *
598 * Compares content of the two #CamelNameValueArray and returns whether
599 * they equal. Note this is an expensive operation for large arrays.
600 *
601 * Returns: Whether the two #CamelNameValueArray have the same content.
602 *
603 * Since: 3.24
604 **/
605 gboolean
camel_name_value_array_equal(const CamelNameValueArray * array_a,const CamelNameValueArray * array_b,CamelCompareType compare_type)606 camel_name_value_array_equal (const CamelNameValueArray *array_a,
607 const CamelNameValueArray *array_b,
608 CamelCompareType compare_type)
609 {
610 guint ii, len;
611
612 if (array_a == array_b)
613 return TRUE;
614
615 if (!array_a || !array_b)
616 return camel_name_value_array_get_length (array_a) == camel_name_value_array_get_length (array_b);
617
618 len = camel_name_value_array_get_length (array_a);
619 if (len != camel_name_value_array_get_length (array_b))
620 return FALSE;
621
622 for (ii = 0; ii < len; ii++) {
623 const gchar *value1, *value2;
624
625 value1 = camel_name_value_array_get_value (array_a, ii);
626 value2 = camel_name_value_array_get_named (array_b, compare_type,
627 camel_name_value_array_get_name (array_a, ii));
628
629 if (g_strcmp0 (value1, value2) != 0)
630 return FALSE;
631 }
632
633 return TRUE;
634 }
635