From 9e272c68f51fa4e1fd0f6832bea9f82d6bceb007 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 4 Jan 2010 23:03:47 +0100 Subject: [PATCH] libmount: add mnt_optstr_* functions This set of functions allows to set/get/remove/append option in unparsed options string. Signed-off-by: Karel Zak --- shlibs/mount/src/Makefile.am | 3 +- shlibs/mount/src/mount.h.in | 11 + shlibs/mount/src/mountP.h | 1 + shlibs/mount/src/optstr.c | 416 +++++++++++++++++++++++++++++++++++ 4 files changed, 430 insertions(+), 1 deletion(-) create mode 100644 shlibs/mount/src/optstr.c diff --git a/shlibs/mount/src/Makefile.am b/shlibs/mount/src/Makefile.am index 59829631..e9c7cc92 100644 --- a/shlibs/mount/src/Makefile.am +++ b/shlibs/mount/src/Makefile.am @@ -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) diff --git a/shlibs/mount/src/mount.h.in b/shlibs/mount/src/mount.h.in index e0f95807..3426729b 100644 --- a/shlibs/mount/src/mount.h.in +++ b/shlibs/mount/src/mount.h.in @@ -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 diff --git a/shlibs/mount/src/mountP.h b/shlibs/mount/src/mountP.h index 21f3e67e..74db2556 100644 --- a/shlibs/mount/src/mountP.h +++ b/shlibs/mount/src/mountP.h @@ -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 index 00000000..3e4eb482 --- /dev/null +++ b/shlibs/mount/src/optstr.c @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2008-2009 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include +#include +#include +#include + +#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, " [] append value to optstr" }, + { "--set", test_set, " [] (un)set value" }, + { "--get", test_get, " search name in optstr" }, + { "--remove", test_remove, " remove name in optstr" }, + { NULL } + }; + return mnt_run_test(tss, argc, argv); +} +#endif /* TEST_PROGRAM */ -- 2.39.5