]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/string-util.c
Merge pull request #8575 from keszybz/non-absolute-paths
[thirdparty/systemd.git] / src / basic / string-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
07630cea
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
07630cea
LP
6***/
7
11c3a366
TA
8#include <errno.h>
9#include <stdarg.h>
10#include <stdint.h>
11#include <stdio.h>
0d536673 12#include <stdio_ext.h>
11c3a366 13#include <stdlib.h>
b6b609db 14#include <string.h>
11c3a366 15
b5efdb8a 16#include "alloc-util.h"
07630cea 17#include "gunicode.h"
11c3a366 18#include "macro.h"
b11d6a7b 19#include "string-util.h"
b4766d5f 20#include "terminal-util.h"
07630cea
LP
21#include "utf8.h"
22#include "util.h"
07630cea
LP
23
24int strcmp_ptr(const char *a, const char *b) {
25
26 /* Like strcmp(), but tries to make sense of NULL pointers */
27 if (a && b)
28 return strcmp(a, b);
29
30 if (!a && b)
31 return -1;
32
33 if (a && !b)
34 return 1;
35
36 return 0;
37}
38
39char* endswith(const char *s, const char *postfix) {
40 size_t sl, pl;
41
42 assert(s);
43 assert(postfix);
44
45 sl = strlen(s);
46 pl = strlen(postfix);
47
48 if (pl == 0)
49 return (char*) s + sl;
50
51 if (sl < pl)
52 return NULL;
53
54 if (memcmp(s + sl - pl, postfix, pl) != 0)
55 return NULL;
56
57 return (char*) s + sl - pl;
58}
59
60char* endswith_no_case(const char *s, const char *postfix) {
61 size_t sl, pl;
62
63 assert(s);
64 assert(postfix);
65
66 sl = strlen(s);
67 pl = strlen(postfix);
68
69 if (pl == 0)
70 return (char*) s + sl;
71
72 if (sl < pl)
73 return NULL;
74
75 if (strcasecmp(s + sl - pl, postfix) != 0)
76 return NULL;
77
78 return (char*) s + sl - pl;
79}
80
81char* first_word(const char *s, const char *word) {
82 size_t sl, wl;
83 const char *p;
84
85 assert(s);
86 assert(word);
87
88 /* Checks if the string starts with the specified word, either
89 * followed by NUL or by whitespace. Returns a pointer to the
90 * NUL or the first character after the whitespace. */
91
92 sl = strlen(s);
93 wl = strlen(word);
94
95 if (sl < wl)
96 return NULL;
97
98 if (wl == 0)
99 return (char*) s;
100
101 if (memcmp(s, word, wl) != 0)
102 return NULL;
103
104 p = s + wl;
105 if (*p == 0)
106 return (char*) p;
107
108 if (!strchr(WHITESPACE, *p))
109 return NULL;
110
111 p += strspn(p, WHITESPACE);
112 return (char*) p;
113}
114
115static size_t strcspn_escaped(const char *s, const char *reject) {
116 bool escaped = false;
117 int n;
118
119 for (n=0; s[n]; n++) {
120 if (escaped)
121 escaped = false;
122 else if (s[n] == '\\')
123 escaped = true;
124 else if (strchr(reject, s[n]))
125 break;
126 }
127
128 /* if s ends in \, return index of previous char */
129 return n - escaped;
130}
131
132/* Split a string into words. */
133const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
134 const char *current;
135
136 current = *state;
137
138 if (!*current) {
139 assert(**state == '\0');
140 return NULL;
141 }
142
143 current += strspn(current, separator);
144 if (!*current) {
145 *state = current;
146 return NULL;
147 }
148
149 if (quoted && strchr("\'\"", *current)) {
150 char quotechars[2] = {*current, '\0'};
151
152 *l = strcspn_escaped(current + 1, quotechars);
153 if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
154 (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
155 /* right quote missing or garbage at the end */
156 *state = current;
157 return NULL;
158 }
159 *state = current++ + *l + 2;
160 } else if (quoted) {
161 *l = strcspn_escaped(current, separator);
162 if (current[*l] && !strchr(separator, current[*l])) {
163 /* unfinished escape */
164 *state = current;
165 return NULL;
166 }
167 *state = current + *l;
168 } else {
169 *l = strcspn(current, separator);
170 *state = current + *l;
171 }
172
173 return current;
174}
175
176char *strnappend(const char *s, const char *suffix, size_t b) {
177 size_t a;
178 char *r;
179
180 if (!s && !suffix)
181 return strdup("");
182
183 if (!s)
184 return strndup(suffix, b);
185
186 if (!suffix)
187 return strdup(s);
188
189 assert(s);
190 assert(suffix);
191
192 a = strlen(s);
193 if (b > ((size_t) -1) - a)
194 return NULL;
195
196 r = new(char, a+b+1);
197 if (!r)
198 return NULL;
199
200 memcpy(r, s, a);
201 memcpy(r+a, suffix, b);
202 r[a+b] = 0;
203
204 return r;
205}
206
207char *strappend(const char *s, const char *suffix) {
7bf7ce28 208 return strnappend(s, suffix, strlen_ptr(suffix));
07630cea
LP
209}
210
605405c6 211char *strjoin_real(const char *x, ...) {
07630cea
LP
212 va_list ap;
213 size_t l;
214 char *r, *p;
215
216 va_start(ap, x);
217
218 if (x) {
219 l = strlen(x);
220
221 for (;;) {
222 const char *t;
223 size_t n;
224
225 t = va_arg(ap, const char *);
226 if (!t)
227 break;
228
229 n = strlen(t);
230 if (n > ((size_t) -1) - l) {
231 va_end(ap);
232 return NULL;
233 }
234
235 l += n;
236 }
237 } else
238 l = 0;
239
240 va_end(ap);
241
242 r = new(char, l+1);
243 if (!r)
244 return NULL;
245
246 if (x) {
247 p = stpcpy(r, x);
248
249 va_start(ap, x);
250
251 for (;;) {
252 const char *t;
253
254 t = va_arg(ap, const char *);
255 if (!t)
256 break;
257
258 p = stpcpy(p, t);
259 }
260
261 va_end(ap);
262 } else
263 r[0] = 0;
264
265 return r;
266}
267
268char *strstrip(char *s) {
269 char *e;
270
7546145e
LP
271 if (!s)
272 return NULL;
273
07630cea
LP
274 /* Drops trailing whitespace. Modifies the string in
275 * place. Returns pointer to first non-space character */
276
277 s += strspn(s, WHITESPACE);
278
279 for (e = strchr(s, 0); e > s; e --)
280 if (!strchr(WHITESPACE, e[-1]))
281 break;
282
283 *e = 0;
284
285 return s;
286}
287
288char *delete_chars(char *s, const char *bad) {
289 char *f, *t;
290
7546145e
LP
291 /* Drops all specified bad characters, regardless where in the string */
292
293 if (!s)
294 return NULL;
295
296 if (!bad)
297 bad = WHITESPACE;
07630cea
LP
298
299 for (f = s, t = s; *f; f++) {
300 if (strchr(bad, *f))
301 continue;
302
303 *(t++) = *f;
304 }
305
306 *t = 0;
307
308 return s;
309}
310
7546145e
LP
311char *delete_trailing_chars(char *s, const char *bad) {
312 char *p, *c = s;
313
314 /* Drops all specified bad characters, at the end of the string */
315
316 if (!s)
317 return NULL;
318
319 if (!bad)
320 bad = WHITESPACE;
321
322 for (p = s; *p; p++)
323 if (!strchr(bad, *p))
324 c = p + 1;
325
326 *c = 0;
327
328 return s;
329}
330
07630cea
LP
331char *truncate_nl(char *s) {
332 assert(s);
333
334 s[strcspn(s, NEWLINE)] = 0;
335 return s;
336}
337
b577e3d5
LP
338char ascii_tolower(char x) {
339
340 if (x >= 'A' && x <= 'Z')
341 return x - 'A' + 'a';
342
343 return x;
344}
345
846b8fc3
LP
346char ascii_toupper(char x) {
347
348 if (x >= 'a' && x <= 'z')
349 return x - 'a' + 'A';
350
351 return x;
352}
353
07630cea
LP
354char *ascii_strlower(char *t) {
355 char *p;
356
357 assert(t);
358
359 for (p = t; *p; p++)
b577e3d5
LP
360 *p = ascii_tolower(*p);
361
362 return t;
363}
364
846b8fc3
LP
365char *ascii_strupper(char *t) {
366 char *p;
367
368 assert(t);
369
370 for (p = t; *p; p++)
371 *p = ascii_toupper(*p);
372
373 return t;
374}
375
b577e3d5
LP
376char *ascii_strlower_n(char *t, size_t n) {
377 size_t i;
378
379 if (n <= 0)
380 return t;
381
382 for (i = 0; i < n; i++)
383 t[i] = ascii_tolower(t[i]);
07630cea
LP
384
385 return t;
386}
522d85ae
LP
387
388int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
389
390 for (; n > 0; a++, b++, n--) {
391 int x, y;
392
393 x = (int) (uint8_t) ascii_tolower(*a);
394 y = (int) (uint8_t) ascii_tolower(*b);
395
396 if (x != y)
397 return x - y;
398 }
399
400 return 0;
401}
c1749834
LP
402
403int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
404 int r;
405
406 r = ascii_strcasecmp_n(a, b, MIN(n, m));
407 if (r != 0)
408 return r;
409
410 if (n < m)
411 return -1;
412 else if (n > m)
413 return 1;
414 else
415 return 0;
416}
07630cea
LP
417
418bool chars_intersect(const char *a, const char *b) {
419 const char *p;
420
421 /* Returns true if any of the chars in a are in b. */
422 for (p = a; *p; p++)
423 if (strchr(b, *p))
424 return true;
425
426 return false;
427}
428
429bool string_has_cc(const char *p, const char *ok) {
430 const char *t;
431
432 assert(p);
433
434 /*
435 * Check if a string contains control characters. If 'ok' is
436 * non-NULL it may be a string containing additional CCs to be
437 * considered OK.
438 */
439
440 for (t = p; *t; t++) {
441 if (ok && strchr(ok, *t))
442 continue;
443
444 if (*t > 0 && *t < ' ')
445 return true;
446
447 if (*t == 127)
448 return true;
449 }
450
451 return false;
452}
453
454static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
455 size_t x;
456 char *r;
457
458 assert(s);
459 assert(percent <= 100);
460 assert(new_length >= 3);
461
462 if (old_length <= 3 || old_length <= new_length)
463 return strndup(s, old_length);
464
481a2b02 465 r = new0(char, new_length+3);
07630cea
LP
466 if (!r)
467 return NULL;
468
469 x = (new_length * percent) / 100;
470
471 if (x > new_length - 3)
472 x = new_length - 3;
473
474 memcpy(r, s, x);
481a2b02
ZJS
475 r[x] = 0xe2; /* tri-dot ellipsis: … */
476 r[x+1] = 0x80;
477 r[x+2] = 0xa6;
07630cea 478 memcpy(r + x + 3,
481a2b02
ZJS
479 s + old_length - (new_length - x - 1),
480 new_length - x - 1);
07630cea
LP
481
482 return r;
483}
484
485char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
486 size_t x;
487 char *e;
488 const char *i, *j;
489 unsigned k, len, len2;
c932fb71 490 int r;
07630cea
LP
491
492 assert(s);
493 assert(percent <= 100);
ddbc9319
LP
494
495 if (new_length == (size_t) -1)
496 return strndup(s, old_length);
497
07630cea
LP
498 assert(new_length >= 3);
499
500 /* if no multibyte characters use ascii_ellipsize_mem for speed */
501 if (ascii_is_valid(s))
502 return ascii_ellipsize_mem(s, old_length, new_length, percent);
503
504 if (old_length <= 3 || old_length <= new_length)
505 return strndup(s, old_length);
506
507 x = (new_length * percent) / 100;
508
509 if (x > new_length - 3)
510 x = new_length - 3;
511
512 k = 0;
513 for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
c932fb71 514 char32_t c;
07630cea 515
c932fb71
SL
516 r = utf8_encoded_to_unichar(i, &c);
517 if (r < 0)
07630cea
LP
518 return NULL;
519 k += unichar_iswide(c) ? 2 : 1;
520 }
521
522 if (k > x) /* last character was wide and went over quota */
313cefa1 523 x++;
07630cea
LP
524
525 for (j = s + old_length; k < new_length && j > i; ) {
c932fb71 526 char32_t c;
07630cea
LP
527
528 j = utf8_prev_char(j);
c932fb71
SL
529 r = utf8_encoded_to_unichar(j, &c);
530 if (r < 0)
07630cea
LP
531 return NULL;
532 k += unichar_iswide(c) ? 2 : 1;
533 }
534 assert(i <= j);
535
536 /* we don't actually need to ellipsize */
537 if (i == j)
538 return memdup(s, old_length + 1);
539
540 /* make space for ellipsis */
541 j = utf8_next_char(j);
542
543 len = i - s;
544 len2 = s + old_length - j;
545 e = new(char, len + 3 + len2 + 1);
546 if (!e)
547 return NULL;
548
549 /*
550 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
551 old_length, new_length, x, len, len2, k);
552 */
553
554 memcpy(e, s, len);
555 e[len] = 0xe2; /* tri-dot ellipsis: … */
556 e[len + 1] = 0x80;
557 e[len + 2] = 0xa6;
558
559 memcpy(e + len + 3, j, len2 + 1);
560
561 return e;
562}
563
564char *ellipsize(const char *s, size_t length, unsigned percent) {
ddbc9319
LP
565
566 if (length == (size_t) -1)
567 return strdup(s);
568
07630cea
LP
569 return ellipsize_mem(s, strlen(s), length, percent);
570}
571
2d5dece8 572bool nulstr_contains(const char *nulstr, const char *needle) {
07630cea
LP
573 const char *i;
574
575 if (!nulstr)
576 return false;
577
578 NULSTR_FOREACH(i, nulstr)
579 if (streq(i, needle))
580 return true;
581
582 return false;
583}
584
585char* strshorten(char *s, size_t l) {
586 assert(s);
587
47b33c7d 588 if (strnlen(s, l+1) > l)
07630cea
LP
589 s[l] = 0;
590
591 return s;
592}
593
594char *strreplace(const char *text, const char *old_string, const char *new_string) {
9d73565a
LP
595 size_t l, old_len, new_len, allocated = 0;
596 char *t, *ret = NULL;
07630cea 597 const char *f;
07630cea 598
07630cea
LP
599 assert(old_string);
600 assert(new_string);
601
9d73565a
LP
602 if (!text)
603 return NULL;
604
07630cea
LP
605 old_len = strlen(old_string);
606 new_len = strlen(new_string);
607
608 l = strlen(text);
9d73565a 609 if (!GREEDY_REALLOC(ret, allocated, l+1))
07630cea
LP
610 return NULL;
611
612 f = text;
9d73565a 613 t = ret;
07630cea 614 while (*f) {
07630cea
LP
615 size_t d, nl;
616
617 if (!startswith(f, old_string)) {
618 *(t++) = *(f++);
619 continue;
620 }
621
9d73565a 622 d = t - ret;
07630cea 623 nl = l - old_len + new_len;
9d73565a
LP
624
625 if (!GREEDY_REALLOC(ret, allocated, nl + 1))
626 return mfree(ret);
07630cea
LP
627
628 l = nl;
9d73565a 629 t = ret + d;
07630cea
LP
630
631 t = stpcpy(t, new_string);
632 f += old_len;
633 }
634
635 *t = 0;
9d73565a 636 return ret;
07630cea
LP
637}
638
b4766d5f
ZJS
639static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
640 if (!offsets)
641 return;
642
643 if ((size_t) diff < offsets[0])
644 shift[0] += size;
645 if ((size_t) diff < offsets[1])
646 shift[1] += size;
647}
648
649char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
07630cea
LP
650 const char *i, *begin = NULL;
651 enum {
652 STATE_OTHER,
653 STATE_ESCAPE,
654 STATE_BRACKET
655 } state = STATE_OTHER;
656 char *obuf = NULL;
b4766d5f 657 size_t osz = 0, isz, shift[2] = {};
07630cea
LP
658 FILE *f;
659
660 assert(ibuf);
661 assert(*ibuf);
662
663 /* Strips ANSI color and replaces TABs by 8 spaces */
664
665 isz = _isz ? *_isz : strlen(*ibuf);
666
667 f = open_memstream(&obuf, &osz);
668 if (!f)
669 return NULL;
670
0d536673
LP
671 /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we created f here
672 * and it doesn't leave our scope. */
673
674 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
db3f45e2 675
07630cea
LP
676 for (i = *ibuf; i < *ibuf + isz + 1; i++) {
677
678 switch (state) {
679
680 case STATE_OTHER:
681 if (i >= *ibuf + isz) /* EOT */
682 break;
683 else if (*i == '\x1B')
684 state = STATE_ESCAPE;
b4766d5f 685 else if (*i == '\t') {
0d536673 686 fputs(" ", f);
b4766d5f
ZJS
687 advance_offsets(i - *ibuf, highlight, shift, 7);
688 } else
0d536673 689 fputc(*i, f);
b4766d5f 690
07630cea
LP
691 break;
692
693 case STATE_ESCAPE:
694 if (i >= *ibuf + isz) { /* EOT */
0d536673 695 fputc('\x1B', f);
b4766d5f 696 advance_offsets(i - *ibuf, highlight, shift, 1);
07630cea
LP
697 break;
698 } else if (*i == '[') {
699 state = STATE_BRACKET;
700 begin = i + 1;
701 } else {
0d536673
LP
702 fputc('\x1B', f);
703 fputc(*i, f);
b4766d5f 704 advance_offsets(i - *ibuf, highlight, shift, 1);
07630cea
LP
705 state = STATE_OTHER;
706 }
707
708 break;
709
710 case STATE_BRACKET:
711
712 if (i >= *ibuf + isz || /* EOT */
4c701096 713 (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
0d536673
LP
714 fputc('\x1B', f);
715 fputc('[', f);
b4766d5f 716 advance_offsets(i - *ibuf, highlight, shift, 2);
07630cea
LP
717 state = STATE_OTHER;
718 i = begin-1;
719 } else if (*i == 'm')
720 state = STATE_OTHER;
721 break;
722 }
723 }
724
725 if (ferror(f)) {
726 fclose(f);
6b430fdb 727 return mfree(obuf);
07630cea
LP
728 }
729
730 fclose(f);
731
732 free(*ibuf);
733 *ibuf = obuf;
734
735 if (_isz)
736 *_isz = osz;
737
b4766d5f
ZJS
738 if (highlight) {
739 highlight[0] += shift[0];
740 highlight[1] += shift[1];
741 }
742
07630cea
LP
743 return obuf;
744}
745
bb8ad9ea
LP
746char *strextend_with_separator(char **x, const char *separator, ...) {
747 bool need_separator;
748 size_t f, l, l_separator;
07630cea 749 char *r, *p;
bb8ad9ea 750 va_list ap;
07630cea
LP
751
752 assert(x);
753
7bf7ce28 754 l = f = strlen_ptr(*x);
07630cea 755
bb8ad9ea
LP
756 need_separator = !isempty(*x);
757 l_separator = strlen_ptr(separator);
758
759 va_start(ap, separator);
07630cea
LP
760 for (;;) {
761 const char *t;
762 size_t n;
763
764 t = va_arg(ap, const char *);
765 if (!t)
766 break;
767
768 n = strlen(t);
bb8ad9ea
LP
769
770 if (need_separator)
771 n += l_separator;
772
07630cea
LP
773 if (n > ((size_t) -1) - l) {
774 va_end(ap);
775 return NULL;
776 }
777
778 l += n;
bb8ad9ea 779 need_separator = true;
07630cea
LP
780 }
781 va_end(ap);
782
bb8ad9ea
LP
783 need_separator = !isempty(*x);
784
07630cea
LP
785 r = realloc(*x, l+1);
786 if (!r)
787 return NULL;
788
789 p = r + f;
790
bb8ad9ea 791 va_start(ap, separator);
07630cea
LP
792 for (;;) {
793 const char *t;
794
795 t = va_arg(ap, const char *);
796 if (!t)
797 break;
798
bb8ad9ea
LP
799 if (need_separator && separator)
800 p = stpcpy(p, separator);
801
07630cea 802 p = stpcpy(p, t);
bb8ad9ea
LP
803
804 need_separator = true;
07630cea
LP
805 }
806 va_end(ap);
807
bb8ad9ea
LP
808 assert(p == r + l);
809
07630cea
LP
810 *p = 0;
811 *x = r;
812
813 return r + l;
814}
815
816char *strrep(const char *s, unsigned n) {
817 size_t l;
818 char *r, *p;
819 unsigned i;
820
821 assert(s);
822
823 l = strlen(s);
824 p = r = malloc(l * n + 1);
825 if (!r)
826 return NULL;
827
828 for (i = 0; i < n; i++)
829 p = stpcpy(p, s);
830
831 *p = 0;
832 return r;
833}
834
835int split_pair(const char *s, const char *sep, char **l, char **r) {
836 char *x, *a, *b;
837
838 assert(s);
839 assert(sep);
840 assert(l);
841 assert(r);
842
843 if (isempty(sep))
844 return -EINVAL;
845
846 x = strstr(s, sep);
847 if (!x)
848 return -EINVAL;
849
850 a = strndup(s, x - s);
851 if (!a)
852 return -ENOMEM;
853
854 b = strdup(x + strlen(sep));
855 if (!b) {
856 free(a);
857 return -ENOMEM;
858 }
859
860 *l = a;
861 *r = b;
862
863 return 0;
864}
865
866int free_and_strdup(char **p, const char *s) {
867 char *t;
868
869 assert(p);
870
871 /* Replaces a string pointer with an strdup()ed new string,
872 * possibly freeing the old one. */
873
874 if (streq_ptr(*p, s))
875 return 0;
876
877 if (s) {
878 t = strdup(s);
879 if (!t)
880 return -ENOMEM;
881 } else
882 t = NULL;
883
884 free(*p);
885 *p = t;
886
887 return 1;
888}
889
4b9545f1 890#if !HAVE_EXPLICIT_BZERO
b6b609db
MB
891/*
892 * Pointer to memset is volatile so that compiler must de-reference
893 * the pointer and can't assume that it points to any function in
894 * particular (such as memset, which it then might further "optimize")
895 * This approach is inspired by openssl's crypto/mem_clr.c.
896 */
897typedef void *(*memset_t)(void *,int,size_t);
9fe4ea21 898
b6b609db 899static volatile memset_t memset_func = memset;
9fe4ea21 900
2d26d8e0
ZJS
901void explicit_bzero(void *p, size_t l) {
902 memset_func(p, '\0', l);
9fe4ea21 903}
2d26d8e0 904#endif
9fe4ea21 905
9fe4ea21 906char* string_erase(char *x) {
07630cea 907 if (!x)
9fe4ea21 908 return NULL;
07630cea
LP
909
910 /* A delicious drop of snake-oil! To be called on memory where
911 * we stored passphrases or so, after we used them. */
2d26d8e0
ZJS
912 explicit_bzero(x, strlen(x));
913 return x;
07630cea
LP
914}
915
916char *string_free_erase(char *s) {
9fe4ea21 917 return mfree(string_erase(s));
07630cea 918}
f3e2e81d
LP
919
920bool string_is_safe(const char *p) {
921 const char *t;
922
923 if (!p)
924 return false;
925
926 for (t = p; *t; t++) {
927 if (*t > 0 && *t < ' ') /* no control characters */
928 return false;
929
930 if (strchr(QUOTES "\\\x7f", *t))
931 return false;
932 }
933
934 return true;
935}