]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/string_utils.c
seccomp: s/seccomp_notif_id_valid/seccomp_notify_id_valid/g
[mirror_lxc.git] / src / lxc / string_utils.c
1 /* liblxcapi
2 *
3 * Copyright © 2019 Christian Brauner <christian.brauner@ubuntu.com>.
4 * Copyright © 2019 Canonical Ltd.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this library; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #ifndef _GNU_SOURCE
22 #define _GNU_SOURCE 1
23 #endif
24 #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
25 #include <ctype.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <grp.h>
30 #include <inttypes.h>
31 #include <libgen.h>
32 #include <pthread.h>
33 #include <stdarg.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/mman.h>
39 #include <sys/mount.h>
40 #include <sys/param.h>
41 #include <sys/prctl.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <sys/wait.h>
45 #include <unistd.h>
46
47 #include "config.h"
48 #include "lxclock.h"
49 #include "macro.h"
50 #include "memory_utils.h"
51 #include "namespace.h"
52 #include "parse.h"
53 #include "string_utils.h"
54
55 #ifndef HAVE_STRLCPY
56 #include "include/strlcpy.h"
57 #endif
58
59 #ifndef HAVE_STRLCAT
60 #include "include/strlcat.h"
61 #endif
62
63 char **lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup)
64 {
65 va_list ap2;
66 size_t count = 1 + skip;
67 char **result;
68
69 /* first determine size of argument list, we don't want to reallocate
70 * constantly...
71 */
72 va_copy(ap2, ap);
73 for (;;) {
74 char *arg = va_arg(ap2, char *);
75 if (!arg)
76 break;
77 count++;
78 }
79 va_end(ap2);
80
81 result = calloc(count, sizeof(char *));
82 if (!result)
83 return NULL;
84
85 count = skip;
86 for (;;) {
87 char *arg = va_arg(ap, char *);
88 if (!arg)
89 break;
90 arg = do_strdup ? strdup(arg) : arg;
91 if (!arg)
92 goto oom;
93 result[count++] = arg;
94 }
95
96 /* calloc has already set last element to NULL*/
97 return result;
98
99 oom:
100 free(result);
101 return NULL;
102 }
103
104 const char **lxc_va_arg_list_to_argv_const(va_list ap, size_t skip)
105 {
106 return (const char **)lxc_va_arg_list_to_argv(ap, skip, 0);
107 }
108
109 char *lxc_string_replace(const char *needle, const char *replacement,
110 const char *haystack)
111 {
112 ssize_t len = -1, saved_len = -1;
113 char *result = NULL;
114 size_t replacement_len = strlen(replacement);
115 size_t needle_len = strlen(needle);
116
117 /* should be executed exactly twice */
118 while (len == -1 || result == NULL) {
119 char *p;
120 char *last_p;
121 ssize_t part_len;
122
123 if (len != -1) {
124 result = calloc(1, len + 1);
125 if (!result)
126 return NULL;
127
128 saved_len = len;
129 }
130
131 len = 0;
132
133 for (last_p = (char *)haystack, p = strstr(last_p, needle); p;
134 last_p = p, p = strstr(last_p, needle)) {
135 part_len = (ssize_t)(p - last_p);
136 if (result && part_len > 0)
137 memcpy(&result[len], last_p, part_len);
138
139 len += part_len;
140
141 if (result && replacement_len > 0)
142 memcpy(&result[len], replacement,
143 replacement_len);
144
145 len += replacement_len;
146 p += needle_len;
147 }
148
149 part_len = strlen(last_p);
150 if (result && part_len > 0)
151 memcpy(&result[len], last_p, part_len);
152
153 len += part_len;
154 }
155
156 /* make sure we did the same thing twice,
157 * once for calculating length, the other
158 * time for copying data */
159 if (saved_len != len) {
160 free(result);
161 return NULL;
162 }
163
164 /* make sure we didn't overwrite any buffer,
165 * due to calloc the string should be 0-terminated */
166 if (result[len] != '\0') {
167 free(result);
168 return NULL;
169 }
170
171 return result;
172 }
173
174 bool lxc_string_in_array(const char *needle, const char **haystack)
175 {
176 for (; haystack && *haystack; haystack++)
177 if (!strcmp(needle, *haystack))
178 return true;
179
180 return false;
181 }
182
183 char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix)
184 {
185 char *result;
186 char **p;
187 size_t sep_len = strlen(sep);
188 size_t result_len = use_as_prefix * sep_len;
189 size_t buf_len;
190
191 /* calculate new string length */
192 for (p = (char **)parts; *p; p++)
193 result_len += (p > (char **)parts) * sep_len + strlen(*p);
194
195 buf_len = result_len + 1;
196 result = calloc(buf_len, 1);
197 if (!result)
198 return NULL;
199
200 if (use_as_prefix)
201 (void)strlcpy(result, sep, buf_len);
202
203 for (p = (char **)parts; *p; p++) {
204 if (p > (char **)parts)
205 (void)strlcat(result, sep, buf_len);
206
207 (void)strlcat(result, *p, buf_len);
208 }
209
210 return result;
211 }
212
213 char **lxc_normalize_path(const char *path)
214 {
215 char **components;
216 char **p;
217 size_t components_len = 0;
218 size_t pos = 0;
219
220 components = lxc_string_split(path, '/');
221 if (!components)
222 return NULL;
223
224 for (p = components; *p; p++)
225 components_len++;
226
227 /* resolve '.' and '..' */
228 for (pos = 0; pos < components_len;) {
229 if (!strcmp(components[pos], ".") ||
230 (!strcmp(components[pos], "..") && pos == 0)) {
231 /* eat this element */
232 free(components[pos]);
233 memmove(&components[pos], &components[pos + 1],
234 sizeof(char *) * (components_len - pos));
235 components_len--;
236 } else if (!strcmp(components[pos], "..")) {
237 /* eat this and the previous element */
238 free(components[pos - 1]);
239 free(components[pos]);
240 memmove(&components[pos - 1], &components[pos + 1],
241 sizeof(char *) * (components_len - pos));
242 components_len -= 2;
243 pos--;
244 } else {
245 pos++;
246 }
247 }
248
249 return components;
250 }
251
252 char *lxc_deslashify(const char *path)
253 {
254 char *dup, *p;
255 char **parts = NULL;
256 size_t n, len;
257
258 dup = strdup(path);
259 if (!dup)
260 return NULL;
261
262 parts = lxc_normalize_path(dup);
263 if (!parts) {
264 free(dup);
265 return NULL;
266 }
267
268 /* We'll end up here if path == "///" or path == "". */
269 if (!*parts) {
270 len = strlen(dup);
271 if (!len) {
272 lxc_free_array((void **)parts, free);
273 return dup;
274 }
275
276 n = strcspn(dup, "/");
277 if (n == len) {
278 free(dup);
279 lxc_free_array((void **)parts, free);
280
281 p = strdup("/");
282 if (!p)
283 return NULL;
284
285 return p;
286 }
287 }
288
289 p = lxc_string_join("/", (const char **)parts, *dup == '/');
290 free(dup);
291 lxc_free_array((void **)parts, free);
292 return p;
293 }
294
295 char *lxc_append_paths(const char *first, const char *second)
296 {
297 int ret;
298 size_t len;
299 char *result = NULL;
300 int pattern_type = 0;
301
302 len = strlen(first) + strlen(second) + 1;
303 if (second[0] != '/') {
304 len += 1;
305 pattern_type = 1;
306 }
307
308 result = calloc(1, len);
309 if (!result)
310 return NULL;
311
312 if (pattern_type == 0)
313 ret = snprintf(result, len, "%s%s", first, second);
314 else
315 ret = snprintf(result, len, "%s/%s", first, second);
316 if (ret < 0 || (size_t)ret >= len) {
317 free(result);
318 return NULL;
319 }
320
321 return result;
322 }
323
324 bool lxc_string_in_list(const char *needle, const char *haystack, char _sep)
325 {
326 __do_free char *str = NULL;
327 char *token;
328 char sep[2] = { _sep, '\0' };
329
330 if (!haystack || !needle)
331 return 0;
332
333 str = must_copy_string(haystack);
334 lxc_iterate_parts(token, str, sep)
335 if (strcmp(needle, token) == 0)
336 return 1;
337
338 return 0;
339 }
340
341 char **lxc_string_split(const char *string, char _sep)
342 {
343 __do_free char *str = NULL;
344 char *token;
345 char sep[2] = {_sep, '\0'};
346 char **tmp = NULL, **result = NULL;
347 size_t result_capacity = 0;
348 size_t result_count = 0;
349 int r, saved_errno;
350
351 if (!string)
352 return calloc(1, sizeof(char *));
353
354 str = must_copy_string(string);
355 lxc_iterate_parts(token, str, sep) {
356 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
357 if (r < 0)
358 goto error_out;
359
360 result[result_count] = strdup(token);
361 if (!result[result_count])
362 goto error_out;
363
364 result_count++;
365 }
366
367 /* if we allocated too much, reduce it */
368 tmp = realloc(result, (result_count + 1) * sizeof(char *));
369 if (!tmp)
370 goto error_out;
371
372 result = tmp;
373
374 /* Make sure we don't return uninitialized memory. */
375 if (result_count == 0)
376 *result = NULL;
377
378 return result;
379
380 error_out:
381 saved_errno = errno;
382 lxc_free_array((void **)result, free);
383 errno = saved_errno;
384 return NULL;
385 }
386
387 static bool complete_word(char ***result, char *start, char *end, size_t *cap,
388 size_t *cnt)
389 {
390 int r;
391
392 r = lxc_grow_array((void ***)result, cap, 2 + *cnt, 16);
393 if (r < 0)
394 return false;
395
396 (*result)[*cnt] = strndup(start, end - start);
397 if (!(*result)[*cnt])
398 return false;
399
400 (*cnt)++;
401
402 return true;
403 }
404
405 /*
406 * Given a a string 'one two "three four"', split into three words,
407 * one, two, and "three four"
408 */
409 char **lxc_string_split_quoted(char *string)
410 {
411 char *nextword = string, *p, state;
412 char **result = NULL;
413 size_t result_capacity = 0;
414 size_t result_count = 0;
415
416 if (!string || !*string)
417 return calloc(1, sizeof(char *));
418
419 // TODO I'm *not* handling escaped quote
420 state = ' ';
421 for (p = string; *p; p++) {
422 switch(state) {
423 case ' ':
424 if (isspace(*p))
425 continue;
426 else if (*p == '"' || *p == '\'') {
427 nextword = p;
428 state = *p;
429 continue;
430 }
431 nextword = p;
432 state = 'a';
433 continue;
434 case 'a':
435 if (isspace(*p)) {
436 complete_word(&result, nextword, p, &result_capacity, &result_count);
437 state = ' ';
438 continue;
439 }
440 continue;
441 case '"':
442 case '\'':
443 if (*p == state) {
444 complete_word(&result, nextword+1, p, &result_capacity, &result_count);
445 state = ' ';
446 continue;
447 }
448 continue;
449 }
450 }
451
452 if (state == 'a')
453 complete_word(&result, nextword, p, &result_capacity, &result_count);
454
455 return realloc(result, (result_count + 1) * sizeof(char *));
456 }
457
458 char **lxc_string_split_and_trim(const char *string, char _sep)
459 {
460 __do_free char *str = NULL;
461 char *token;
462 char sep[2] = { _sep, '\0' };
463 char **result = NULL;
464 size_t result_capacity = 0;
465 size_t result_count = 0;
466 int r, saved_errno;
467 size_t i = 0;
468
469 if (!string)
470 return calloc(1, sizeof(char *));
471
472 str = must_copy_string(string);
473 lxc_iterate_parts(token, str, sep) {
474 while (token[0] == ' ' || token[0] == '\t')
475 token++;
476
477 i = strlen(token);
478 while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) {
479 token[i - 1] = '\0';
480 i--;
481 }
482
483 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
484 if (r < 0)
485 goto error_out;
486
487 result[result_count] = strdup(token);
488 if (!result[result_count])
489 goto error_out;
490
491 result_count++;
492 }
493
494 /* if we allocated too much, reduce it */
495 return realloc(result, (result_count + 1) * sizeof(char *));
496
497 error_out:
498 saved_errno = errno;
499 lxc_free_array((void **)result, free);
500 errno = saved_errno;
501 return NULL;
502 }
503
504 void lxc_free_array(void **array, lxc_free_fn element_free_fn)
505 {
506 void **p;
507
508 for (p = array; p && *p; p++)
509 element_free_fn(*p);
510
511 free((void*)array);
512 }
513
514 int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, size_t capacity_increment)
515 {
516 size_t new_capacity;
517 void **new_array;
518
519 /* first time around, catch some trivial mistakes of the user
520 * only initializing one of these */
521 if (!*array || !*capacity) {
522 *array = NULL;
523 *capacity = 0;
524 }
525
526 new_capacity = *capacity;
527 while (new_size + 1 > new_capacity)
528 new_capacity += capacity_increment;
529
530 if (new_capacity != *capacity) {
531 /* we have to reallocate */
532 new_array = realloc(*array, new_capacity * sizeof(void *));
533 if (!new_array)
534 return -1;
535
536 memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *));
537 *array = new_array;
538 *capacity = new_capacity;
539 }
540
541 /* array has sufficient elements */
542 return 0;
543 }
544
545 size_t lxc_array_len(void **array)
546 {
547 void **p;
548 size_t result = 0;
549
550 for (p = array; p && *p; p++)
551 result++;
552
553 return result;
554 }
555
556 void **lxc_append_null_to_array(void **array, size_t count)
557 {
558 void **temp;
559
560 /* Append NULL to the array */
561 if (count) {
562 temp = realloc(array, (count + 1) * sizeof(*array));
563 if (!temp) {
564 size_t i;
565 for (i = 0; i < count; i++)
566 free(array[i]);
567 free(array);
568 return NULL;
569 }
570
571 array = temp;
572 array[count] = NULL;
573 }
574
575 return array;
576 }
577
578 static int lxc_append_null_to_list(void ***list)
579 {
580 int newentry = 0;
581 void **tmp;
582
583 if (*list)
584 for (; (*list)[newentry]; newentry++) {
585 ;
586 }
587
588 tmp = realloc(*list, (newentry + 2) * sizeof(void **));
589 if (!tmp)
590 return -1;
591
592 *list = tmp;
593 (*list)[newentry + 1] = NULL;
594
595 return newentry;
596 }
597
598 int lxc_append_string(char ***list, char *entry)
599 {
600 char *copy;
601 int newentry;
602
603 newentry = lxc_append_null_to_list((void ***)list);
604 if (newentry < 0)
605 return -1;
606
607 copy = strdup(entry);
608 if (!copy)
609 return -1;
610
611 (*list)[newentry] = copy;
612
613 return 0;
614 }
615
616 int lxc_safe_uint(const char *numstr, unsigned int *converted)
617 {
618 char *err = NULL;
619 unsigned long int uli;
620
621 while (isspace(*numstr))
622 numstr++;
623
624 if (*numstr == '-')
625 return -EINVAL;
626
627 errno = 0;
628 uli = strtoul(numstr, &err, 0);
629 if (errno == ERANGE && uli == ULONG_MAX)
630 return -ERANGE;
631
632 if (err == numstr || *err != '\0')
633 return -EINVAL;
634
635 if (uli > UINT_MAX)
636 return -ERANGE;
637
638 *converted = (unsigned int)uli;
639 return 0;
640 }
641
642 int lxc_safe_ulong(const char *numstr, unsigned long *converted)
643 {
644 char *err = NULL;
645 unsigned long int uli;
646
647 while (isspace(*numstr))
648 numstr++;
649
650 if (*numstr == '-')
651 return -EINVAL;
652
653 errno = 0;
654 uli = strtoul(numstr, &err, 0);
655 if (errno == ERANGE && uli == ULONG_MAX)
656 return -ERANGE;
657
658 if (err == numstr || *err != '\0')
659 return -EINVAL;
660
661 *converted = uli;
662 return 0;
663 }
664
665 int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base)
666 {
667 char *err = NULL;
668 uint64_t u;
669
670 while (isspace(*numstr))
671 numstr++;
672
673 if (*numstr == '-')
674 return -EINVAL;
675
676 errno = 0;
677 u = strtoull(numstr, &err, base);
678 if (errno == ERANGE && u == UINT64_MAX)
679 return -ERANGE;
680
681 if (err == numstr || *err != '\0')
682 return -EINVAL;
683
684 *converted = u;
685 return 0;
686 }
687
688 int lxc_safe_int(const char *numstr, int *converted)
689 {
690 char *err = NULL;
691 signed long int sli;
692
693 errno = 0;
694 sli = strtol(numstr, &err, 0);
695 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
696 return -ERANGE;
697
698 if (errno != 0 && sli == 0)
699 return -EINVAL;
700
701 if (err == numstr || *err != '\0')
702 return -EINVAL;
703
704 if (sli > INT_MAX || sli < INT_MIN)
705 return -ERANGE;
706
707 *converted = (int)sli;
708 return 0;
709 }
710
711 int lxc_safe_long(const char *numstr, long int *converted)
712 {
713 char *err = NULL;
714 signed long int sli;
715
716 errno = 0;
717 sli = strtol(numstr, &err, 0);
718 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
719 return -ERANGE;
720
721 if (errno != 0 && sli == 0)
722 return -EINVAL;
723
724 if (err == numstr || *err != '\0')
725 return -EINVAL;
726
727 *converted = sli;
728 return 0;
729 }
730
731 int lxc_safe_long_long(const char *numstr, long long int *converted)
732 {
733 char *err = NULL;
734 signed long long int sli;
735
736 errno = 0;
737 sli = strtoll(numstr, &err, 0);
738 if (errno == ERANGE && (sli == LLONG_MAX || sli == LLONG_MIN))
739 return -ERANGE;
740
741 if (errno != 0 && sli == 0)
742 return -EINVAL;
743
744 if (err == numstr || *err != '\0')
745 return -EINVAL;
746
747 *converted = sli;
748 return 0;
749 }
750
751 char *must_concat(const char *first, ...)
752 {
753 va_list args;
754 char *cur, *dest;
755 size_t cur_len, it_len;
756
757 dest = must_copy_string(first);
758 cur_len = it_len = strlen(first);
759
760 va_start(args, first);
761 while ((cur = va_arg(args, char *)) != NULL) {
762 it_len = strlen(cur);
763
764 dest = must_realloc(dest, cur_len + it_len + 1);
765
766 (void)memcpy(dest + cur_len, cur, it_len);
767 cur_len += it_len;
768 }
769 va_end(args);
770
771 dest[cur_len] = '\0';
772 return dest;
773 }
774
775 char *must_make_path(const char *first, ...)
776 {
777 va_list args;
778 char *cur, *dest;
779 size_t full_len = strlen(first);
780 size_t buf_len;
781 size_t cur_len;
782
783 dest = must_copy_string(first);
784 cur_len = full_len;
785
786 va_start(args, first);
787 while ((cur = va_arg(args, char *)) != NULL) {
788 buf_len = strlen(cur);
789
790 full_len += buf_len;
791 if (cur[0] != '/')
792 full_len++;
793
794 dest = must_realloc(dest, full_len + 1);
795
796 if (cur[0] != '/') {
797 memcpy(dest + cur_len, "/", 1);
798 cur_len++;
799 }
800
801 memcpy(dest + cur_len, cur, buf_len);
802 cur_len += buf_len;
803 }
804 va_end(args);
805
806 dest[cur_len] = '\0';
807 return dest;
808 }
809
810 char *must_append_path(char *first, ...)
811 {
812 char *cur;
813 size_t full_len;
814 va_list args;
815 char *dest = first;
816 size_t buf_len;
817 size_t cur_len;
818
819 full_len = strlen(first);
820 cur_len = full_len;
821
822 va_start(args, first);
823 while ((cur = va_arg(args, char *)) != NULL) {
824 buf_len = strlen(cur);
825
826 full_len += buf_len;
827 if (cur[0] != '/')
828 full_len++;
829
830 dest = must_realloc(dest, full_len + 1);
831
832 if (cur[0] != '/') {
833 memcpy(dest + cur_len, "/", 1);
834 cur_len++;
835 }
836
837 memcpy(dest + cur_len, cur, buf_len);
838 cur_len += buf_len;
839 }
840 va_end(args);
841
842 dest[cur_len] = '\0';
843 return dest;
844 }
845
846 char *must_copy_string(const char *entry)
847 {
848 char *ret;
849
850 if (!entry)
851 return NULL;
852
853 do {
854 ret = strdup(entry);
855 } while (!ret);
856
857 return ret;
858 }
859
860 void *must_realloc(void *orig, size_t sz)
861 {
862 void *ret;
863
864 do {
865 ret = realloc(orig, sz);
866 } while (!ret);
867
868 return ret;
869 }
870
871 int parse_byte_size_string(const char *s, int64_t *converted)
872 {
873 int ret, suffix_len;
874 long long int conv;
875 int64_t mltpl, overflow;
876 char *end;
877 char dup[INTTYPE_TO_STRLEN(int64_t)];
878 char suffix[3] = {0};
879
880 if (!s || !strcmp(s, ""))
881 return -EINVAL;
882
883 end = stpncpy(dup, s, sizeof(dup) - 1);
884 if (*end != '\0')
885 return -EINVAL;
886
887 if (isdigit(*(end - 1)))
888 suffix_len = 0;
889 else if (isalpha(*(end - 1)))
890 suffix_len = 1;
891 else
892 return -EINVAL;
893
894 if (suffix_len > 0 && (end - 2) == dup && !isdigit(*(end - 2)))
895 return -EINVAL;
896
897 if (suffix_len > 0 && isalpha(*(end - 2)))
898 suffix_len++;
899
900 if (suffix_len > 0) {
901 memcpy(suffix, end - suffix_len, suffix_len);
902 *(suffix + suffix_len) = '\0';
903 *(end - suffix_len) = '\0';
904 }
905 dup[lxc_char_right_gc(dup, strlen(dup))] = '\0';
906
907 ret = lxc_safe_long_long(dup, &conv);
908 if (ret < 0)
909 return -ret;
910
911 if (suffix_len != 2) {
912 *converted = conv;
913 return 0;
914 }
915
916 if (strcasecmp(suffix, "KB") == 0)
917 mltpl = 1024;
918 else if (strcasecmp(suffix, "MB") == 0)
919 mltpl = 1024 * 1024;
920 else if (strcasecmp(suffix, "GB") == 0)
921 mltpl = 1024 * 1024 * 1024;
922 else
923 return -EINVAL;
924
925 overflow = conv * mltpl;
926 if (conv != 0 && (overflow / conv) != mltpl)
927 return -ERANGE;
928
929 *converted = overflow;
930 return 0;
931 }
932
933 void remove_trailing_newlines(char *l)
934 {
935 char *p = l;
936
937 while (*p)
938 p++;
939
940 while (--p >= l && *p == '\n')
941 *p = '\0';
942 }
943
944 int lxc_char_left_gc(const char *buffer, size_t len)
945 {
946 size_t i;
947
948 for (i = 0; i < len; i++) {
949 if (buffer[i] == ' ' ||
950 buffer[i] == '\t')
951 continue;
952
953 return i;
954 }
955
956 return 0;
957 }
958
959 int lxc_char_right_gc(const char *buffer, size_t len)
960 {
961 int i;
962
963 for (i = len - 1; i >= 0; i--) {
964 if (buffer[i] == ' ' ||
965 buffer[i] == '\t' ||
966 buffer[i] == '\n' ||
967 buffer[i] == '\0')
968 continue;
969
970 return i + 1;
971 }
972
973 return 0;
974 }
975
976 char *lxc_trim_whitespace_in_place(char *buffer)
977 {
978 buffer += lxc_char_left_gc(buffer, strlen(buffer));
979 buffer[lxc_char_right_gc(buffer, strlen(buffer))] = '\0';
980 return buffer;
981 }
982
983 int lxc_is_line_empty(const char *line)
984 {
985 int i;
986 size_t len = strlen(line);
987
988 for (i = 0; i < len; i++)
989 if (line[i] != ' ' && line[i] != '\t' &&
990 line[i] != '\n' && line[i] != '\r' &&
991 line[i] != '\f' && line[i] != '\0')
992 return 0;
993 return 1;
994 }
995
996 void remove_trailing_slashes(char *p)
997 {
998 int l = strlen(p);
999 while (--l >= 0 && (p[l] == '/' || p[l] == '\n'))
1000 p[l] = '\0';
1001 }