This source file includes following definitions.
- crm_itoa_stack
- scan_ll
- crm_parse_ll
- crm_parse_int
- pcmk__scan_double
- pcmk__guint_from_hash
- crm_get_msec
- crm_is_true
- crm_str_to_boolean
- crm_strip_trailing_newline
- pcmk__starts_with
- ends_with
- pcmk__ends_with
- pcmk__ends_with_ext
- g_str_hash_traditional
- crm_strcase_equal
- crm_strcase_hash
- copy_str_table_entry
- crm_str_table_dup
- pcmk__add_separated_word
- pcmk__compress
- crm_strdup_printf
- pcmk__parse_ll_range
- pcmk__str_in_list
- str_any_of
- pcmk__strcase_any_of
- pcmk__str_any_of
- pcmk__char_in_any_str
- pcmk_numeric_strcasecmp
- pcmk__strcmp
- safe_str_neq
- crm_str_eq
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #ifndef _GNU_SOURCE
13 # define _GNU_SOURCE
14 #endif
15
16 #include <regex.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <float.h>
22 #include <limits.h>
23 #include <math.h>
24 #include <bzlib.h>
25 #include <sys/types.h>
26
27 char *
28 crm_itoa_stack(int an_int, char *buffer, size_t len)
29 {
30 if (buffer != NULL) {
31 snprintf(buffer, len, "%d", an_int);
32 }
33
34 return buffer;
35 }
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 static int
51 scan_ll(const char *text, long long *result, char **end_text)
52 {
53 long long local_result = PCMK__PARSE_INT_DEFAULT;
54 char *local_end_text = NULL;
55 int rc = pcmk_rc_ok;
56
57 errno = 0;
58 if (text != NULL) {
59 #ifdef ANSI_ONLY
60 local_result = (long long) strtol(text, &local_end_text, 10);
61 #else
62 local_result = strtoll(text, &local_end_text, 10);
63 #endif
64 if (errno == ERANGE) {
65 rc = EOVERFLOW;
66 crm_warn("Integer parsed from %s was clipped to %lld",
67 text, local_result);
68
69 } else if (errno != 0) {
70 rc = errno;
71 local_result = PCMK__PARSE_INT_DEFAULT;
72 crm_warn("Could not parse integer from %s (using %d instead): %s",
73 text, PCMK__PARSE_INT_DEFAULT, pcmk_rc_str(rc));
74
75 } else if (local_end_text == text) {
76 rc = EINVAL;
77 local_result = PCMK__PARSE_INT_DEFAULT;
78 crm_warn("Could not parse integer from %s (using %d instead): "
79 "No digits found", text, PCMK__PARSE_INT_DEFAULT);
80 }
81
82 if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
83 crm_warn("Characters left over after parsing '%s': '%s'",
84 text, local_end_text);
85 }
86 errno = rc;
87 }
88 if (end_text != NULL) {
89 *end_text = local_end_text;
90 }
91 if (result != NULL) {
92 *result = local_result;
93 }
94 return rc;
95 }
96
97
98
99
100
101
102
103
104
105
106 long long
107 crm_parse_ll(const char *text, const char *default_text)
108 {
109 long long result;
110
111 if (text == NULL) {
112 text = default_text;
113 if (text == NULL) {
114 crm_err("No default conversion value supplied");
115 errno = EINVAL;
116 return PCMK__PARSE_INT_DEFAULT;
117 }
118 }
119 scan_ll(text, &result, NULL);
120 return result;
121 }
122
123
124
125
126
127
128
129
130
131
132
133 int
134 crm_parse_int(const char *text, const char *default_text)
135 {
136 long long result = crm_parse_ll(text, default_text);
137
138 if (result < INT_MIN) {
139
140 if (errno != ERANGE) {
141 crm_err("Conversion of %s was clipped: %lld", text, result);
142 errno = ERANGE;
143 }
144 return INT_MIN;
145
146 } else if (result > INT_MAX) {
147
148 if (errno != ERANGE) {
149 crm_err("Conversion of %s was clipped: %lld", text, result);
150 errno = ERANGE;
151 }
152 return INT_MAX;
153 }
154
155 return (int) result;
156 }
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177 int
178 pcmk__scan_double(const char *text, double *result, const char *default_text,
179 char **end_text)
180 {
181 int rc = pcmk_rc_ok;
182 char *local_end_text = NULL;
183
184 CRM_ASSERT(result != NULL);
185 *result = PCMK__PARSE_DBL_DEFAULT;
186
187 text = (text != NULL) ? text : default_text;
188
189 if (text == NULL) {
190 rc = EINVAL;
191 crm_debug("No text and no default conversion value supplied");
192
193 } else {
194 errno = 0;
195 *result = strtod(text, &local_end_text);
196
197 if (errno == ERANGE) {
198
199
200
201
202
203
204
205
206
207 const char *over_under;
208
209 if (fabs(*result) > DBL_MIN) {
210 rc = EOVERFLOW;
211 over_under = "over";
212 } else {
213 rc = pcmk_rc_underflow;
214 over_under = "under";
215 }
216
217 crm_debug("Floating-point value parsed from '%s' would %sflow "
218 "(using %g instead)", text, over_under, *result);
219
220 } else if (errno != 0) {
221 rc = errno;
222
223 *result = PCMK__PARSE_DBL_DEFAULT;
224
225 crm_debug("Could not parse floating-point value from '%s' (using "
226 "%.1f instead): %s", text, PCMK__PARSE_DBL_DEFAULT,
227 pcmk_rc_str(rc));
228
229 } else if (local_end_text == text) {
230
231 rc = EINVAL;
232 *result = PCMK__PARSE_DBL_DEFAULT;
233
234 crm_debug("Could not parse floating-point value from '%s' (using "
235 "%.1f instead): No digits found", text,
236 PCMK__PARSE_DBL_DEFAULT);
237
238 } else if (fabs(*result) <= DBL_MIN) {
239
240
241
242
243
244
245
246
247
248
249
250
251 for (const char *p = text; p != local_end_text; p++) {
252 if (strchr("0.eE", *p) == NULL) {
253 rc = pcmk_rc_underflow;
254 crm_debug("Floating-point value parsed from '%s' would "
255 "underflow (using %g instead)", text, *result);
256 break;
257 }
258 }
259
260 } else {
261 crm_trace("Floating-point value parsed successfully from "
262 "'%s': %g", text, *result);
263 }
264
265 if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
266 crm_debug("Characters left over after parsing '%s': '%s'",
267 text, local_end_text);
268 }
269 }
270
271 if (end_text != NULL) {
272 *end_text = local_end_text;
273 }
274
275 return rc;
276 }
277
278
279
280
281
282
283
284
285
286
287
288
289 int
290 pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val,
291 guint *result)
292 {
293 const char *value;
294 long long value_ll;
295
296 CRM_CHECK((table != NULL) && (key != NULL), return EINVAL);
297
298 value = g_hash_table_lookup(table, key);
299 if (value == NULL) {
300 if (result != NULL) {
301 *result = default_val;
302 }
303 return pcmk_rc_ok;
304 }
305
306 errno = 0;
307 value_ll = crm_parse_ll(value, NULL);
308 if (errno != 0) {
309 return errno;
310 }
311 if ((value_ll < 0) || (value_ll > G_MAXUINT)) {
312 crm_warn("Could not parse non-negative integer from %s", value);
313 return ERANGE;
314 }
315
316 if (result != NULL) {
317 *result = (guint) value_ll;
318 }
319 return pcmk_rc_ok;
320 }
321
322 #ifndef NUMCHARS
323 # define NUMCHARS "0123456789."
324 #endif
325
326 #ifndef WHITESPACE
327 # define WHITESPACE " \t\n\r\f"
328 #endif
329
330
331
332
333
334
335
336
337
338
339 long long
340 crm_get_msec(const char *input)
341 {
342 const char *num_start = NULL;
343 const char *units;
344 long long multiplier = 1000;
345 long long divisor = 1;
346 long long msec = PCMK__PARSE_INT_DEFAULT;
347 size_t num_len = 0;
348 char *end_text = NULL;
349
350 if (input == NULL) {
351 return PCMK__PARSE_INT_DEFAULT;
352 }
353
354 num_start = input + strspn(input, WHITESPACE);
355 num_len = strspn(num_start, NUMCHARS);
356 if (num_len < 1) {
357 return PCMK__PARSE_INT_DEFAULT;
358 }
359 units = num_start + num_len;
360 units += strspn(units, WHITESPACE);
361
362 if (!strncasecmp(units, "ms", 2) || !strncasecmp(units, "msec", 4)) {
363 multiplier = 1;
364 divisor = 1;
365 } else if (!strncasecmp(units, "us", 2) || !strncasecmp(units, "usec", 4)) {
366 multiplier = 1;
367 divisor = 1000;
368 } else if (!strncasecmp(units, "s", 1) || !strncasecmp(units, "sec", 3)) {
369 multiplier = 1000;
370 divisor = 1;
371 } else if (!strncasecmp(units, "m", 1) || !strncasecmp(units, "min", 3)) {
372 multiplier = 60 * 1000;
373 divisor = 1;
374 } else if (!strncasecmp(units, "h", 1) || !strncasecmp(units, "hr", 2)) {
375 multiplier = 60 * 60 * 1000;
376 divisor = 1;
377 } else if ((*units != EOS) && (*units != '\n') && (*units != '\r')) {
378 return PCMK__PARSE_INT_DEFAULT;
379 }
380
381 scan_ll(num_start, &msec, &end_text);
382 if (msec > (LLONG_MAX / multiplier)) {
383
384 return LLONG_MAX;
385 }
386 msec *= multiplier;
387 msec /= divisor;
388 return msec;
389 }
390
391 gboolean
392 crm_is_true(const char *s)
393 {
394 gboolean ret = FALSE;
395
396 if (s != NULL) {
397 crm_str_to_boolean(s, &ret);
398 }
399 return ret;
400 }
401
402 int
403 crm_str_to_boolean(const char *s, int *ret)
404 {
405 if (s == NULL) {
406 return -1;
407
408 } else if (strcasecmp(s, "true") == 0
409 || strcasecmp(s, "on") == 0
410 || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
411 *ret = TRUE;
412 return 1;
413
414 } else if (strcasecmp(s, "false") == 0
415 || strcasecmp(s, "off") == 0
416 || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
417 *ret = FALSE;
418 return 1;
419 }
420 return -1;
421 }
422
423 char *
424 crm_strip_trailing_newline(char *str)
425 {
426 int len;
427
428 if (str == NULL) {
429 return str;
430 }
431
432 for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
433 str[len] = '\0';
434 }
435
436 return str;
437 }
438
439
440
441
442
443
444
445
446
447
448
449
450
451 bool
452 pcmk__starts_with(const char *str, const char *prefix)
453 {
454 const char *s = str;
455 const char *p = prefix;
456
457 if (!s || !p) {
458 return false;
459 }
460 while (*s && *p) {
461 if (*s++ != *p++) {
462 return false;
463 }
464 }
465 return (*p == 0);
466 }
467
468 static inline bool
469 ends_with(const char *s, const char *match, bool as_extension)
470 {
471 if (pcmk__str_empty(match)) {
472 return true;
473 } else if (s == NULL) {
474 return false;
475 } else {
476 size_t slen, mlen;
477
478
479
480
481 if (as_extension) {
482 s = strrchr(s, match[0]);
483 return (s == NULL)? false : !strcmp(s, match);
484 }
485
486 mlen = strlen(match);
487 slen = strlen(s);
488 return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
489 }
490 }
491
492
493
494
495
496
497
498
499
500
501
502
503 bool
504 pcmk__ends_with(const char *s, const char *match)
505 {
506 return ends_with(s, match, false);
507 }
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530 bool
531 pcmk__ends_with_ext(const char *s, const char *match)
532 {
533 return ends_with(s, match, true);
534 }
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549 guint
550 g_str_hash_traditional(gconstpointer v)
551 {
552 const signed char *p;
553 guint32 h = 0;
554
555 for (p = v; *p != '\0'; p++)
556 h = (h << 5) - h + *p;
557
558 return h;
559 }
560
561
562 gboolean
563 crm_strcase_equal(gconstpointer a, gconstpointer b)
564 {
565 return pcmk__str_eq((const char *)a, (const char *)b, pcmk__str_casei);
566 }
567
568 guint
569 crm_strcase_hash(gconstpointer v)
570 {
571 const signed char *p;
572 guint32 h = 0;
573
574 for (p = v; *p != '\0'; p++)
575 h = (h << 5) - h + g_ascii_tolower(*p);
576
577 return h;
578 }
579
580 static void
581 copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
582 {
583 if (key && value && user_data) {
584 g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
585 }
586 }
587
588 GHashTable *
589 crm_str_table_dup(GHashTable *old_table)
590 {
591 GHashTable *new_table = NULL;
592
593 if (old_table) {
594 new_table = crm_str_table_new();
595 g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
596 }
597 return new_table;
598 }
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616 void
617 pcmk__add_separated_word(char **list, size_t *len, const char *word,
618 const char *separator)
619 {
620 size_t orig_len, new_len;
621
622 CRM_ASSERT(list != NULL);
623
624 if (pcmk__str_empty(word)) {
625 return;
626 }
627
628
629 orig_len = (len != NULL)? *len : ((*list == NULL)? 0 : strlen(*list));
630
631
632 if (orig_len == 0) {
633 separator = "";
634
635
636 } else if (separator == NULL) {
637 separator = " ";
638 }
639
640 new_len = orig_len + strlen(separator) + strlen(word);
641 if (len != NULL) {
642 *len = new_len;
643 }
644
645
646 *list = pcmk__realloc(*list, new_len + 1);
647 sprintf(*list + orig_len, "%s%s", separator, word);
648 }
649
650
651
652
653
654
655
656
657
658
659
660
661
662 int
663 pcmk__compress(const char *data, unsigned int length, unsigned int max,
664 char **result, unsigned int *result_len)
665 {
666 int rc;
667 char *compressed = NULL;
668 char *uncompressed = strdup(data);
669 #ifdef CLOCK_MONOTONIC
670 struct timespec after_t;
671 struct timespec before_t;
672 #endif
673
674 if (max == 0) {
675 max = (length * 1.01) + 601;
676 }
677
678 #ifdef CLOCK_MONOTONIC
679 clock_gettime(CLOCK_MONOTONIC, &before_t);
680 #endif
681
682 compressed = calloc((size_t) max, sizeof(char));
683 CRM_ASSERT(compressed);
684
685 *result_len = max;
686 rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length,
687 CRM_BZ2_BLOCKS, 0, CRM_BZ2_WORK);
688 free(uncompressed);
689 if (rc != BZ_OK) {
690 crm_err("Compression of %d bytes failed: %s " CRM_XS " bzerror=%d",
691 length, bz2_strerror(rc), rc);
692 free(compressed);
693 return pcmk_rc_error;
694 }
695
696 #ifdef CLOCK_MONOTONIC
697 clock_gettime(CLOCK_MONOTONIC, &after_t);
698
699 crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
700 length, *result_len, length / (*result_len),
701 (after_t.tv_sec - before_t.tv_sec) * 1000 +
702 (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
703 #else
704 crm_trace("Compressed %d bytes into %d (ratio %d:1)",
705 length, *result_len, length / (*result_len));
706 #endif
707
708 *result = compressed;
709 return pcmk_rc_ok;
710 }
711
712 char *
713 crm_strdup_printf(char const *format, ...)
714 {
715 va_list ap;
716 int len = 0;
717 char *string = NULL;
718
719 va_start(ap, format);
720 len = vasprintf (&string, format, ap);
721 CRM_ASSERT(len > 0);
722 va_end(ap);
723 return string;
724 }
725
726 int
727 pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
728 {
729 char *remainder = NULL;
730
731 CRM_ASSERT(start != NULL && end != NULL);
732
733 *start = PCMK__PARSE_INT_DEFAULT;
734 *end = PCMK__PARSE_INT_DEFAULT;
735
736 crm_trace("Attempting to decode: [%s]", srcstring);
737 if (pcmk__str_empty(srcstring) || !strcmp(srcstring, "-")) {
738 return pcmk_rc_unknown_format;
739 }
740
741
742
743
744 if (*srcstring == '-') {
745 int rc = scan_ll(srcstring+1, end, &remainder);
746
747 if (rc != pcmk_rc_ok || *remainder != '\0') {
748 return pcmk_rc_unknown_format;
749 } else {
750 return pcmk_rc_ok;
751 }
752 }
753
754 if (scan_ll(srcstring, start, &remainder) != pcmk_rc_ok) {
755 return pcmk_rc_unknown_format;
756 }
757
758 if (*remainder && *remainder == '-') {
759 if (*(remainder+1)) {
760 char *more_remainder = NULL;
761 int rc = scan_ll(remainder+1, end, &more_remainder);
762
763 if (rc != pcmk_rc_ok || *more_remainder != '\0') {
764 return pcmk_rc_unknown_format;
765 }
766 }
767 } else if (*remainder && *remainder != '-') {
768 *start = PCMK__PARSE_INT_DEFAULT;
769 return pcmk_rc_unknown_format;
770 } else {
771
772
773
774
775 *end = *start;
776 }
777
778 return pcmk_rc_ok;
779 }
780
781
782
783
784
785
786
787
788
789
790
791
792
793 gboolean
794 pcmk__str_in_list(GList *lst, const gchar *s)
795 {
796 if (lst == NULL) {
797 return FALSE;
798 }
799
800 if (strcmp(lst->data, "*") == 0 && lst->next == NULL) {
801 return TRUE;
802 }
803
804 return g_list_find_custom(lst, s, (GCompareFunc) strcmp) != NULL;
805 }
806
807 static bool
808 str_any_of(bool casei, const char *s, va_list args)
809 {
810 bool rc = false;
811
812 if (s != NULL) {
813 while (1) {
814 const char *ele = va_arg(args, const char *);
815
816 if (ele == NULL) {
817 break;
818 } else if (pcmk__str_eq(s, ele,
819 casei? pcmk__str_casei : pcmk__str_none)) {
820 rc = true;
821 break;
822 }
823 }
824 }
825 return rc;
826 }
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841 bool
842 pcmk__strcase_any_of(const char *s, ...)
843 {
844 va_list ap;
845 bool rc;
846
847 va_start(ap, s);
848 rc = str_any_of(true, s, ap);
849 va_end(ap);
850 return rc;
851 }
852
853
854
855
856
857
858
859
860
861
862
863
864
865 bool
866 pcmk__str_any_of(const char *s, ...)
867 {
868 va_list ap;
869 bool rc;
870
871 va_start(ap, s);
872 rc = str_any_of(false, s, ap);
873 va_end(ap);
874 return rc;
875 }
876
877
878
879
880
881
882
883
884
885
886
887
888 bool
889 pcmk__char_in_any_str(int ch, ...)
890 {
891 bool rc = false;
892 va_list ap;
893
894
895
896
897
898 va_start(ap, ch);
899
900 while (1) {
901 const char *ele = va_arg(ap, const char *);
902
903 if (ele == NULL) {
904 break;
905 } else if (strchr(ele, ch) != NULL) {
906 rc = true;
907 break;
908 }
909 }
910
911 va_end(ap);
912 return rc;
913 }
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930 int
931 pcmk_numeric_strcasecmp(const char *s1, const char *s2)
932 {
933 while (*s1 && *s2) {
934 if (isdigit(*s1) && isdigit(*s2)) {
935
936
937 char *end1 = NULL;
938 char *end2 = NULL;
939 long num1 = strtol(s1, &end1, 10);
940 long num2 = strtol(s2, &end2, 10);
941
942
943 size_t len1 = end1 - s1;
944 size_t len2 = end2 - s2;
945
946 if (num1 < num2) {
947 return -1;
948 } else if (num1 > num2) {
949 return 1;
950 } else if (len1 < len2) {
951 return -1;
952 } else if (len1 > len2) {
953 return 1;
954 }
955 s1 = end1;
956 s2 = end2;
957 } else {
958
959 int lower1 = tolower(*s1);
960 int lower2 = tolower(*s2);
961
962 if (lower1 < lower2) {
963 return -1;
964 } else if (lower1 > lower2) {
965 return 1;
966 }
967 ++s1;
968 ++s2;
969 }
970 }
971 if (!*s1 && *s2) {
972 return -1;
973 } else if (*s1 && !*s2) {
974 return 1;
975 }
976 return 0;
977 }
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008 int
1009 pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
1010 {
1011
1012 if (pcmk_is_set(flags, pcmk__str_regex)) {
1013 regex_t *r_patt = calloc(1, sizeof(regex_t));
1014 int reg_flags = REG_EXTENDED | REG_NOSUB;
1015 int regcomp_rc = 0;
1016 int rc = 0;
1017
1018 if (s1 == NULL || s2 == NULL) {
1019 free(r_patt);
1020 return 1;
1021 }
1022
1023 if (pcmk_is_set(flags, pcmk__str_casei)) {
1024 reg_flags |= REG_ICASE;
1025 }
1026 regcomp_rc = regcomp(r_patt, s2, reg_flags);
1027 if (regcomp_rc != 0) {
1028 rc = 1;
1029 crm_err("Bad regex '%s' for update: %s", s2, strerror(regcomp_rc));
1030 } else {
1031 rc = regexec(r_patt, s1, 0, NULL, 0);
1032
1033 if (rc != 0) {
1034 rc = 1;
1035 }
1036 }
1037
1038 regfree(r_patt);
1039 free(r_patt);
1040 return rc;
1041 }
1042
1043
1044 if (s1 == s2) {
1045 return 0;
1046 }
1047
1048
1049
1050
1051
1052 if (pcmk_is_set(flags, pcmk__str_null_matches)) {
1053 if (s1 == NULL || s2 == NULL) {
1054 return 0;
1055 }
1056 }
1057
1058
1059
1060
1061 if (s1 == NULL) {
1062 return -1;
1063 } else if (s2 == NULL) {
1064 return 1;
1065 }
1066
1067 if (pcmk_is_set(flags, pcmk__str_casei)) {
1068 return strcasecmp(s1, s2);
1069 } else {
1070 return strcmp(s1, s2);
1071 }
1072 }
1073
1074
1075
1076 gboolean safe_str_neq(const char *a, const char *b);
1077
1078 gboolean crm_str_eq(const char *a, const char *b, gboolean use_case);
1079
1080
1081 gboolean
1082 safe_str_neq(const char *a, const char *b)
1083 {
1084 if (a == b) {
1085 return FALSE;
1086
1087 } else if (a == NULL || b == NULL) {
1088 return TRUE;
1089
1090 } else if (strcasecmp(a, b) == 0) {
1091 return FALSE;
1092 }
1093 return TRUE;
1094 }
1095
1096
1097 gboolean
1098 crm_str_eq(const char *a, const char *b, gboolean use_case)
1099 {
1100 if (use_case) {
1101 return g_strcmp0(a, b) == 0;
1102
1103
1104 } else if (a == b) {
1105 return TRUE;
1106
1107 } else if (a == NULL || b == NULL) {
1108
1109 return FALSE;
1110
1111 } else if (strcasecmp(a, b) == 0) {
1112 return TRUE;
1113 }
1114 return FALSE;
1115 }