]> git.ipfire.org Git - thirdparty/dracut.git/commitdiff
install: install all PATH binaries found
authorHarald Hoyer <harald@redhat.com>
Thu, 19 Dec 2013 15:59:46 +0000 (16:59 +0100)
committerHarald Hoyer <harald@redhat.com>
Thu, 19 Dec 2013 16:00:18 +0000 (17:00 +0100)
This should fix the issues with symlinks in /bin pointing to /usr/bin on
some distributions.

Makefile
install/dracut-install.c
install/strv.c [new file with mode: 0644]
install/strv.h [new file with mode: 0644]
install/util.c
install/util.h

index ba551cfae63ceb364098cd551550e9b7bcad7f32..fed5f48b56d903d8cd766317ff91f7912de0f236 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -47,6 +47,7 @@ DRACUT_INSTALL_OBJECTS = \
         install/dracut-install.o \
         install/hashmap.o\
         install/log.o \
+        install/strv.o \
         install/util.o
 
 # deps generated with gcc -MM
@@ -56,6 +57,7 @@ install/hashmap.o: install/hashmap.c install/util.h install/macro.h install/log.
        install/hashmap.h
 install/log.o: install/log.c install/log.h install/macro.h install/util.h
 install/util.o: install/util.c install/util.h install/macro.h install/log.h
+install/strv.o: install/strv.c install/strv.h install/util.h install/macro.h install/log.h
 
 install/dracut-install: $(DRACUT_INSTALL_OBJECTS)
 
index 6b9c613b8f741898884c4fb4789ac666978ff178..397aa621b0e8c9deb9e6a4789294f9e51cc9c8c3 100644 (file)
@@ -44,6 +44,7 @@
 #include "log.h"
 #include "hashmap.h"
 #include "util.h"
+#include "strv.h"
 
 static bool arg_hmac = false;
 static bool arg_createdir = false;
@@ -784,13 +785,13 @@ static int resolve_lazy(int argc, char **argv)
         return ret;
 }
 
-static char *find_binary(const char *src)
+static char **find_binary(const char *src)
 {
-        _cleanup_free_ char *path = NULL;
-        char *p, *q;
-        bool end = false;
+        char *path = NULL;
+        _cleanup_strv_free_ char **p = NULL;
+        char **ret = NULL;
+        char **q;
         char *newsrc = NULL;
-        int ret;
 
         path = getenv("PATH");
 
@@ -798,34 +799,21 @@ static char *find_binary(const char *src)
                 log_error("PATH is not set");
                 exit(EXIT_FAILURE);
         }
-        path = strdup(path);
-        p = path;
-
-        if (path == NULL) {
-                log_error("Out of memory!");
-                exit(EXIT_FAILURE);
-        }
 
         log_debug("PATH=%s", path);
 
-        do {
-                struct stat sb;
-
-                for (q = p; *q && *q != ':'; q++) ;
+        p = strv_split(path, ":");
 
-                if (*q == '\0')
-                        end = true;
-                else
-                        *q = '\0';
+        STRV_FOREACH(q, p) {
+                struct stat sb;
+                int r;
 
-                ret = asprintf(&newsrc, "%s/%s", p, src);
-                if (ret < 0) {
+                r = asprintf(&newsrc, "%s/%s", *q, src);
+                if (r < 0) {
                         log_error("Out of memory!");
                         exit(EXIT_FAILURE);
                 }
 
-                p = q + 1;
-
                 if (stat(newsrc, &sb) != 0) {
                         log_debug("stat(%s) != 0", newsrc);
                         free(newsrc);
@@ -833,30 +821,37 @@ static char *find_binary(const char *src)
                         continue;
                 }
 
-                end = true;
+                strv_push(&ret, newsrc);
 
-        } while (!end);
+        };
 
-        if (newsrc)
-                log_debug("find_binary(%s) == %s", src, newsrc);
+        if (ret) {
+                STRV_FOREACH(q, ret) {
+                        log_debug("find_binary(%s) == %s", src, *q);
+                }
+        }
 
-        return newsrc;
+        return ret;
 }
 
 static int install_one(const char *src, const char *dst)
 {
         int r = EXIT_SUCCESS;
-        int ret;
+        int ret = 0;
 
         if (strchr(src, '/') == NULL) {
-                char *newsrc = find_binary(src);
-                if (newsrc) {
-                        log_debug("dracut_install '%s' '%s'", newsrc, dst);
-                        ret = dracut_install(newsrc, dst, arg_createdir, arg_resolvedeps, true);
-                        if (ret == 0) {
-                                log_debug("dracut_install '%s' '%s' OK", newsrc, dst);
+                char **q = NULL;
+                char **p = find_binary(src);
+                if (p) {
+                        STRV_FOREACH(q, p) {
+                                char *newsrc = *q;
+                                log_debug("dracut_install '%s' '%s'", newsrc, dst);
+                                ret = dracut_install(newsrc, dst, arg_createdir, arg_resolvedeps, true);
+                                if (ret == 0) {
+                                        log_debug("dracut_install '%s' '%s' OK", newsrc, dst);
+                                }
                         }
-                        free(newsrc);
+                        strv_free(p);
                 } else {
                         ret = -1;
                 }
@@ -877,17 +872,22 @@ static int install_all(int argc, char **argv)
         int r = EXIT_SUCCESS;
         int i;
         for (i = 0; i < argc; i++) {
-                int ret;
+                int ret = 0;
                 log_debug("Handle '%s'", argv[i]);
 
                 if (strchr(argv[i], '/') == NULL) {
-                        _cleanup_free_ char *newsrc = find_binary(argv[i]);
-                        if (newsrc) {
-                                log_debug("dracut_install '%s'", newsrc);
-                                ret = dracut_install(newsrc, newsrc, arg_createdir, arg_resolvedeps, true);
-                                if (ret == 0) {
-                                        log_debug("dracut_install '%s' OK", newsrc);
+                        char **q = NULL;
+                        char **p = find_binary(argv[i]);
+                        if (p) {
+                                STRV_FOREACH(q, p) {
+                                        char *newsrc = *q;
+                                        log_debug("dracut_install '%s'", newsrc);
+                                        ret = dracut_install(newsrc, newsrc, arg_createdir, arg_resolvedeps, true);
+                                        if (ret == 0) {
+                                                log_debug("dracut_install '%s' OK", newsrc);
+                                        }
                                 }
+                                strv_free(p);
                         } else {
                                 ret = -1;
                         }
diff --git a/install/strv.c b/install/strv.c
new file mode 100644 (file)
index 0000000..152b70c
--- /dev/null
@@ -0,0 +1,587 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "strv.h"
+
+char *strv_find(char **l, const char *name) {
+        char **i;
+
+        assert(name);
+
+        STRV_FOREACH(i, l)
+                if (streq(*i, name))
+                        return *i;
+
+        return NULL;
+}
+
+char *strv_find_prefix(char **l, const char *name) {
+        char **i;
+
+        assert(name);
+
+        STRV_FOREACH(i, l)
+                if (startswith(*i, name))
+                        return *i;
+
+        return NULL;
+}
+
+void strv_free(char **l) {
+        char **k;
+
+        if (!l)
+                return;
+
+        for (k = l; *k; k++)
+                free(*k);
+
+        free(l);
+}
+
+char **strv_copy(char * const *l) {
+        char **r, **k;
+
+        k = r = new(char*, strv_length(l) + 1);
+        if (!r)
+                return NULL;
+
+        if (l)
+                for (; *l; k++, l++) {
+                        *k = strdup(*l);
+                        if (!*k) {
+                                strv_free(r);
+                                return NULL;
+                        }
+                }
+
+        *k = NULL;
+        return r;
+}
+
+unsigned strv_length(char * const *l) {
+        unsigned n = 0;
+
+        if (!l)
+                return 0;
+
+        for (; *l; l++)
+                n++;
+
+        return n;
+}
+
+char **strv_new_ap(const char *x, va_list ap) {
+        const char *s;
+        char **a;
+        unsigned n = 0, i = 0;
+        va_list aq;
+
+        /* As a special trick we ignore all listed strings that equal
+         * (const char*) -1. This is supposed to be used with the
+         * STRV_IFNOTNULL() macro to include possibly NULL strings in
+         * the string list. */
+
+        if (x) {
+                n = x == (const char*) -1 ? 0 : 1;
+
+                va_copy(aq, ap);
+                while ((s = va_arg(aq, const char*))) {
+                        if (s == (const char*) -1)
+                                continue;
+
+                        n++;
+                }
+
+                va_end(aq);
+        }
+
+        a = new(char*, n+1);
+        if (!a)
+                return NULL;
+
+        if (x) {
+                if (x != (const char*) -1) {
+                        a[i] = strdup(x);
+                        if (!a[i])
+                                goto fail;
+                        i++;
+                }
+
+                while ((s = va_arg(ap, const char*))) {
+
+                        if (s == (const char*) -1)
+                                continue;
+
+                        a[i] = strdup(s);
+                        if (!a[i])
+                                goto fail;
+
+                        i++;
+                }
+        }
+
+        a[i] = NULL;
+
+        return a;
+
+fail:
+        strv_free(a);
+        return NULL;
+}
+
+char **strv_new(const char *x, ...) {
+        char **r;
+        va_list ap;
+
+        va_start(ap, x);
+        r = strv_new_ap(x, ap);
+        va_end(ap);
+
+        return r;
+}
+
+char **strv_merge(char **a, char **b) {
+        char **r, **k;
+
+        if (!a)
+                return strv_copy(b);
+
+        if (!b)
+                return strv_copy(a);
+
+        r = new(char*, strv_length(a) + strv_length(b) + 1);
+        if (!r)
+                return NULL;
+
+        for (k = r; *a; k++, a++) {
+                *k = strdup(*a);
+                if (!*k)
+                        goto fail;
+        }
+
+        for (; *b; k++, b++) {
+                *k = strdup(*b);
+                if (!*k)
+                        goto fail;
+        }
+
+        *k = NULL;
+        return r;
+
+fail:
+        strv_free(r);
+        return NULL;
+}
+
+char **strv_merge_concat(char **a, char **b, const char *suffix) {
+        char **r, **k;
+
+        /* Like strv_merge(), but appends suffix to all strings in b, before adding */
+
+        if (!b)
+                return strv_copy(a);
+
+        r = new(char*, strv_length(a) + strv_length(b) + 1);
+        if (!r)
+                return NULL;
+
+        k = r;
+        if (a)
+                for (; *a; k++, a++) {
+                        *k = strdup(*a);
+                        if (!*k)
+                                goto fail;
+                }
+
+        for (; *b; k++, b++) {
+                *k = strappend(*b, suffix);
+                if (!*k)
+                        goto fail;
+        }
+
+        *k = NULL;
+        return r;
+
+fail:
+        strv_free(r);
+        return NULL;
+
+}
+
+char **strv_split(const char *s, const char *separator) {
+        char *state;
+        char *w;
+        size_t l;
+        unsigned n, i;
+        char **r;
+
+        assert(s);
+
+        n = 0;
+        FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
+                n++;
+
+        r = new(char*, n+1);
+        if (!r)
+                return NULL;
+
+        i = 0;
+        FOREACH_WORD_SEPARATOR(w, l, s, separator, state) {
+                r[i] = strndup(w, l);
+                if (!r[i]) {
+                        strv_free(r);
+                        return NULL;
+                }
+
+                i++;
+        }
+
+        r[i] = NULL;
+        return r;
+}
+
+char **strv_split_quoted(const char *s) {
+        char *state;
+        char *w;
+        size_t l;
+        unsigned n, i;
+        char **r;
+
+        assert(s);
+
+        n = 0;
+        FOREACH_WORD_QUOTED(w, l, s, state)
+                n++;
+
+        r = new(char*, n+1);
+        if (!r)
+                return NULL;
+
+        i = 0;
+        FOREACH_WORD_QUOTED(w, l, s, state) {
+                r[i] = cunescape_length(w, l);
+                if (!r[i]) {
+                        strv_free(r);
+                        return NULL;
+                }
+                i++;
+        }
+
+        r[i] = NULL;
+        return r;
+}
+
+char **strv_split_newlines(const char *s) {
+        char **l;
+        unsigned n;
+
+        assert(s);
+
+        /* Special version of strv_split() that splits on newlines and
+         * suppresses an empty string at the end */
+
+        l = strv_split(s, NEWLINE);
+        if (!l)
+                return NULL;
+
+        n = strv_length(l);
+        if (n <= 0)
+                return l;
+
+        if (isempty(l[n-1])) {
+                free(l[n-1]);
+                l[n-1] = NULL;
+        }
+
+        return l;
+}
+
+char *strv_join(char **l, const char *separator) {
+        char *r, *e;
+        char **s;
+        size_t n, k;
+
+        if (!separator)
+                separator = " ";
+
+        k = strlen(separator);
+
+        n = 0;
+        STRV_FOREACH(s, l) {
+                if (n != 0)
+                        n += k;
+                n += strlen(*s);
+        }
+
+        r = new(char, n+1);
+        if (!r)
+                return NULL;
+
+        e = r;
+        STRV_FOREACH(s, l) {
+                if (e != r)
+                        e = stpcpy(e, separator);
+
+                e = stpcpy(e, *s);
+        }
+
+        *e = 0;
+
+        return r;
+}
+
+char **strv_append(char **l, const char *s) {
+        char **r, **k;
+
+        if (!l)
+                return strv_new(s, NULL);
+
+        if (!s)
+                return strv_copy(l);
+
+        r = new(char*, strv_length(l)+2);
+        if (!r)
+                return NULL;
+
+        for (k = r; *l; k++, l++) {
+                *k = strdup(*l);
+                if (!*k)
+                        goto fail;
+        }
+
+        k[0] = strdup(s);
+        if (!k[0])
+                goto fail;
+
+        k[1] = NULL;
+        return r;
+
+fail:
+        strv_free(r);
+        return NULL;
+}
+
+int strv_push(char ***l, char *value) {
+        char **c;
+        unsigned n;
+
+        if (!value)
+                return 0;
+
+        n = strv_length(*l);
+        c = realloc(*l, sizeof(char*) * (n + 2));
+        if (!c)
+                return -ENOMEM;
+
+        c[n] = value;
+        c[n+1] = NULL;
+
+        *l = c;
+        return 0;
+}
+
+int strv_extend(char ***l, const char *value) {
+        char *v;
+        int r;
+
+        if (!value)
+                return 0;
+
+        v = strdup(value);
+        if (!v)
+                return -ENOMEM;
+
+        r = strv_push(l, v);
+        if (r < 0)
+                free(v);
+
+        return r;
+}
+
+char **strv_uniq(char **l) {
+        char **i;
+
+        /* Drops duplicate entries. The first identical string will be
+         * kept, the others dropped */
+
+        STRV_FOREACH(i, l)
+                strv_remove(i+1, *i);
+
+        return l;
+}
+
+char **strv_remove(char **l, const char *s) {
+        char **f, **t;
+
+        if (!l)
+                return NULL;
+
+        assert(s);
+
+        /* Drops every occurrence of s in the string list, edits
+         * in-place. */
+
+        for (f = t = l; *f; f++) {
+
+                if (streq(*f, s)) {
+                        free(*f);
+                        continue;
+                }
+
+                *(t++) = *f;
+        }
+
+        *t = NULL;
+        return l;
+}
+
+char **strv_remove_prefix(char **l, const char *s) {
+        char **f, **t;
+
+        if (!l)
+                return NULL;
+
+        assert(s);
+
+        /* Drops every occurrence of a string prefixed with s in the
+         * string list, edits in-place. */
+
+        for (f = t = l; *f; f++) {
+
+                if (startswith(*f, s)) {
+                        free(*f);
+                        continue;
+                }
+
+                *(t++) = *f;
+        }
+
+        *t = NULL;
+        return l;
+}
+
+char **strv_parse_nulstr(const char *s, size_t l) {
+        const char *p;
+        unsigned c = 0, i = 0;
+        char **v;
+
+        assert(s || l <= 0);
+
+        if (l <= 0)
+                return new0(char*, 1);
+
+        for (p = s; p < s + l; p++)
+                if (*p == 0)
+                        c++;
+
+        if (s[l-1] != 0)
+                c++;
+
+        v = new0(char*, c+1);
+        if (!v)
+                return NULL;
+
+        p = s;
+        while (p < s + l) {
+                const char *e;
+
+                e = memchr(p, 0, s + l - p);
+
+                v[i] = strndup(p, e ? e - p : s + l - p);
+                if (!v[i]) {
+                        strv_free(v);
+                        return NULL;
+                }
+
+                i++;
+
+                if (!e)
+                        break;
+
+                p = e + 1;
+        }
+
+        assert(i == c);
+
+        return v;
+}
+
+char **strv_split_nulstr(const char *s) {
+        const char *i;
+        char **r = NULL;
+
+        NULSTR_FOREACH(i, s)
+                if (strv_extend(&r, i) < 0) {
+                        strv_free(r);
+                        return NULL;
+                }
+
+        if (!r)
+                return strv_new(NULL, NULL);
+
+        return r;
+}
+
+bool strv_overlap(char **a, char **b) {
+        char **i, **j;
+
+        STRV_FOREACH(i, a) {
+                STRV_FOREACH(j, b) {
+                        if (streq(*i, *j))
+                                return true;
+                }
+        }
+
+        return false;
+}
+
+static int str_compare(const void *_a, const void *_b) {
+        const char **a = (const char**) _a, **b = (const char**) _b;
+
+        return strcmp(*a, *b);
+}
+
+char **strv_sort(char **l) {
+
+        if (strv_isempty(l))
+                return l;
+
+        qsort(l, strv_length(l), sizeof(char*), str_compare);
+        return l;
+}
+
+void strv_print(char **l) {
+        char **s;
+
+        if (!l)
+                return;
+
+        STRV_FOREACH(s, l)
+                puts(*s);
+}
diff --git a/install/strv.h b/install/strv.h
new file mode 100644 (file)
index 0000000..193f1dd
--- /dev/null
@@ -0,0 +1,118 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "util.h"
+
+char *strv_find(char **l, const char *name) _pure_;
+char *strv_find_prefix(char **l, const char *name) _pure_;
+
+void strv_free(char **l);
+DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
+#define _cleanup_strv_free_ _cleanup_(strv_freep)
+
+char **strv_copy(char * const *l);
+unsigned strv_length(char * const *l) _pure_;
+
+char **strv_merge(char **a, char **b);
+char **strv_merge_concat(char **a, char **b, const char *suffix);
+char **strv_append(char **l, const char *s);
+int strv_extend(char ***l, const char *value);
+int strv_push(char ***l, char *value);
+
+char **strv_remove(char **l, const char *s);
+char **strv_remove_prefix(char **l, const char *s);
+char **strv_uniq(char **l);
+
+#define strv_contains(l, s) (!!strv_find((l), (s)))
+
+char **strv_new(const char *x, ...) _sentinel_;
+char **strv_new_ap(const char *x, va_list ap);
+
+static inline const char* STRV_IFNOTNULL(const char *x) {
+        return x ? x : (const char *) -1;
+}
+
+static inline bool strv_isempty(char * const *l) {
+        return !l || !*l;
+}
+
+char **strv_split(const char *s, const char *separator);
+char **strv_split_quoted(const char *s);
+char **strv_split_newlines(const char *s);
+
+char *strv_join(char **l, const char *separator);
+
+char **strv_parse_nulstr(const char *s, size_t l);
+char **strv_split_nulstr(const char *s);
+
+bool strv_overlap(char **a, char **b) _pure_;
+
+#define STRV_FOREACH(s, l)                      \
+        for ((s) = (l); (s) && *(s); (s)++)
+
+#define STRV_FOREACH_BACKWARDS(s, l)            \
+        STRV_FOREACH(s, l)                      \
+                ;                               \
+        for ((s)--; (l) && ((s) >= (l)); (s)--)
+
+#define STRV_FOREACH_PAIR(x, y, l)               \
+        for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
+
+char **strv_sort(char **l);
+void strv_print(char **l);
+
+#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
+
+#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
+
+#define strv_from_stdarg_alloca(first)                          \
+        ({                                                      \
+                char **_l;                                      \
+                                                                \
+                if (!first)                                     \
+                        _l = (char**) &first;                   \
+                else {                                          \
+                        unsigned _n;                            \
+                        va_list _ap;                            \
+                                                                \
+                        _n = 1;                                 \
+                        va_start(_ap, first);                   \
+                        while (va_arg(_ap, char*))              \
+                                _n++;                           \
+                        va_end(_ap);                            \
+                                                                \
+                        _l = newa(char*, _n+1);                 \
+                        _l[_n = 0] = (char*) first;             \
+                        va_start(_ap, first);                   \
+                        for (;;) {                              \
+                                _l[++_n] = va_arg(_ap, char*);  \
+                                if (!_l[_n])                    \
+                                        break;                  \
+                        }                                       \
+                        va_end(_ap);                            \
+                }                                               \
+                _l;                                             \
+        })
index 9aa131f8ecdeb42262be80f8c63430ce6fd51188..c28db764a97337106ebf4e6d6a0c4b4f8f66ec85 100644 (file)
@@ -277,3 +277,238 @@ char *strjoin(const char *x, ...) {
 
         return r;
 }
+
+char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
+        char *r, *t;
+        const char *f;
+        size_t pl;
+
+        assert(s);
+
+        /* Undoes C style string escaping, and optionally prefixes it. */
+
+        pl = prefix ? strlen(prefix) : 0;
+
+        r = new(char, pl+length+1);
+        if (!r)
+                return r;
+
+        if (prefix)
+                memcpy(r, prefix, pl);
+
+        for (f = s, t = r + pl; f < s + length; f++) {
+
+                if (*f != '\\') {
+                        *(t++) = *f;
+                        continue;
+                }
+
+                f++;
+
+                switch (*f) {
+
+                case 'a':
+                        *(t++) = '\a';
+                        break;
+                case 'b':
+                        *(t++) = '\b';
+                        break;
+                case 'f':
+                        *(t++) = '\f';
+                        break;
+                case 'n':
+                        *(t++) = '\n';
+                        break;
+                case 'r':
+                        *(t++) = '\r';
+                        break;
+                case 't':
+                        *(t++) = '\t';
+                        break;
+                case 'v':
+                        *(t++) = '\v';
+                        break;
+                case '\\':
+                        *(t++) = '\\';
+                        break;
+                case '"':
+                        *(t++) = '"';
+                        break;
+                case '\'':
+                        *(t++) = '\'';
+                        break;
+
+                case 's':
+                        /* This is an extension of the XDG syntax files */
+                        *(t++) = ' ';
+                        break;
+
+                case 'x': {
+                        /* hexadecimal encoding */
+                        int a, b;
+
+                        a = unhexchar(f[1]);
+                        b = unhexchar(f[2]);
+
+                        if (a < 0 || b < 0) {
+                                /* Invalid escape code, let's take it literal then */
+                                *(t++) = '\\';
+                                *(t++) = 'x';
+                        } else {
+                                *(t++) = (char) ((a << 4) | b);
+                                f += 2;
+                        }
+
+                        break;
+                }
+
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7': {
+                        /* octal encoding */
+                        int a, b, c;
+
+                        a = unoctchar(f[0]);
+                        b = unoctchar(f[1]);
+                        c = unoctchar(f[2]);
+
+                        if (a < 0 || b < 0 || c < 0) {
+                                /* Invalid escape code, let's take it literal then */
+                                *(t++) = '\\';
+                                *(t++) = f[0];
+                        } else {
+                                *(t++) = (char) ((a << 6) | (b << 3) | c);
+                                f += 2;
+                        }
+
+                        break;
+                }
+
+                case 0:
+                        /* premature end of string.*/
+                        *(t++) = '\\';
+                        goto finish;
+
+                default:
+                        /* Invalid escape code, let's take it literal then */
+                        *(t++) = '\\';
+                        *(t++) = *f;
+                        break;
+                }
+        }
+
+finish:
+        *t = 0;
+        return r;
+}
+
+char *cunescape_length(const char *s, size_t length) {
+        return cunescape_length_with_prefix(s, length, NULL);
+}
+
+
+/* Split a string into words, but consider strings enclosed in '' and
+ * "" as words even if they include spaces. */
+char *split_quoted(const char *c, size_t *l, char **state) {
+        const char *current, *e;
+        bool escaped = false;
+
+        assert(c);
+        assert(l);
+        assert(state);
+
+        current = *state ? *state : c;
+
+        current += strspn(current, WHITESPACE);
+
+        if (*current == 0)
+                return NULL;
+
+        else if (*current == '\'') {
+                current ++;
+
+                for (e = current; *e; e++) {
+                        if (escaped)
+                                escaped = false;
+                        else if (*e == '\\')
+                                escaped = true;
+                        else if (*e == '\'')
+                                break;
+                }
+
+                *l = e-current;
+                *state = (char*) (*e == 0 ? e : e+1);
+
+        } else if (*current == '\"') {
+                current ++;
+
+                for (e = current; *e; e++) {
+                        if (escaped)
+                                escaped = false;
+                        else if (*e == '\\')
+                                escaped = true;
+                        else if (*e == '\"')
+                                break;
+                }
+
+                *l = e-current;
+                *state = (char*) (*e == 0 ? e : e+1);
+
+        } else {
+                for (e = current; *e; e++) {
+                        if (escaped)
+                                escaped = false;
+                        else if (*e == '\\')
+                                escaped = true;
+                        else if (strchr(WHITESPACE, *e))
+                                break;
+                }
+                *l = e-current;
+                *state = (char*) e;
+        }
+
+        return (char*) current;
+}
+
+/* Split a string into words. */
+char *split(const char *c, size_t *l, const char *separator, char **state) {
+        char *current;
+
+        current = *state ? *state : (char*) c;
+
+        if (!*current || *c == 0)
+                return NULL;
+
+        current += strspn(current, separator);
+        *l = strcspn(current, separator);
+        *state = current+*l;
+
+        return (char*) current;
+}
+
+int unhexchar(char c) {
+
+        if (c >= '0' && c <= '9')
+                return c - '0';
+
+        if (c >= 'a' && c <= 'f')
+                return c - 'a' + 10;
+
+        if (c >= 'A' && c <= 'F')
+                return c - 'A' + 10;
+
+        return -1;
+}
+
+int unoctchar(char c) {
+
+        if (c >= '0' && c <= '7')
+                return c - '0';
+
+        return -1;
+}
index 3d7c23204462b76983d465a03d7bf854ef90d745..4820842725fee4fa141602bf94bdf06e4c799dff 100644 (file)
@@ -134,8 +134,16 @@ static inline bool isempty(const char *p) {
         return !p || !p[0];
 }
 
+
+static inline const char *startswith(const char *s, const char *prefix) {
+        if (strncmp(s, prefix, strlen(prefix)) == 0)
+                return s + strlen(prefix);
+        return NULL;
+}
+
 bool endswith(const char *s, const char *postfix);
-bool startswith(const char *s, const char *prefix);
+
+
 bool startswith_no_case(const char *s, const char *prefix);
 
 bool first_word(const char *s, const char *word);
@@ -562,4 +570,16 @@ void warn_melody(void);
 
 char *strjoin(const char *x, ...) _sentinel_;
 
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)                 \
+        static inline void func##p(type *p) {                   \
+                if (*p)                                         \
+                        func(*p);                               \
+        }                                                       \
+        struct __useless_struct_to_allow_trailing_semicolon__
+
+char *split_quoted(const char *c, size_t *l, char **state);
+char *cunescape_length(const char *s, size_t length);
+int unhexchar(char c) _const_;
+int unoctchar(char c) _const_;
+
 #endif