From 799757ccf1d03c33c75bc597cd5ef77741dcb6a7 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 3 Jun 2011 09:17:04 +0000 Subject: Imported upstream 4.91 --- src/textfile.c | 492 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 492 insertions(+) create mode 100644 src/textfile.c (limited to 'src/textfile.c') diff --git a/src/textfile.c b/src/textfile.c new file mode 100644 index 0000000..d115ff6 --- /dev/null +++ b/src/textfile.c @@ -0,0 +1,492 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "textfile.h" + +int create_dirs(const char *filename, const mode_t mode) +{ + struct stat st; + char dir[PATH_MAX + 1], *prev, *next; + int err; + + err = stat(filename, &st); + if (!err && S_ISREG(st.st_mode)) + return 0; + + memset(dir, 0, PATH_MAX + 1); + strcat(dir, "/"); + + prev = strchr(filename, '/'); + + while (prev) { + next = strchr(prev + 1, '/'); + if (!next) + break; + + if (next - prev == 1) { + prev = next; + continue; + } + + strncat(dir, prev + 1, next - prev); + mkdir(dir, mode); + + prev = next; + } + + return 0; +} + +int create_file(const char *filename, const mode_t mode) +{ + int fd; + + umask(S_IWGRP | S_IWOTH); + create_dirs(filename, S_IRUSR | S_IWUSR | S_IXUSR | + S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + + fd = open(filename, O_RDWR | O_CREAT, mode); + if (fd < 0) + return fd; + + close(fd); + + return 0; +} + +int create_name(char *buf, size_t size, const char *path, const char *address, const char *name) +{ + return snprintf(buf, size, "%s/%s/%s", path, address, name); +} + +static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase) +{ + char *ptr = map; + size_t ptrlen = size; + + while (ptrlen > len + 1) { + int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len); + if (cmp == 0) { + if (ptr == map) + return ptr; + + if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') && + *(ptr + len) == ' ') + return ptr; + } + + if (icase) { + char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1); + char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1); + + if (!p1) + ptr = p2; + else if (!p2) + ptr = p1; + else + ptr = (p1 < p2) ? p1 : p2; + } else + ptr = memchr(ptr + 1, *key, ptrlen - 1); + + if (!ptr) + return NULL; + + ptrlen = size - (ptr - map); + } + + return NULL; +} + +static inline int write_key_value(int fd, const char *key, const char *value) +{ + char *str; + size_t size; + int err = 0; + + size = strlen(key) + strlen(value) + 2; + + str = malloc(size + 1); + if (!str) + return ENOMEM; + + sprintf(str, "%s %s\n", key, value); + + if (write(fd, str, size) < 0) + err = errno; + + free(str); + + return err; +} + +static char *strnpbrk(const char *s, ssize_t len, const char *accept) +{ + const char *p = s; + const char *end; + + end = s + len - 1; + + while (p <= end && *p) { + const char *a = accept; + + while (*a) { + if (*p == *a) + return (char *) p; + a++; + } + + p++; + } + + return NULL; +} + +static int write_key(const char *pathname, const char *key, const char *value, int icase) +{ + struct stat st; + char *map, *off, *end, *str; + off_t size, pos; size_t base; + int fd, len, err = 0; + + fd = open(pathname, O_RDWR); + if (fd < 0) + return -errno; + + if (flock(fd, LOCK_EX) < 0) { + err = errno; + goto close; + } + + if (fstat(fd, &st) < 0) { + err = errno; + goto unlock; + } + + size = st.st_size; + + if (!size) { + if (value) { + pos = lseek(fd, size, SEEK_SET); + err = write_key_value(fd, key, value); + } + goto unlock; + } + + map = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_LOCKED, fd, 0); + if (!map || map == MAP_FAILED) { + err = errno; + goto unlock; + } + + len = strlen(key); + off = find_key(map, size, key, len, icase); + if (!off) { + if (value) { + munmap(map, size); + pos = lseek(fd, size, SEEK_SET); + err = write_key_value(fd, key, value); + } + goto unlock; + } + + base = off - map; + + end = strnpbrk(off, size, "\r\n"); + if (!end) { + err = EILSEQ; + goto unmap; + } + + if (value && ((ssize_t) strlen(value) == end - off - len - 1) && + !strncmp(off + len + 1, value, end - off - len - 1)) + goto unmap; + + len = strspn(end, "\r\n"); + end += len; + + len = size - (end - map); + if (!len) { + munmap(map, size); + if (ftruncate(fd, base) < 0) { + err = errno; + goto unlock; + } + pos = lseek(fd, base, SEEK_SET); + if (value) + err = write_key_value(fd, key, value); + + goto unlock; + } + + if (len < 0 || len > size) { + err = EILSEQ; + goto unmap; + } + + str = malloc(len); + if (!str) { + err = errno; + goto unmap; + } + + memcpy(str, end, len); + + munmap(map, size); + if (ftruncate(fd, base) < 0) { + err = errno; + free(str); + goto unlock; + } + pos = lseek(fd, base, SEEK_SET); + if (value) + err = write_key_value(fd, key, value); + + if (write(fd, str, len) < 0) + err = errno; + + free(str); + + goto unlock; + +unmap: + munmap(map, size); + +unlock: + flock(fd, LOCK_UN); + +close: + fdatasync(fd); + + close(fd); + errno = err; + + return -err; +} + +static char *read_key(const char *pathname, const char *key, int icase) +{ + struct stat st; + char *map, *off, *end, *str = NULL; + off_t size; size_t len; + int fd, err = 0; + + fd = open(pathname, O_RDONLY); + if (fd < 0) + return NULL; + + if (flock(fd, LOCK_SH) < 0) { + err = errno; + goto close; + } + + if (fstat(fd, &st) < 0) { + err = errno; + goto unlock; + } + + size = st.st_size; + + map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (!map || map == MAP_FAILED) { + err = errno; + goto unlock; + } + + len = strlen(key); + off = find_key(map, size, key, len, icase); + if (!off) { + err = EILSEQ; + goto unmap; + } + + end = strnpbrk(off, size - (map - off), "\r\n"); + if (!end) { + err = EILSEQ; + goto unmap; + } + + str = malloc(end - off - len); + if (!str) { + err = EILSEQ; + goto unmap; + } + + memset(str, 0, end - off - len); + strncpy(str, off + len + 1, end - off - len - 1); + +unmap: + munmap(map, size); + +unlock: + flock(fd, LOCK_UN); + +close: + close(fd); + errno = err; + + return str; +} + +int textfile_put(const char *pathname, const char *key, const char *value) +{ + return write_key(pathname, key, value, 0); +} + +int textfile_caseput(const char *pathname, const char *key, const char *value) +{ + return write_key(pathname, key, value, 1); +} + +int textfile_del(const char *pathname, const char *key) +{ + return write_key(pathname, key, NULL, 0); +} + +int textfile_casedel(const char *pathname, const char *key) +{ + return write_key(pathname, key, NULL, 1); +} + +char *textfile_get(const char *pathname, const char *key) +{ + return read_key(pathname, key, 0); +} + +char *textfile_caseget(const char *pathname, const char *key) +{ + return read_key(pathname, key, 1); +} + +int textfile_foreach(const char *pathname, textfile_cb func, void *data) +{ + struct stat st; + char *map, *off, *end, *key, *value; + off_t size; size_t len; + int fd, err = 0; + + fd = open(pathname, O_RDONLY); + if (fd < 0) + return -errno; + + if (flock(fd, LOCK_SH) < 0) { + err = errno; + goto close; + } + + if (fstat(fd, &st) < 0) { + err = errno; + goto unlock; + } + + size = st.st_size; + + map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (!map || map == MAP_FAILED) { + err = errno; + goto unlock; + } + + off = map; + + while (size - (off - map) > 0) { + end = strnpbrk(off, size - (off - map), " "); + if (!end) { + err = EILSEQ; + break; + } + + len = end - off; + + key = malloc(len + 1); + if (!key) { + err = errno; + break; + } + + memset(key, 0, len + 1); + memcpy(key, off, len); + + off = end + 1; + + if (size - (off - map) < 0) { + err = EILSEQ; + free(key); + break; + } + + end = strnpbrk(off, size - (off - map), "\r\n"); + if (!end) { + err = EILSEQ; + free(key); + break; + } + + len = end - off; + + value = malloc(len + 1); + if (!value) { + err = errno; + free(key); + break; + } + + memset(value, 0, len + 1); + memcpy(value, off, len); + + func(key, value, data); + + free(key); + free(value); + + off = end + 1; + } + + munmap(map, size); + +unlock: + flock(fd, LOCK_UN); + +close: + close(fd); + errno = err; + + return 0; +} -- cgit v1.2.3