]> err.no Git - util-linux/commitdiff
libmount: add mnt_optstr_* functions
authorKarel Zak <kzak@redhat.com>
Mon, 4 Jan 2010 22:03:47 +0000 (23:03 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 3 Jun 2010 13:20:10 +0000 (15:20 +0200)
This set of functions allows to set/get/remove/append option in
unparsed options string.

Signed-off-by: Karel Zak <kzak@redhat.com>
shlibs/mount/src/Makefile.am
shlibs/mount/src/mount.h.in
shlibs/mount/src/mountP.h
shlibs/mount/src/optstr.c [new file with mode: 0644]

index 598296316fc5b76e6fec08f42f28e937235e4377..e9c7cc9236f75571a5c6c34a301f2305bc4fc9fb 100644 (file)
@@ -12,6 +12,7 @@ usrlib_exec_LTLIBRARIES = libmount.la
 libmount_la_SOURCES = $(mountinc_HEADERS)
 
 nodist_libmount_la_SOURCES = mount.h version.c utils.c test.c init.c cache.c \
+                       optstr.c \
                        $(top_srcdir)/lib/canonicalize.c
 
 libmount_la_LIBADD = $(ul_libblkid_la)
@@ -21,7 +22,7 @@ libmount_la_DEPENDENCIES = $(libmount_la_LIBADD) mount.sym mount.h.in
 libmount_la_LDFLAGS = -Wl,--version-script=$(ul_libmount_srcdir)/mount.sym \
                       -version-info $(LIBMOUNT_VERSION_INFO)
 
-tests = test_version test_cache
+tests = test_version test_cache test_optstr
 
 EXTRA_DIST = mount.sym mount.h.in
 CLEANFILES = $(tests)
index e0f95807ec6c33f0dc50598cda3d951872f2a9bc..3426729bf8ba37a14b9b97aefa87843f08697afa 100644 (file)
@@ -56,6 +56,17 @@ extern char *mnt_resolve_path(const char *path, mnt_cache *cache);
 extern char *mnt_resolve_tag(const char *token, const char *value, mnt_cache *cache);
 extern char *mnt_resolve_spec(const char *spec, mnt_cache *cache);
 
+/* optstr.c */
+extern int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz,
+                               char **value, size_t *valuesz);
+extern int mnt_optstr_append_option(char **optstr, const char *name,
+                               const char *value);
+extern int mnt_optstr_get_option(char *optstr, const char *name,
+                               char **value, size_t *valsz);
+extern int mnt_optstr_set_option(char **optstr, const char *name,
+                               const char *value);
+extern int mnt_optstr_remove_option(char **optstr, const char *name);
+
 #ifdef __cplusplus
 }
 #endif
index 21f3e67ed416be8de0acb07e857d4393c591cdfd..74db25568a845bd20f74809b6f56f3c7d9053d6e 100644 (file)
@@ -31,6 +31,7 @@
 
 #define DEBUG_INIT     (1 << 1)
 #define DEBUG_CACHE    (1 << 2)
+#define DEBUG_OPTIONS  (1 << 3)
 #define DEBUG_ALL      0xFFFF
 
 #ifdef CONFIG_LIBMOUNT_DEBUG
diff --git a/shlibs/mount/src/optstr.c b/shlibs/mount/src/optstr.c
new file mode 100644 (file)
index 0000000..3e4eb48
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "nls.h"
+#include "mountP.h"
+
+/*
+ * Parses the first option from @optstr. The @optstr pointer is set to begin of
+ * the next option.
+ *
+ * Returns -1 on parse error, 1 at the end of optstr and 0 on success.
+ */
+static int mnt_optstr_parse_next(char **optstr,         char **name, size_t *namesz,
+                                       char **value, size_t *valsz)
+{
+       int open_quote = 0;
+       char *start = NULL, *stop = NULL, *p, *sep = NULL;
+       char *optstr0;
+
+       assert(optstr);
+       assert(*optstr);
+
+       optstr0 = *optstr;
+
+       if (name)
+               *name = NULL;
+       if (namesz)
+               *namesz = 0;
+       if (value)
+               *value = NULL;
+       if (valsz)
+               *valsz = 0;
+
+       for (p = optstr0; p && *p; p++) {
+               if (!start)
+                       start = p;              /* begin of the option item */
+               if (*p == '"')
+                       open_quote ^= 1;        /* reverse the status */
+               if (open_quote)
+                       continue;               /* still in quoted block */
+               if (!sep && *p == '=')
+                       sep = p;                /* name and value separator */
+               if (*p == ',')
+                       stop = p;               /* terminate the option item */
+               else if (*(p + 1) == '\0')
+                       stop = p + 1;           /* end of optstr */
+               if (!start || !stop)
+                       continue;
+               if (stop <= start)
+                       goto error;
+
+               if (name)
+                       *name = start;
+               if (namesz)
+                       *namesz = sep ? sep - start : stop - start;
+               *optstr = *stop ? stop + 1 : stop;
+
+               if (sep) {
+                       if (value)
+                               *value = sep + 1;
+                       if (valsz)
+                               *valsz = stop - sep - 1;
+               }
+               return 0;
+       }
+
+       return 1;                               /* end of optstr */
+
+error:
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+                       "libmount: parse error: \"%s\"\n", optstr0));
+       return -1;
+}
+
+/*
+ * Locates the first option that match with @name. The @end is set to
+ * char behind the option (it means ',' or \0).
+ *
+ * Returns -1 on parse error, 1 when not found and 0 on success.
+ */
+static int mnt_optstr_locate_option(char *optstr, const char *name, char **begin,
+                                       char **end, char **value, size_t *valsz)
+{
+       char *n;
+       size_t namesz, nsz;
+       int rc;
+
+       assert(name);
+       assert(optstr);
+
+       namesz = strlen(name);
+
+       do {
+               rc = mnt_optstr_parse_next(&optstr, &n, &nsz, value, valsz);
+               if (rc)
+                       break;
+
+               if (namesz == nsz && strncmp(n, name, nsz) == 0) {
+                       if (begin)
+                               *begin = n;
+                       if (end)
+                               *end = *(optstr - 1) == ',' ?
+                                       optstr - 1 : optstr;
+                       return 0;
+               }
+       } while(1);
+
+       DBG(DEBUG_OPTIONS, fprintf(stderr,
+                       "libmount: can't found '%s' option\n", name));
+       return rc;
+}
+
+/**
+ * mnt_optstr_next_option:
+ * @optstr: option string, returns position to next option
+ * @name: returns option name
+ * @namesz: returns option name length
+ * @value: returns option value or NULL
+ * @valuesz: returns option value length or zero
+ *
+ * Parses the first option in @optstr  or -1 in case of error.
+ *
+ * Returns 0 on success, 1 at the end of @optstr or -1 in case of error.
+ */
+int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz,
+                                       char **value, size_t *valuesz)
+{
+       if (!optstr || !*optstr)
+               return -1;
+       return mnt_optstr_parse_next(optstr, name, namesz, value, valuesz);
+}
+
+/**
+ * mnt_optstr_append_option:
+ * @optstr: option string or NULL
+ * @name: value name
+ * @value: value
+ *
+ * Returns: reallocated (or newly allocated) @optstr with ,name=value
+ */
+int mnt_optstr_append_option(char **optstr, const char *name, const char *value)
+{
+       char *p;
+       size_t sz, vsz, osz, nsz;
+
+       if (!name)
+               return -1;
+
+       osz = *optstr ? strlen(*optstr) : 0;
+       nsz = strlen(name);
+       vsz = value ? strlen(value) : 0;
+
+       sz = osz + nsz + 1;             /* 1: '\0' */
+       if (osz)
+               sz++;                   /* ',' options separator */
+       if (vsz)
+               sz += vsz + 1;          /* 1: '=' */
+
+       p = realloc(*optstr, sz);
+       if (!p)
+               return -1;
+       *optstr = p;
+
+       if (osz) {
+               p += osz;
+               *p++ = ',';
+       }
+
+       memcpy(p, name, nsz);
+       p += nsz;
+
+       if (vsz) {
+               *p++ = '=';
+               memcpy(p, value, vsz);
+               p += vsz;
+       }
+       *p = '\0';
+
+       return 0;
+}
+
+/**
+ * mnt_optstr_get_option:
+ * @optstr: string with comma separated list of options
+ * @name: requested option name
+ * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL
+ * @valsz: returns size of the value or 0
+ *
+ * Returns: 0 on success, 1 when not found the @name or -1 in case of error.
+ */
+int mnt_optstr_get_option(char *optstr, const char *name,
+                               char **value, size_t *valsz)
+{
+       return mnt_optstr_locate_option(optstr, name, NULL, NULL, value, valsz);
+}
+
+/* Removes substring located between @begin and @end from @str
+ * -- result never starts or ends with comma or contains two commas
+ *    (e.g. ",aaa,bbb" or "aaa,,bbb" or "aaa,")
+ */
+static void remove_substring(char *str, char *begin, char *end)
+{
+       size_t sz = strlen(end);
+
+       if ((begin == str || *(begin - 1) == ',') && *end == ',')
+               end++;
+
+       memmove(begin, end, sz + 1);
+       if (!*begin && *(begin - 1) == ',')
+               *(begin - 1) = '\0';
+}
+
+/* insert '=substr' to @str on position @pos */
+static int insert_substring(char **str, char *pos, const char *substr)
+{
+       char *p;
+       size_t ssz = strlen(substr);    /* substring size */
+
+       p = realloc(*str, strlen(*str) + 1 + ssz);
+       if (!p)
+               return -1;
+       *str = p;
+
+       memmove(pos + ssz + 1, pos, strlen(pos) + 1);
+       *pos++ = '=';
+       memcpy(pos, substr, ssz);
+       return 0;
+}
+
+/**
+ * mnt_optstr_set_option:
+ * @optstr: string with comma separated list of options
+ * @name: requested option
+ * @value: new value or NULL
+ *
+ * Set or unset option @value.
+ *
+ * Returns: 0 on success, 1 when not found the @name or -1 in case of error.
+ */
+int mnt_optstr_set_option(char **optstr, const char *name, const char *value)
+{
+       char *val = NULL, *begin, *end, *nameend;
+       size_t valsz;
+       int rc = 1;
+
+       if (!optstr)
+               return -1;
+       if (*optstr)
+               rc = mnt_optstr_locate_option(*optstr, name,
+                                       &begin, &end, &val, &valsz);
+       if (rc == -1)
+               /* parse error */
+               return -1;
+       if (rc == 1)
+               /* not found */
+               return mnt_optstr_append_option(optstr, name, value);
+
+       nameend = begin + strlen(name);
+
+       if (value == NULL && val && valsz)
+               /* remove unwanted "=value" */
+               remove_substring(*optstr, nameend, end);
+
+       else if (value && val == NULL)
+               /* insert "=value" */
+               rc = insert_substring(optstr, nameend, value);
+
+       else if (value && val && strlen(value) == valsz)
+               /* simply replace =value */
+               memcpy(val, value, valsz);
+
+       else if (value && val) {
+               remove_substring(*optstr, nameend, end);
+               rc = insert_substring(optstr, nameend, value);
+       }
+
+
+       return 0;
+}
+
+/**
+ * mnt_optstr_remove_option:
+ * @optstr: string with comma separated list of options
+ * @name: requested option name
+ *
+ * Returns: 0 on success, 1 when not found the @name or -1 in case of error.
+ */
+int mnt_optstr_remove_option(char **optstr, const char *name)
+{
+       char *begin, *end;
+       int rc;
+
+       rc = mnt_optstr_locate_option(*optstr, name,
+                               &begin, &end, NULL, NULL);
+       if (rc != 0)
+               return rc;
+
+       remove_substring(*optstr, begin, end);
+       return 0;
+}
+
+#ifdef TEST_PROGRAM
+
+int test_append(struct mtest *ts, int argc, char *argv[])
+{
+       const char *value = NULL, *name;
+       char *optstr;
+
+       if (argc < 3)
+               goto done;
+       optstr = strdup(argv[1]);
+       name = argv[2];
+
+       if (argc == 4)
+               value = argv[3];
+
+       if (mnt_optstr_append_option(&optstr, name, value) == 0) {
+               printf("result: >%s<\n", optstr);
+               return 0;
+       }
+done:
+       return -1;
+}
+
+int test_set(struct mtest *ts, int argc, char *argv[])
+{
+       const char *value = NULL, *name;
+       char *optstr;
+
+       if (argc < 3)
+               goto done;
+       optstr = strdup(argv[1]);
+       name = argv[2];
+
+       if (argc == 4)
+               value = argv[3];
+
+       if (mnt_optstr_set_option(&optstr, name, value) == 0) {
+               printf("result: >%s<\n", optstr);
+               return 0;
+       }
+done:
+       return -1;
+}
+
+int test_get(struct mtest *ts, int argc, char *argv[])
+{
+       char *optstr;
+       const char *name;
+       char *val = NULL;
+       size_t sz = 0;
+       int rc;
+
+       if (argc < 2)
+               goto done;
+       optstr = argv[1];
+       name = argv[2];
+
+       rc = mnt_optstr_get_option(optstr, name, &val, &sz);
+       if (rc == 0) {
+               printf("found; name: %s", name);
+               if (sz) {
+                       printf(", argument: size=%zd data=", sz);
+                       if (fwrite(val, 1, sz, stdout) != sz)
+                               goto done;
+               }
+               printf("\n");
+               return 0;
+       } else if (rc == 1)
+               printf("%s: not found\n", name);
+       else
+               printf("parse error: %s\n", optstr);
+done:
+       return -1;
+}
+
+int test_remove(struct mtest *ts, int argc, char *argv[])
+{
+       const char *name;
+       char *optstr;
+
+       if (argc < 3)
+               goto done;
+       optstr = strdup(argv[1]);
+       name = argv[2];
+
+       if (mnt_optstr_remove_option(&optstr, name) == 0) {
+               printf("result: >%s<\n", optstr);
+               return 0;
+       }
+done:
+       return -1;
+}
+
+
+int main(int argc, char *argv[])
+{
+       struct mtest tss[] = {
+               { "--append", test_append, "<optstr> <name> [<value>]  append value to optstr" },
+               { "--set",    test_set,    "<optstr> <name> [<value>]  (un)set value" },
+               { "--get",    test_get,    "<optstr> <name>            search name in optstr" },
+               { "--remove", test_remove, "<optstr> <name>            remove name in optstr" },
+               { NULL }
+       };
+       return  mnt_run_test(tss, argc, argv);
+}
+#endif /* TEST_PROGRAM */