diff options
Diffstat (limited to 'libcontacts_save_contact.c')
-rw-r--r-- | libcontacts_save_contact.c | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/libcontacts_save_contact.c b/libcontacts_save_contact.c new file mode 100644 index 0000000..5d2fcd7 --- /dev/null +++ b/libcontacts_save_contact.c @@ -0,0 +1,93 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libcontacts_save_contact(struct libcontacts_contact *contact, const struct passwd *user) +{ + char *data = NULL, *path = NULL, *tmppath; + int oflags = O_WRONLY | O_CREAT | O_TRUNC; + int fd = -1, saved_errno = errno; + ssize_t r; + size_t p, n, num = 0; + char *basenam = NULL; + + if (libcontacts_format_contact(contact, &data)) + goto fail; + + if (!contact->id) { + oflags = O_WRONLY | O_CREAT | O_EXCL; + generate_id: + if (!num) { + if (!contact->name || !*contact->name) { + contact->id = strdup("unnamed"); + if (!contact->id) + goto fail; + } else { + contact->id = strdup(contact->name); + if (!contact->id) + goto fail; + for (p = 0; contact->id[p]; p++) { + if (isalpha(contact->id[p])) + contact->id[p] = tolower(contact->id[p]); + else + contact->id[p] = '-'; + } + } + } else { + free(contact->id); + snprintf(NULL, 0, "%s-%zu%zn", basenam, num, &r); + contact->id = malloc((size_t)r + 1); + if (!contact->id) + goto fail; + sprintf(contact->id, "%s-%zu", basenam, num); + } + } + + path = libcontacts_get_path(contact->id, user); + if (!path) + goto fail; + + tmppath = alloca(strlen(path) + sizeof("~")); + stpcpy(stpcpy(tmppath, path), "~"); + + fd = open(tmppath, oflags, 0644); + if (fd < 0) { + if ((oflags & O_EXCL) && errno == EEXIST) { + if (!num++) { + basenam = contact->id; + contact->id = NULL; + } + goto generate_id; + } + goto fail; + } + + n = strlen(data); + for (p = 0; p < n; p += (size_t)r) { + r = write(fd, &data[p], n - p); + if (r < 0) + goto fail; + } + + close(fd), fd = -1; + + if (rename(tmppath, path)) + goto fail; + + free(data); + free(path); + free(basenam); + errno = saved_errno; + return 0; + +fail: + saved_errno = errno; + if (fd >= 0) + close(fd); + free(data); + free(path); + free(basenam); + errno = saved_errno; + return -1; +} |