diff options
Diffstat (limited to '')
-rw-r--r-- | set-contact-photos.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/set-contact-photos.c b/set-contact-photos.c new file mode 100644 index 0000000..9c62d24 --- /dev/null +++ b/set-contact-photos.c @@ -0,0 +1,236 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +USAGE("[-ru] contact-id ... photo"); + + +static char * +get_target(const char *path, size_t *lenp) +{ + char *target = NULL; + size_t size = 0; + ssize_t n = 0; + + do { + if ((size_t)n == size) + target = erealloc(target, size += 512); + n = readlink(path, target, size); + if (n < 0) + eprintf("readlink %s:", path); + } while ((size_t)n >= size); + + *lenp = (size_t)n; + target[n] = '\0'; + + return target; + +} + +static char * +get_absolute_path(const char *path, const char *cwd) +{ + char *ret, *r, *w, *target, *new; + size_t len1, len2, len3, targetlen, retlennul, looplimit = 64; + + if (path[0] == '/') { + retlennul = strlen(path) + 1; + ret = emalloc(retlennul); + memcpy(ret, path, retlennul); + } else { + len1 = strlen(path); + len2 = strlen(cwd); + retlennul = len1 + len2 + 2; + ret = emalloc(retlennul); + memcpy(ret, path, len1); + ret[len1++] = '/'; + memcpy(&ret[len1], cwd, ++len2); + } + +again: + for (w = r = ret; *r;) { + if (r[0] == '/' && (r[1] == '/' || !r[1])) { + r += 1; + } else if (r[0] == '/' && r[1] == '.' && (r[2] == '/' || !r[2])) { + r += 2; + } else if (r[0] == '/' && r[1] == '.' && r[2] == '.' && (r[3] == '/' || !r[3])) { + *w = '\0'; + target = get_target(ret, &targetlen); + if (!target) { + while (w[-1] != '/') + w--; + r += 3; + } else if (!target[0]) { + eprintf("%s: encountered symlink with empty target\n", path); + } else if (target[0] == '/') { + if (!looplimit--) { + errno = ELOOP; + eprintf("%s:", path); + } + len1 = targetlen; + len2 = retlennul - (size_t)(r - ret); + retlennul = len1 + len2; + new = emalloc(retlennul); + memcpy(new, target, len1); + memcpy(&new[len1], r, len2); + free(ret); + ret = new; + free(target); + goto again; + } else { + if (!looplimit--) { + errno = ELOOP; + eprintf("%s:", path); + } + while (w[-1] != '/') + w--; + len1 = (size_t)(w - ret); + len2 = targetlen; + len3 = retlennul - (size_t)(r - ret); + retlennul = len1 + len2 + len3; + new = emalloc(retlennul); + memcpy(new, ret, len1); + memcpy(&new[len1], target, len2); + memcpy(&new[len1 + len2], r, len3); + free(ret); + ret = new; + r = w = &ret[len1]; + free(target); + } + } else { + *w++ = *r++; + } + } + + *w = '\0'; + if (!*ret) { + ret[0] = '/'; + ret[1] = '\0'; + } + return ret; +} + +static const char * +get_cwd(char **free_this) +{ + char *cwd = NULL; + size_t size = 64 / 2; + int saved_errno = errno; + const char *pwd; + struct stat cst, pst; + + *free_this = NULL; + for (;;) { + cwd = erealloc(*free_this, size *= 2); + *free_this = cwd; + if (getcwd(cwd, size)) + break; + if (errno != ERANGE) + eprintf("getcwd %zu:", size); + } + + errno = saved_errno; + if (!(pwd = getenv("PWD")) || *pwd != '/' || stat(pwd, &pst) || stat(cwd, &cst)) + return cwd; + + if (pst.st_dev == cst.st_dev && pst.st_ino == cst.st_ino) { + free(*free_this); + *free_this = NULL; + return pwd; + } + return cwd; +} + +int +main(int argc, char *argv[]) +{ + int remove = 0, as_is = 0; + struct passwd *user; + struct libcontacts_contact contact; + char *photo, **r, **w, *newpath = NULL, *home, *cwd_free; + const char *cwd; + int ret = 0; + size_t i, len; + + ARGBEGIN { + case 'r': + as_is = 1; + break; + case 'u': + remove = 1; + as_is = 1; + break; + default: + usage(); + } ARGEND; + + if (argc < 2) + usage(); + + photo = argv[--argc]; + argv[argc] = NULL; + + for (i = 0; argv[i]; i++) + if (!*argv[i] || strchr(argv[i], '/')) + usage(); + + errno = 0; + user = getpwuid(getuid()); + if (!user) + eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist"); + + if (!as_is && user->pw_dir && user->pw_dir[0] == '/' && photo[0] != '/') { + cwd = get_cwd(&cwd_free); + if (cwd[0] != '/') + abort(); + newpath = get_absolute_path(photo, cwd); + home = get_absolute_path(user->pw_dir, cwd); + free(cwd_free); + len = strlen(home); + if (!strncmp(newpath, home, len) && newpath[len] == '/') + photo = &newpath[len + 1]; + else + photo = newpath; + free(home); + } + + for (; *argv; argv++) { + if (libcontacts_load_contact(*argv, &contact, user)) { + weprintf("libcontacts_load_contact %s: %s\n", *argv, errno ? strerror(errno) : "contact file is malformatted"); + ret = 1; + continue; + } + if (contact.photos) { + for (i = 0; contact.photos[i]; i++) + if (!strcmp(contact.photos[i], photo)) + break; + r = &contact.photos[i]; + if (remove && *r) { + free(*r); + for (w = r++; *r;) + *w++ = *r++; + *w = NULL; + if (libcontacts_save_contact(&contact, user)) { + weprintf("libcontacts_save_contact %s:", *argv); + ret = 1; + } + } else if (!remove && !*r) { + goto add_photo; + } + } else if (!remove) { + i = 0; + add_photo: + contact.photos = erealloc(contact.photos, (i + 2) * sizeof(*contact.photos)); + contact.photos[i + 1] = NULL; + contact.photos[i] = photo; + if (libcontacts_save_contact(&contact, user)) { + weprintf("libcontacts_save_contact %s:", *argv); + ret = 1; + } + contact.photos[i] = NULL; + } + libcontacts_contact_destroy(&contact); + } + + free(newpath); + return ret; +} |