diff options
Diffstat (limited to 'common/textfile.c')
| -rw-r--r-- | common/textfile.c | 463 | 
1 files changed, 463 insertions, 0 deletions
diff --git a/common/textfile.c b/common/textfile.c new file mode 100644 index 00000000..eef0f96a --- /dev/null +++ b/common/textfile.c @@ -0,0 +1,463 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  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 <config.h> +#endif + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/param.h> + +#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 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, len; +	int fd, 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 = strpbrk(off, "\r\n"); +	if (!end) { +		err = EILSEQ; +		goto unmap; +	} + +	if (value && (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: +	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 = strpbrk(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, +		void (*func)(char *key, char *value, void *data), 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 (1) { +		end = strpbrk(off, " "); +		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; + +		end = strpbrk(off, "\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; +}  | 
