aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--Makefile22
-rw-r--r--TODO8
-rw-r--r--find-contact-by-organisation.c79
-rw-r--r--get-contact-emails.c8
-rw-r--r--get-contact-groups.c68
-rw-r--r--get-contact-organisations.c8
-rw-r--r--get-contact-pgpkeys.c8
-rw-r--r--get-contact-sites.c8
-rw-r--r--list-contact-groups.c106
-rw-r--r--list-contact-organisations.c117
-rw-r--r--list-group-contacts.c46
-rw-r--r--list-organisation-contacts.c58
-rw-r--r--set-contact-gender.c6
-rw-r--r--set-contact-groups.c79
15 files changed, 610 insertions, 20 deletions
diff --git a/.gitignore b/.gitignore
index 1411d9a..7e17692 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,29 +7,34 @@
*.su
*.lo
*.bo
+/contacts
+/contacts.c
/find-contact-by-email
/find-contact-by-name
+/find-contact-by-organisation
/find-contact-by-pgpkey
/find-contact-by-site
/get-contact-emails
/get-contact-file
/get-contact-gender
+/get-contact-groups
/get-contact-name
/get-contact-notes
/get-contact-organisations
/get-contact-pgpkeys
/get-contact-sites
/is-contact-ice
+/list-contact-groups
/list-contact-organisations
/list-contacts
+/list-group-contacts
/list-organisation-contacts
/set-contact-emails
/set-contact-gender
+/set-contact-groups
/set-contact-ice
/set-contact-name
/set-contact-notes
/set-contact-organisations
/set-contact-pgpkeys
/set-contact-sites
-/contacts
-/contacts.c
diff --git a/Makefile b/Makefile
index 362ada4..33efd3d 100644
--- a/Makefile
+++ b/Makefile
@@ -7,20 +7,27 @@ include $(CONFIGFILE)
BIN =\
find-contact-by-email\
find-contact-by-name\
+ find-contact-by-organisation\
find-contact-by-pgpkey\
find-contact-by-site\
get-contact-emails\
get-contact-file\
get-contact-gender\
+ get-contact-groups\
get-contact-name\
get-contact-notes\
get-contact-organisations\
get-contact-pgpkeys\
get-contact-sites\
is-contact-ice\
+ list-contact-groups\
+ list-contact-organisations\
list-contacts\
+ list-group-contacts\
+ list-organisation-contacts\
set-contact-emails\
set-contact-gender\
+ set-contact-groups\
set-contact-ice\
set-contact-name\
set-contact-notes\
@@ -60,6 +67,9 @@ find-contact-by-email: find-contact-by-email.o
find-contact-by-name: find-contact-by-name.o
$(CC) -o $@ $@.o $(LDFLAGS)
+find-contact-by-organisation: find-contact-by-organisation.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
find-contact-by-pgpkey: find-contact-by-pgpkey.o
$(CC) -o $@ $@.o $(LDFLAGS)
@@ -75,6 +85,9 @@ get-contact-file: get-contact-file.o
get-contact-gender: get-contact-gender.o
$(CC) -o $@ $@.o $(LDFLAGS)
+get-contact-groups: get-contact-groups.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
get-contact-name: get-contact-name.o
$(CC) -o $@ $@.o $(LDFLAGS)
@@ -93,12 +106,18 @@ get-contact-sites: get-contact-sites.o
is-contact-ice: is-contact-ice.o
$(CC) -o $@ $@.o $(LDFLAGS)
+list-contact-groups: list-contact-groups.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
list-contact-organisations: list-contact-organisations.o
$(CC) -o $@ $@.o $(LDFLAGS)
list-contacts: list-contacts.o
$(CC) -o $@ $@.o $(LDFLAGS)
+list-group-contacts: list-group-contacts.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
list-organisation-contacts: list-organisation-contacts.o
$(CC) -o $@ $@.o $(LDFLAGS)
@@ -108,6 +127,9 @@ set-contact-emails: set-contact-emails.o
set-contact-gender: set-contact-gender.o
$(CC) -o $@ $@.o $(LDFLAGS)
+set-contact-groups: set-contact-groups.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
set-contact-ice: set-contact-ice.o
$(CC) -o $@ $@.o $(LDFLAGS)
diff --git a/TODO b/TODO
index 33e964e..992e7b8 100644
--- a/TODO
+++ b/TODO
@@ -1,18 +1,17 @@
-list-contact-organisations
-list-organisation-contacts
-
Add tools for .photos
-Add tools for .groups
Add tools for .blocks
Add tools for .numbers
Add tools for .addresses
Add tools for .chats
Add tools for .birthday
+
Test find-contact-by-email
Test find-contact-by-name
+Test find-contact-by-organisation
Test find-contact-by-pgpkey
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
@@ -20,6 +19,7 @@ Test get-contact-sites
Test list-contact-organisations
Test list-organisation-contacts
Test set-contact-emails
+Test set-contact-groups
Test set-contact-name
Test set-contact-organisations
Test set-contact-pgpkeys
diff --git a/find-contact-by-organisation.c b/find-contact-by-organisation.c
new file mode 100644
index 0000000..8287225
--- /dev/null
+++ b/find-contact-by-organisation.c
@@ -0,0 +1,79 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-o organisation] [-t title] [-OT]");
+
+
+int
+main(int argc, char *argv[])
+{
+ int display_organisation = 0, display_title = 0;
+ struct passwd *user;
+ struct libcontacts_contact **contacts;
+ struct libcontacts_organisation **orgs, *org;
+ char *organisation = NULL, *title = NULL;
+ size_t i;
+
+ ARGBEGIN {
+ case 'o':
+ if (organisation)
+ usage();
+ organisation = ARG();
+ break;
+ case 't':
+ if (title)
+ usage();
+ title = ARG();
+ break;
+ case 'O':
+ display_organisation = 1;
+ break;
+ case 'T':
+ display_title = 1;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (argc)
+ usage();
+
+ if (!organisation && !title && !display_organisation && !display_title)
+ display_organisation = 1;
+
+ 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 ((orgs = contacts[i]->organisations)) {
+ for (; (org = *orgs); orgs++) {
+ if (organisation && strcmpnul(org->organisation, organisation))
+ continue;
+ if (title && strcmpnul(org->title, title))
+ continue;
+ if (display_organisation && display_title) {
+ printf("%s (%s: %s)\n", contacts[i]->id, org->organisation, org->title);
+ } else if (display_organisation) {
+ printf("%s (%s)\n", contacts[i]->id, org->organisation);
+ } else if (display_title) {
+ printf("%s (%s)\n", contacts[i]->id, org->title);
+ } else {
+ printf("%s\n", contacts[i]->id);
+ break;
+ }
+ }
+ }
+ 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-emails.c b/get-contact-emails.c
index 891c873..c41b48f 100644
--- a/get-contact-emails.c
+++ b/get-contact-emails.c
@@ -57,8 +57,10 @@ main(int argc, char *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;
- } else {
- for (emails = contact.emails; (email = *emails); emails++) {
+ continue;
+ }
+ if ((emails = contact.emails)) {
+ for (; (email = *emails); emails++) {
if (lookup_ctx && strcmpnul(email->context, lookup_ctx))
continue;
if (lookup_addr && strcmpnul(email->address, lookup_addr))
@@ -76,8 +78,8 @@ main(int argc, char *argv[])
else
printf("%s\n", email->context);
}
- libcontacts_contact_destroy(&contact);
}
+ libcontacts_contact_destroy(&contact);
}
if (fflush(stdout) || ferror(stdout) || fclose(stdout))
diff --git a/get-contact-groups.c b/get-contact-groups.c
new file mode 100644
index 0000000..fa1eb77
--- /dev/null
+++ b/get-contact-groups.c
@@ -0,0 +1,68 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-g group | -n] contact-id ...");
+
+
+int
+main(int argc, char *argv[])
+{
+ int lookup_unassigned = 0;
+ struct passwd *user;
+ struct libcontacts_contact contact;
+ char *lookup_group = NULL;
+ char **groups;
+ int ret = 0;
+ size_t i;
+
+ ARGBEGIN {
+ case 'g':
+ if (lookup_group)
+ usage();
+ lookup_group = ARG();
+ break;
+ case 'n':
+ lookup_unassigned = 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");
+
+ 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;
+ }
+ if (lookup_unassigned) {
+ if (!contact.groups || !*contact.groups)
+ printf("%s\n", *argv);
+ } else if ((groups = contact.groups)) {
+ for (; *groups; groups++) {
+ if (lookup_group) {
+ printf("%s\n", *argv);
+ } else {
+ if (argc > 1)
+ printf("%s: ", *argv);
+ printf("%s\n", *groups);
+ }
+ }
+ }
+ libcontacts_contact_destroy(&contact);
+ }
+
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout))
+ eprintf("printf:");
+ return ret;
+}
diff --git a/get-contact-organisations.c b/get-contact-organisations.c
index 3441f55..70feea5 100644
--- a/get-contact-organisations.c
+++ b/get-contact-organisations.c
@@ -57,8 +57,10 @@ main(int argc, char *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;
- } else {
- for (orgs = contact.organisations; (org = *orgs); orgs++) {
+ continue;
+ }
+ if ((orgs = contact.organisations)) {
+ for (; (org = *orgs); orgs++) {
if (lookup_org && strcmpnul(org->organisation, lookup_org))
continue;
if (lookup_title && strcmpnul(org->title, lookup_title))
@@ -76,8 +78,8 @@ main(int argc, char *argv[])
else
printf("%s\n", org->organisation);
}
- libcontacts_contact_destroy(&contact);
}
+ libcontacts_contact_destroy(&contact);
}
if (fflush(stdout) || ferror(stdout) || fclose(stdout))
diff --git a/get-contact-pgpkeys.c b/get-contact-pgpkeys.c
index ad56cc2..7935d17 100644
--- a/get-contact-pgpkeys.c
+++ b/get-contact-pgpkeys.c
@@ -57,8 +57,10 @@ main(int argc, char *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;
- } else {
- for (keys = contact.pgpkeys; (key = *keys); keys++) {
+ continue;
+ }
+ if ((keys = contact.pgpkeys)) {
+ for (; (key = *keys); keys++) {
if (lookup_ctx && strcmpnul(key->context, lookup_ctx))
continue;
if (lookup_id && strcmpnul(key->id, lookup_id))
@@ -76,8 +78,8 @@ main(int argc, char *argv[])
else
printf("%s\n", key->context);
}
- libcontacts_contact_destroy(&contact);
}
+ libcontacts_contact_destroy(&contact);
}
if (fflush(stdout) || ferror(stdout) || fclose(stdout))
diff --git a/get-contact-sites.c b/get-contact-sites.c
index 7208d9f..06a8d6b 100644
--- a/get-contact-sites.c
+++ b/get-contact-sites.c
@@ -57,8 +57,10 @@ main(int argc, char *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;
- } else {
- for (sites = contact.sites; (site = *sites); sites++) {
+ continue;
+ }
+ if ((sites = contact.sites)) {
+ for (; (site = *sites); sites++) {
if (lookup_ctx && strcmpnul(site->context, lookup_ctx))
continue;
if (lookup_addr && strcmpnul(site->address, lookup_addr))
@@ -76,8 +78,8 @@ main(int argc, char *argv[])
else
printf("%s\n", site->context);
}
- libcontacts_contact_destroy(&contact);
}
+ libcontacts_contact_destroy(&contact);
}
if (fflush(stdout) || ferror(stdout) || fclose(stdout))
diff --git a/list-contact-groups.c b/list-contact-groups.c
new file mode 100644
index 0000000..f0c1dd1
--- /dev/null
+++ b/list-contact-groups.c
@@ -0,0 +1,106 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("-L | contact-id ...");
+
+
+struct group_node {
+ char *name;
+ struct group_node *next;
+ struct group_node *prev;
+};
+
+static struct group_node head;
+static struct group_node tail;
+
+static void
+list_groups(const struct libcontacts_contact *contact)
+{
+ char **groups, *group;
+ struct group_node *node, *new;
+ int cmp;
+
+ if ((groups = contact->groups)) {
+ for (; (group = *groups); groups++) {
+ for (node = head.next; node->next; node = node->next) {
+ cmp = strcmp(group, node->name);
+ if (cmp < 0)
+ continue;
+ if (cmp == 0)
+ goto next_group;
+ break;
+ }
+ printf("%s\n", group);
+ new = ecalloc(1, sizeof(*new));
+ (new->prev = node->prev)->next = new;
+ (new->next = node)->prev = new;
+ new->name = estrdup(group);
+ next_group:;
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int list = 0;
+ struct passwd *user;
+ struct libcontacts_contact **contacts, contact;
+ struct group_node *node, *next_node;
+ int ret = 0;
+ size_t i;
+
+ ARGBEGIN {
+ case 'L':
+ list = 1;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (list ? argc : !argc)
+ usage();
+
+ head.prev = NULL;
+ head.next = &tail;
+ tail.prev = &head;
+ tail.next = NULL;
+
+ errno = 0;
+ user = getpwuid(getuid());
+ if (!user)
+ eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist");
+
+ if (list) {
+ if (libcontacts_load_contacts(&contacts, user))
+ eprintf("libcontacts_load_contacts:");
+ for (i = 0; contacts[i]; i++) {
+ list_groups(contacts[i]);
+ libcontacts_contact_destroy(contacts[i]);
+ free(contacts[i]);
+ }
+ free(contacts);
+ } else {
+ 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;
+ } else {
+ list_groups(&contact);
+ libcontacts_contact_destroy(&contact);
+ }
+ }
+ }
+
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout))
+ eprintf("printf:");
+
+ for (node = head.next; node->next; node = next_node) {
+ next_node = node->next;
+ free(node->name);
+ free(node);
+ }
+
+ return ret;
+}
diff --git a/list-contact-organisations.c b/list-contact-organisations.c
new file mode 100644
index 0000000..3a67e42
--- /dev/null
+++ b/list-contact-organisations.c
@@ -0,0 +1,117 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-t title] (-L | contact-id ...)");
+
+
+struct org_node {
+ char *name;
+ struct org_node *next;
+ struct org_node *prev;
+};
+
+static struct org_node head;
+static struct org_node tail;
+
+static void
+list_orgs(const struct libcontacts_contact *contact, const char *title)
+{
+ struct libcontacts_organisation **orgs, *org;
+ struct org_node *node, *new;
+ int cmp;
+
+ if ((orgs = contact->organisations)) {
+ for (; (org = *orgs); orgs++) {
+ if (!org->organisation)
+ continue;
+ if (title && strcmpnul(org->title, title))
+ continue;
+ for (node = head.next; node->next; node = node->next) {
+ cmp = strcmp(org->organisation, node->name);
+ if (cmp < 0)
+ continue;
+ if (cmp == 0)
+ goto next_org;
+ break;
+ }
+ printf("%s\n", org->organisation);
+ new = ecalloc(1, sizeof(*new));
+ (new->prev = node->prev)->next = new;
+ (new->next = node)->prev = new;
+ new->name = org->organisation;
+ org->organisation = NULL;
+ next_org:;
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int list = 0;
+ struct passwd *user;
+ struct libcontacts_contact **contacts, contact;
+ struct org_node *node, *next_node;
+ int ret = 0;
+ char *title = NULL;
+ size_t i;
+
+ ARGBEGIN {
+ case 't':
+ if (title)
+ usage();
+ title = ARG();
+ break;
+ case 'L':
+ list = 1;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (list ? argc : !argc)
+ usage();
+
+ head.prev = NULL;
+ head.next = &tail;
+ tail.prev = &head;
+ tail.next = NULL;
+
+ errno = 0;
+ user = getpwuid(getuid());
+ if (!user)
+ eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist");
+
+ if (list) {
+ if (libcontacts_load_contacts(&contacts, user))
+ eprintf("libcontacts_load_contacts:");
+ for (i = 0; contacts[i]; i++) {
+ list_orgs(contacts[i], title);
+ libcontacts_contact_destroy(contacts[i]);
+ free(contacts[i]);
+ }
+ free(contacts);
+ } else {
+ 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;
+ } else {
+ list_orgs(&contact, title);
+ libcontacts_contact_destroy(&contact);
+ }
+ }
+ }
+
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout))
+ eprintf("printf:");
+
+ for (node = head.next; node->next; node = next_node) {
+ next_node = node->next;
+ free(node->name);
+ free(node);
+ }
+
+ return ret;
+}
diff --git a/list-group-contacts.c b/list-group-contacts.c
new file mode 100644
index 0000000..45299fd
--- /dev/null
+++ b/list-group-contacts.c
@@ -0,0 +1,46 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("group ...");
+
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *user;
+ struct libcontacts_contact **contacts;
+ char **groups;
+ int ret = 0;
+ size_t i, j;
+
+ NOFLAGS(!argc);
+
+ 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 ((groups = contacts[i]->groups)) {
+ for (; *groups; groups++) {
+ for (j = 0; argv[j]; j++) {
+ if (!strcmp(*groups, argv[j])) {
+ printf("%s\n", contacts[i]->id);
+ goto done;
+ }
+ }
+ }
+ }
+ done:
+ libcontacts_contact_destroy(contacts[i]);
+ free(contacts[i]);
+ }
+ free(contacts);
+
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout))
+ eprintf("printf:");
+
+ return ret;
+}
diff --git a/list-organisation-contacts.c b/list-organisation-contacts.c
new file mode 100644
index 0000000..2e6ddae
--- /dev/null
+++ b/list-organisation-contacts.c
@@ -0,0 +1,58 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-t title] organisation");
+
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *user;
+ struct libcontacts_contact **contacts;
+ struct libcontacts_organisation **orgs, *org;
+ char *title = NULL;
+ size_t i;
+
+ ARGBEGIN {
+ case 't':
+ if (title)
+ usage();
+ title = ARG();
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (argc != 1)
+ 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 ((orgs = contacts[i]->organisations)) {
+ for (; (org = *orgs); orgs++) {
+ if (strcmpnul(org->organisation, argv[0]))
+ continue;
+ if (title && strcmpnul(org->title, title))
+ continue;
+ if (title || !org->title)
+ printf("%s\n", contacts[i]->id);
+ else if (org->title)
+ printf("%s (%s)\n", contacts[i]->id, org->title);
+ }
+ }
+ libcontacts_contact_destroy(contacts[i]);
+ free(contacts[i]);
+ }
+ free(contacts);
+
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout))
+ eprintf("printf:");
+
+ return 0;
+}
diff --git a/set-contact-gender.c b/set-contact-gender.c
index f9750d6..b0b0258 100644
--- a/set-contact-gender.c
+++ b/set-contact-gender.c
@@ -57,14 +57,16 @@ main(int argc, char *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;
- } else {
+ continue;
+ }
+ if (contact.gender != gender) {
contact.gender = gender;
if (libcontacts_save_contact(&contact, user)) {
weprintf("libcontacts_save_contact %s:", *argv);
ret = 1;
}
- libcontacts_contact_destroy(&contact);
}
+ libcontacts_contact_destroy(&contact);
}
return ret;
diff --git a/set-contact-groups.c b/set-contact-groups.c
new file mode 100644
index 0000000..2b8e3c1
--- /dev/null
+++ b/set-contact-groups.c
@@ -0,0 +1,79 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-u] contact-id ... group");
+
+
+int
+main(int argc, char *argv[])
+{
+ int remove = 0;
+ struct passwd *user;
+ struct libcontacts_contact contact;
+ char *group, **r, **w;
+ int ret = 0;
+ size_t i;
+
+ ARGBEGIN {
+ case 'u':
+ remove = 1;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (argc < 2)
+ usage();
+
+ group = 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");
+
+ 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.groups) {
+ for (i = 0; contact.groups[i]; i++)
+ if (!strcmp(contact.groups[i], group))
+ break;
+ r = &contact.groups[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_group;
+ }
+ } else if (!remove) {
+ i = 0;
+ add_group:
+ contact.groups = erealloc(contact.groups, (i + 2) * sizeof(*contact.groups));
+ contact.groups[i + 1] = NULL;
+ contact.groups[i] = group;
+ if (libcontacts_save_contact(&contact, user)) {
+ weprintf("libcontacts_save_contact %s:", *argv);
+ ret = 1;
+ }
+ contact.groups[i] = NULL;
+ }
+ libcontacts_contact_destroy(&contact);
+ }
+
+ return ret;
+}