diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/textfile.c | 160 | ||||
-rw-r--r-- | common/textfile.h | 1 |
2 files changed, 149 insertions, 12 deletions
diff --git a/common/textfile.c b/common/textfile.c index 6f84cfa9..0bda2a94 100644 --- a/common/textfile.c +++ b/common/textfile.c @@ -40,51 +40,186 @@ #include <sys/stat.h> #include <sys/mman.h> +static int write_key_value(const int fd, const char *key, const char *value) +{ + char *str; + int size, err = 0; + + size = strlen(key) + strlen(value) + 2; + + str = malloc(size); + if (!str) + return ENOMEM; + + sprintf(str, "%s %s\n", key, value); + + if (write(fd, str, size) < 0) + err = errno; + + free(str); + + return err; +} + +int textfile_put(const char *pathname, const char *key, const char *value) +{ + 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) { + pos = lseek(fd, size, SEEK_SET); + err = write_key_value(fd, key, value); + goto unlock; + } + + map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) { + err = errno; + goto unlock; + } + + off = strstr(map, key); + if (!off) { + munmap(map, size); + pos = lseek(fd, size, SEEK_SET); + err = write_key_value(fd, key, value); + goto unlock; + } + + if (off > map) { + while (*(off - 1) != '\r' && *(off - 1) != '\n') { + off = strstr(off, key); + if (!off) { + 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; + } + + len = strspn(end, "\r\n"); + end += len; + + len = size - (end - map); + + str = malloc(len); + if (!str) { + err = errno; + goto unmap; + } + + memcpy(str, end, len); + munmap(map, size); + + ftruncate(fd, base); + pos = lseek(fd, base, SEEK_SET); + + write_key_value(fd, key, value); + write(fd, str, len); + + free(str); + + goto unlock; + +unmap: + munmap(map, size); + +unlock: + flock(fd, LOCK_UN); + +close: + close(fd); + errno = err; + + return -err; +} + char *textfile_get(const char *pathname, const char *key) { struct stat st; char *map, *off, *end, *str = NULL; off_t size; size_t len; - int fd; + int fd, err = 0; fd = open(pathname, O_RDONLY); if (fd < 0) return NULL; - if (flock(fd, LOCK_SH) < 0) + if (flock(fd, LOCK_SH) < 0) { + err = errno; goto close; + } - if (fstat(fd, &st) < 0) + if (fstat(fd, &st) < 0) { + err = errno; goto unlock; + } size = st.st_size; - map = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); - if (map == MAP_FAILED) - goto close; + map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) { + err = errno; + goto unlock; + } off = strstr(map, key); - if (!off) + if (!off) { + err = EILSEQ; goto unmap; + } if (off > map) { while (*(off - 1) != '\r' && *(off - 1) != '\n') { - off = strstr(map, key); - if (!off) + off = strstr(off, key); + if (!off) { + err = EILSEQ; goto unmap; + } } } end = strpbrk(off, "\r\n"); - if (!end) + if (!end) { + err = EILSEQ; goto unmap; + } len = strlen(key); str = malloc(end - off - len); - memset(str, 0, end - off - len); - if (!str) + if (!str) { + err = EILSEQ; goto unmap; + } + memset(str, 0, end - off - len); strncpy(str, off + len + 1, end - off - len - 1); unmap: @@ -95,6 +230,7 @@ unlock: close: close(fd); + errno = err; return str; } diff --git a/common/textfile.h b/common/textfile.h index 56a0bc10..8871f53c 100644 --- a/common/textfile.h +++ b/common/textfile.h @@ -26,4 +26,5 @@ * $Id$ */ +int textfile_put(const char *pathname, const char *key, const char *value); char *textfile_get(const char *pathname, const char *key); |