aboutsummaryrefslogtreecommitdiffstats
path: root/libcontacts_save_contact.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcontacts_save_contact.c')
-rw-r--r--libcontacts_save_contact.c93
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;
+}