From b75c1ec41abb411423218895205395e8cb191be2 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sat, 3 Apr 2021 15:02:18 +0200 Subject: Add photo utils MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 3 + Makefile | 12 +++ TODO | 5 +- find-contact-by-photo.c | 52 +++++++++++ get-contact-photos.c | 88 ++++++++++++++++++ set-contact-photos.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 394 insertions(+), 2 deletions(-) create mode 100644 find-contact-by-photo.c create mode 100644 get-contact-photos.c create mode 100644 set-contact-photos.c diff --git a/.gitignore b/.gitignore index 7e17692..67e3b03 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ /find-contact-by-name /find-contact-by-organisation /find-contact-by-pgpkey +/find-contact-by-photo /find-contact-by-site /get-contact-emails /get-contact-file @@ -22,6 +23,7 @@ /get-contact-notes /get-contact-organisations /get-contact-pgpkeys +/get-contact-photos /get-contact-sites /is-contact-ice /list-contact-groups @@ -37,4 +39,5 @@ /set-contact-notes /set-contact-organisations /set-contact-pgpkeys +/set-contact-photos /set-contact-sites diff --git a/Makefile b/Makefile index 33efd3d..347cb54 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ BIN =\ find-contact-by-name\ find-contact-by-organisation\ find-contact-by-pgpkey\ + find-contact-by-photo\ find-contact-by-site\ get-contact-emails\ get-contact-file\ @@ -18,6 +19,7 @@ BIN =\ get-contact-notes\ get-contact-organisations\ get-contact-pgpkeys\ + get-contact-photos\ get-contact-sites\ is-contact-ice\ list-contact-groups\ @@ -33,6 +35,7 @@ BIN =\ set-contact-notes\ set-contact-organisations\ set-contact-pgpkeys\ + set-contact-photos\ set-contact-sites HDR =\ @@ -73,6 +76,9 @@ find-contact-by-organisation: find-contact-by-organisation.o find-contact-by-pgpkey: find-contact-by-pgpkey.o $(CC) -o $@ $@.o $(LDFLAGS) +find-contact-by-photo: find-contact-by-photo.o + $(CC) -o $@ $@.o $(LDFLAGS) + find-contact-by-site: find-contact-by-site.o $(CC) -o $@ $@.o $(LDFLAGS) @@ -100,6 +106,9 @@ get-contact-organisations: get-contact-organisations.o get-contact-pgpkeys: get-contact-pgpkeys.o $(CC) -o $@ $@.o $(LDFLAGS) +get-contact-photos: get-contact-photos.o + $(CC) -o $@ $@.o $(LDFLAGS) + get-contact-sites: get-contact-sites.o $(CC) -o $@ $@.o $(LDFLAGS) @@ -145,6 +154,9 @@ set-contact-organisations: set-contact-organisations.o set-contact-pgpkeys: set-contact-pgpkeys.o $(CC) -o $@ $@.o $(LDFLAGS) +set-contact-photos: set-contact-photos.o + $(CC) -o $@ $@.o $(LDFLAGS) + set-contact-sites: set-contact-sites.o $(CC) -o $@ $@.o $(LDFLAGS) diff --git a/TODO b/TODO index 992e7b8..7fe8575 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,3 @@ -Add tools for .photos Add tools for .blocks Add tools for .numbers Add tools for .addresses @@ -9,12 +8,13 @@ Test find-contact-by-email Test find-contact-by-name Test find-contact-by-organisation Test find-contact-by-pgpkey +Test find-contact-by-photo Test find-contact-by-site Test get-contact-emails -Test get-contact-groups Test get-contact-name Test get-contact-organisations Test get-contact-pgpkeys +Test get-contact-photos Test get-contact-sites Test list-contact-organisations Test list-organisation-contacts @@ -23,6 +23,7 @@ Test set-contact-groups Test set-contact-name Test set-contact-organisations Test set-contact-pgpkeys +Test set-contact-photos Test set-contact-sites Add man pages Add readme diff --git a/find-contact-by-photo.c b/find-contact-by-photo.c new file mode 100644 index 0000000..46c14ed --- /dev/null +++ b/find-contact-by-photo.c @@ -0,0 +1,52 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +USAGE("-L | photo"); + + +int +main(int argc, char *argv[]) +{ + int list = 0; + struct passwd *user; + struct libcontacts_contact **contacts; + char **photos, *photo; + size_t i; + + ARGBEGIN { + case 'L': + list = 1; + break; + default: + usage(); + } ARGEND; + + if (argc != 1 - list) + usage(); + + errno = 0; + user = getpwuid(getuid()); + if (!user) + eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist"); + + if (libcontacts_load_contacts(&contacts, user)) + eprintf("libcontacts_load_contacts:"); + for (i = 0; contacts[i]; i++) { + if ((photos = contacts[i]->photos)) { + for (; (photo = *photos); photos++) { + if (list) + printf("%s (%s)\n", contacts[i]->id, photo); + else if (!strcmp(photo, argv[0])) + printf("%s\n", contacts[i]->id); + } + } + libcontacts_contact_destroy(contacts[i]); + free(contacts[i]); + } + free(contacts); + + if (fflush(stdout) || ferror(stdout) || fclose(stdout)) + eprintf("printf:"); + + return 0; +} diff --git a/get-contact-photos.c b/get-contact-photos.c new file mode 100644 index 0000000..3b44393 --- /dev/null +++ b/get-contact-photos.c @@ -0,0 +1,88 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +USAGE("[-a] contact-id ..."); + + +int +main(int argc, char *argv[]) +{ + int absolute_path = 0; + struct passwd *user; + struct libcontacts_contact contact; + const char *slash = ""; + char **photos, **r, **w; + size_t i; + + ARGBEGIN { + case 'a': + absolute_path = 1; + break; + default: + usage(); + } ARGEND; + + if (!argc) + usage(); + + 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 (libcontacts_load_contact(*argv, &contact, user)) + eprintf("libcontacts_load_contact %s: %s\n", *argv, errno ? strerror(errno) : "contact file is malformatted"); + photos = contact.photos; + contact.photos = NULL; + libcontacts_contact_destroy(&contact); + if (!photos || !*photos) { + free(photos); + return 0; + } + + for (; *argv; argv++) { + if (libcontacts_load_contact(*argv, &contact, user)) + eprintf("libcontacts_load_contact %s: %s\n", *argv, errno ? strerror(errno) : "contact file is malformatted"); + if (!contact.photos) { + libcontacts_contact_destroy(&contact); + for (i = 0; photos[i]; i++) + free(photos[i]); + free(photos); + return 0; + } + for (w = r = photos; *r; r++) { + for (i = 0; contact.photos[i]; i++) + if (!strcmp(contact.photos[i], *r)) + break; + if (!contact.photos[i]) + free(*r); + else + *w++ = *r; + } + *w = NULL; + libcontacts_contact_destroy(&contact); + if (!*photos) { + free(photos); + return 0; + } + } + + if (absolute_path && strchr(user->pw_dir, '\0')[-1] != '/') + slash = "/"; + for (i = 0; photos[i]; i++) { + if (photos[i][0] != '/' && absolute_path) + printf("%s%s%s\n", user->pw_dir, slash, photos[i]); + else if (photos[i][0]) + printf("%s\n", photos[i]); + free(photos[i]); + } + free(photos); + + if (fflush(stdout) || ferror(stdout) || fclose(stdout)) + eprintf("printf:"); + return 0; +} 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; +} -- cgit v1.2.3-70-g09d2