aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2021-04-03 15:02:18 +0200
committerMattias Andrée <maandree@kth.se>2021-04-03 15:02:18 +0200
commitb75c1ec41abb411423218895205395e8cb191be2 (patch)
tree62abd35bd88e543a400912d9db35a456da1c72bb
parentm + add more tools (diff)
downloadcontacts-b75c1ec41abb411423218895205395e8cb191be2.tar.gz
contacts-b75c1ec41abb411423218895205395e8cb191be2.tar.bz2
contacts-b75c1ec41abb411423218895205395e8cb191be2.tar.xz
Add photo utils
Signed-off-by: Mattias Andrée <maandree@kth.se>
-rw-r--r--.gitignore3
-rw-r--r--Makefile12
-rw-r--r--TODO5
-rw-r--r--find-contact-by-photo.c52
-rw-r--r--get-contact-photos.c88
-rw-r--r--set-contact-photos.c236
6 files changed, 394 insertions, 2 deletions
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;
+}