aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--Makefile34
-rw-r--r--TODO12
-rw-r--r--add-contact.c46
-rw-r--r--common.h5
-rw-r--r--config.mk2
-rw-r--r--find-contact-by-number.c104
-rw-r--r--get-contact-birthday.c160
-rw-r--r--get-contact-file.c3
-rw-r--r--get-contact-numbers.c91
-rw-r--r--print-contact.c55
-rw-r--r--remove-contact.c32
-rw-r--r--set-contact-numbers.c140
13 files changed, 688 insertions, 5 deletions
diff --git a/.gitignore b/.gitignore
index 3ce8ba4..2867b2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,13 +9,16 @@
*.bo
/contacts
/contacts.c
+/add-contact
/find-contact-by-chat
/find-contact-by-email
/find-contact-by-name
+/find-contact-by-number
/find-contact-by-organisation
/find-contact-by-pgpkey
/find-contact-by-photo
/find-contact-by-site
+/get-contact-birthday
/get-contact-chats
/get-contact-emails
/get-contact-file
@@ -23,17 +26,22 @@
/get-contact-groups
/get-contact-name
/get-contact-notes
+/get-contact-numbers
/get-contact-organisations
/get-contact-pgpkeys
/get-contact-photos
/get-contact-sites
/is-contact-ice
+/list-birthdays
/list-chat-contacts
/list-contact-groups
/list-contact-organisations
/list-contacts
/list-group-contacts
/list-organisation-contacts
+/print-contact
+/remove-contact
+/set-contact-birthday
/set-contact-chats
/set-contact-emails
/set-contact-gender
@@ -41,6 +49,7 @@
/set-contact-ice
/set-contact-name
/set-contact-notes
+/set-contact-numbers
/set-contact-organisations
/set-contact-pgpkeys
/set-contact-photos
diff --git a/Makefile b/Makefile
index c821140..5ea2d5b 100644
--- a/Makefile
+++ b/Makefile
@@ -5,13 +5,16 @@ include $(CONFIGFILE)
BIN =\
+ add-contact\
find-contact-by-chat\
find-contact-by-email\
find-contact-by-name\
+ find-contact-by-number\
find-contact-by-organisation\
find-contact-by-pgpkey\
find-contact-by-photo\
find-contact-by-site\
+ get-contact-birthday\
get-contact-chats\
get-contact-emails\
get-contact-file\
@@ -19,6 +22,7 @@ BIN =\
get-contact-groups\
get-contact-name\
get-contact-notes\
+ get-contact-numbers\
get-contact-organisations\
get-contact-pgpkeys\
get-contact-photos\
@@ -30,6 +34,8 @@ BIN =\
list-contacts\
list-group-contacts\
list-organisation-contacts\
+ print-contact\
+ remove-contact\
set-contact-chats\
set-contact-emails\
set-contact-gender\
@@ -37,6 +43,7 @@ BIN =\
set-contact-ice\
set-contact-name\
set-contact-notes\
+ set-contact-numbers\
set-contact-organisations\
set-contact-pgpkeys\
set-contact-photos\
@@ -68,6 +75,9 @@ contacts.c: contacts.c.in Makefile
printf '\n\n' >> $@
cat contacts.c.in >> $@
+add-contact: add-contact.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
find-contact-by-chat: find-contact-by-chat.o
$(CC) -o $@ $@.o $(LDFLAGS)
@@ -77,6 +87,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-number: find-contact-by-number.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
find-contact-by-organisation: find-contact-by-organisation.o
$(CC) -o $@ $@.o $(LDFLAGS)
@@ -89,6 +102,9 @@ find-contact-by-photo: find-contact-by-photo.o
find-contact-by-site: find-contact-by-site.o
$(CC) -o $@ $@.o $(LDFLAGS)
+get-contact-birthday: get-contact-birthday.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
get-contact-chats: get-contact-chats.o
$(CC) -o $@ $@.o $(LDFLAGS)
@@ -110,6 +126,9 @@ get-contact-name: get-contact-name.o
get-contact-notes: get-contact-notes.o
$(CC) -o $@ $@.o $(LDFLAGS)
+get-contact-numbers: get-contact-numbers.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
get-contact-organisations: get-contact-organisations.o
$(CC) -o $@ $@.o $(LDFLAGS)
@@ -125,6 +144,9 @@ get-contact-sites: get-contact-sites.o
is-contact-ice: is-contact-ice.o
$(CC) -o $@ $@.o $(LDFLAGS)
+list-birthdays: list-birthdays.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
list-chat-contacts: list-chat-contacts.o
$(CC) -o $@ $@.o $(LDFLAGS)
@@ -143,6 +165,15 @@ list-group-contacts: list-group-contacts.o
list-organisation-contacts: list-organisation-contacts.o
$(CC) -o $@ $@.o $(LDFLAGS)
+print-contact: print-contact.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
+remove-contact: remove-contact.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
+set-contact-birthday: set-contact-birthday.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
set-contact-chats: set-contact-chats.o
$(CC) -o $@ $@.o $(LDFLAGS)
@@ -164,6 +195,9 @@ set-contact-name: set-contact-name.o
set-contact-notes: set-contact-notes.o
$(CC) -o $@ $@.o $(LDFLAGS)
+set-contact-numbers: set-contact-numbers.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
set-contact-organisations: set-contact-organisations.o
$(CC) -o $@ $@.o $(LDFLAGS)
diff --git a/TODO b/TODO
index 5ad9943..f0c6588 100644
--- a/TODO
+++ b/TODO
@@ -1,29 +1,37 @@
+Add tools for checking context for contact info
Add tools for .blocks
-Add tools for .numbers
Add tools for .addresses
-Add tools for .birthday
+
+list-birthdays
+set-contact-birthday
Test find-contact-by-chat
Test find-contact-by-email
Test find-contact-by-name
+Test find-contact-by-number
Test find-contact-by-organisation
Test find-contact-by-pgpkey
Test find-contact-by-photo
Test find-contact-by-site
+Test get-contact-birthday
Test get-contact-chats
Test get-contact-emails
Test get-contact-name
+Test get-contact-numbers
Test get-contact-organisations
Test get-contact-pgpkeys
Test get-contact-photos
Test get-contact-sites
+Test list-birthdays
Test list-chat-contacts
Test list-contact-organisations
Test list-organisation-contacts
+Test set-contact-birthday
Test set-contact-chats
Test set-contact-emails
Test set-contact-groups
Test set-contact-name
+Test set-contact-numbers
Test set-contact-organisations
Test set-contact-pgpkeys
Test set-contact-photos
diff --git a/add-contact.c b/add-contact.c
new file mode 100644
index 0000000..5a1b68c
--- /dev/null
+++ b/add-contact.c
@@ -0,0 +1,46 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[contact-id]");
+
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *user;
+ struct libcontacts_contact contact;
+ char *path;
+ int fd;
+
+ NOFLAGS(argc > 1);
+
+ if (argc && (!*argv[0] || strchr(argv[0], '/')))
+ usage();
+
+ errno = 0;
+ user = getpwuid(getuid());
+ if (!user)
+ eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist");
+
+ if (argc) {
+ path = libcontacts_get_path(argv[0], user);
+ if (!path)
+ eprintf("libcontacts_get_path %s:", argv[0]);
+ fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (fd < 0)
+ eprintf("open %s O_WRONLY|O_CREAT|O_EXCL 0666:", path);
+ if (close(fd))
+ eprintf("close %s:", path);
+ free(path);
+ } else {
+ memset(&contact, 0, sizeof(contact));
+ if (libcontacts_save_contact(&contact, user))
+ eprintf("libcontacts_save_contact:");
+ printf("%s\n", contact.id);
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout))
+ eprintf("printf:");
+ free(contact.id);
+ }
+
+ return 0;
+}
diff --git a/common.h b/common.h
index f4e1dad..ca6db51 100644
--- a/common.h
+++ b/common.h
@@ -4,6 +4,11 @@
#include <libsimple-arg.h>
+#ifndef BUFSIZ
+# define BUFSIZ 4096
+#endif
+
+
#ifdef MULTICALL_BINARY
# undef NUSAGE
# define NUSAGE(STATUS, SYNOPSIS)\
diff --git a/config.mk b/config.mk
index 2c119c1..7145091 100644
--- a/config.mk
+++ b/config.mk
@@ -5,4 +5,4 @@ CC = cc
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -I../libcontacts
CFLAGS = -std=c99 -Wall -O2
-LDFLAGS = -s -L../libcontacts -lcontacts -lsimple
+LDFLAGS = -L../libcontacts -lcontacts -lsimple
diff --git a/find-contact-by-number.c b/find-contact-by-number.c
new file mode 100644
index 0000000..e426c57
--- /dev/null
+++ b/find-contact-by-number.c
@@ -0,0 +1,104 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-c context] [-C local-country-calling-code] [-D address-book-country-calling-code] [-F | -f] [-M | -m] [-t] (-L | number)");
+
+
+int
+main(int argc, char *argv[])
+{
+ int list = 0, require_mobile = -1, require_fax = -1, display_type = 0;
+ struct passwd *user;
+ struct libcontacts_contact **contacts;
+ struct libcontacts_number **numbers, *number;
+ char *context = NULL, *cc_contacts = NULL, *cc_location = NULL;
+ size_t i;
+
+ ARGBEGIN {
+ case 'c':
+ if (context)
+ usage();
+ context = ARG();
+ break;
+ case 'C':
+ if (cc_location)
+ usage();
+ cc_location = ARG();
+ break;
+ case 'D':
+ if (cc_contacts)
+ usage();
+ cc_contacts = ARG();
+ break;
+ case 'F':
+ if (require_fax >= 0)
+ usage();
+ require_fax = 0;
+ break;
+ case 'f':
+ if (require_fax >= 0)
+ usage();
+ require_fax = 1;
+ break;
+ case 'M':
+ if (require_mobile >= 0)
+ usage();
+ require_mobile = 0;
+ break;
+ case 'm':
+ if (require_mobile >= 0)
+ usage();
+ require_mobile = 1;
+ break;
+ case 'L':
+ list = 1;
+ break;
+ case 't':
+ display_type = 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 ((numbers = contacts[i]->numbers)) {
+ for (; (number = *numbers); numbers++) {
+ if (!number->number)
+ continue;
+ if (require_mobile >= 0 && number->is_mobile != require_mobile)
+ continue;
+ if (require_fax >= 0 && number->is_facsimile != require_fax)
+ continue;
+ if (context && strcmpnul(number->context, context))
+ continue;
+ if (list) {
+ if (display_type)
+ printf("%c%c ", "-m"[number->is_mobile], "-f"[number->is_facsimile]);
+ printf("%s (%s)\n", contacts[i]->id, number->number);
+ } else if (libcontacts_same_number(number->number, cc_contacts, argv[0], cc_location)) {
+ if (display_type)
+ printf("%c%c ", "-m"[number->is_mobile], "-f"[number->is_facsimile]);
+ 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-birthday.c b/get-contact-birthday.c
new file mode 100644
index 0000000..29be768
--- /dev/null
+++ b/get-contact-birthday.c
@@ -0,0 +1,160 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-n] contact-id ...");
+
+
+static int
+get_age(struct libcontacts_birthday *bday, const struct tm *now)
+{
+ int age = now->tm_year + 1900 - (int)bday->year;
+ if (now->tm_mon + 1 < bday->month)
+ age -= 1;
+ else if (now->tm_mon + 1 == bday->month)
+ age -= (now->tm_mday < bday->day);
+ return age;
+}
+
+static void
+print_birthdate(struct libcontacts_birthday *bday, const struct tm *now)
+{
+ int age;
+ if (bday->year)
+ printf("%04u-", bday->year);
+ else
+ printf("??""??""-");
+ if (bday->month) {
+ printf("(%02u)%.3s-", (unsigned)bday->month,
+ &"JanFebMarAprMayJunJulAugSepOctNovDec"[3 * ((bday->month - 1) % 12)]);
+ } else {
+ printf("(??"")??""?-");
+ }
+ if (bday->day)
+ printf("%02u", (unsigned)bday->day);
+ else
+ printf("??");
+ if ((bday->month - 1) % 12 == 1 && bday->day == 29) {
+ bday->day -= bday->before_on_common;
+ printf(" (%s on common years)", bday->before_on_common ? "(02)Feb-28" : "(03)Mar-01");
+ }
+ if (bday->year && bday->month && bday->day) {
+ age = get_age(bday, now);
+ printf(", %i %s old today", age, age == 1 ? "year" : "years");
+ }
+ printf("\n");
+}
+
+static void
+print_birthday(struct libcontacts_birthday *bday, const struct tm *now)
+{
+ static const int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ int next_year = 0, leap_year = 0, year, age, days = 0, y, m, d;
+ struct tm when;
+ if (now->tm_mon + 1 > bday->month)
+ next_year = 1;
+ else if (now->tm_mon + 1 == bday->month)
+ next_year = (now->tm_mday > bday->day);
+ if (bday->year) {
+ printf("%04i-", now->tm_year + next_year + 1900);
+ } else {
+ printf("??""??""-");
+ }
+ year = now->tm_year + next_year + 1900;
+ if (year % 4 == 0 && (year % 100 || year % 400 == 0))
+ leap_year = 1;
+ if (((bday->month - 1) % 12 == 1 && bday->day == 29) && leap_year) {
+ if (bday->before_on_common) {
+ bday->day -= 1;
+ } else {
+ bday->day = 1;
+ bday->month += 1;
+ }
+ }
+ printf("(%02u)%.3s-%02u (", (unsigned)bday->month % 12,
+ &"JanFebMarAprMayJunJulAugSepOctNovDec"[3 * ((bday->month - 1) % 12)], (unsigned)bday->day);
+ when.tm_year = now->tm_year + next_year;
+ when.tm_mon = (bday->month - 1) % 12;
+ when.tm_mday = bday->day;
+ if (bday->year) {
+ age = get_age(bday, &when);
+ printf("%i %s ", age, age == 1 ? "year" : "years");
+ }
+ if (when.tm_mon == now->tm_mon && when.tm_mday == now->tm_mday) {
+ printf("today)\n");
+ } else {
+ y = now->tm_year;
+ m = now->tm_mon;
+ d = now->tm_mday;
+ while (m != when.tm_mon || d > when.tm_mday) {
+ days += days_in_month[m] - (d - 1);
+ days += (m == 1 && y % 4 == 0 && (y % 100 || y % 400 == 0));
+ d = 1;
+ if (++m == 12) {
+ y += 1;
+ m = 0;
+ }
+ }
+ days += when.tm_mday - d;
+ printf("in %i %s)\n", days, days == 1 ? "day" : "days");
+ }
+
+}
+
+int
+main(int argc, char *argv[])
+{
+ int next = 0;
+ struct passwd *user;
+ struct libcontacts_contact contact;
+ struct tm *now;
+ time_t tim;
+ int ret = 0;
+ size_t i;
+
+ ARGBEGIN {
+ case 'n':
+ next = 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");
+
+ tim = time(NULL);
+ now = localtime(&tim);
+ if (!now)
+ eprintf("localtime:");
+
+ 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 {
+ if (contact.birthday) {
+ if (next) {
+ print_birthday(contact.birthday, now);
+ } else if (contact.birthday->month && contact.birthday->year) {
+ if (argc > 1)
+ printf("%s: ", *argv);
+ print_birthdate(contact.birthday, now);
+ }
+ }
+ libcontacts_contact_destroy(&contact);
+ }
+ }
+
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout))
+ eprintf("printf:");
+ return ret;
+}
diff --git a/get-contact-file.c b/get-contact-file.c
index e244941..535d2f1 100644
--- a/get-contact-file.c
+++ b/get-contact-file.c
@@ -1,8 +1,6 @@
/* See LICENSE file for copyright and license details. */
#include "common.h"
-/* Useful for deleting or backing up contacts */
-
USAGE("contact-id");
@@ -30,5 +28,6 @@ main(int argc, char *argv[])
if (fflush(stdout) || ferror(stdout) || fclose(stdout))
eprintf("printf:");
+ free(path);
return 0;
}
diff --git a/get-contact-numbers.c b/get-contact-numbers.c
new file mode 100644
index 0000000..0d9c02b
--- /dev/null
+++ b/get-contact-numbers.c
@@ -0,0 +1,91 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-c context] [-F | -f] [-M | -m] contact-id ...");
+
+
+int
+main(int argc, char *argv[])
+{
+ int require_mobile = -1, require_fax = -1;
+ struct passwd *user;
+ struct libcontacts_contact contact;
+ struct libcontacts_number **numbers, *number;
+ const char *lookup_ctx = NULL;
+ int ret = 0;
+ size_t i;
+
+ ARGBEGIN {
+ case 'c':
+ if (lookup_ctx)
+ usage();
+ lookup_ctx = ARG();
+ break;
+ case 'F':
+ if (require_fax >= 0)
+ usage();
+ require_fax = 0;
+ break;
+ case 'f':
+ if (require_fax >= 0)
+ usage();
+ require_fax = 1;
+ break;
+ case 'M':
+ if (require_mobile >= 0)
+ usage();
+ require_mobile = 0;
+ break;
+ case 'm':
+ if (require_mobile >= 0)
+ usage();
+ require_mobile = 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;
+ continue;
+ }
+ if ((numbers = contact.numbers)) {
+ for (; (number = *numbers); numbers++) {
+ if (!number->number)
+ continue;
+ if (require_mobile >= 0 && number->is_mobile != require_mobile)
+ continue;
+ if (require_fax >= 0 && number->is_facsimile != require_fax)
+ continue;
+ if (lookup_ctx && strcmpnul(number->context, lookup_ctx))
+ continue;
+ if (argc > 1)
+ printf("%s: ", *argv);
+ printf("%c%c", "-m"[number->is_mobile], "-f"[number->is_facsimile]);
+ if (!lookup_ctx)
+ printf(" %s (%s)\n", number->number, number->context);
+ else
+ printf(" %s\n", number->number);
+ }
+ }
+ libcontacts_contact_destroy(&contact);
+ }
+
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout))
+ eprintf("printf:");
+ return ret;
+}
diff --git a/print-contact.c b/print-contact.c
new file mode 100644
index 0000000..7a9b92e
--- /dev/null
+++ b/print-contact.c
@@ -0,0 +1,55 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("contact-id");
+
+
+int
+main(int argc, char *argv[])
+{
+ char buf[BUFSIZ];
+ struct passwd *user;
+ char *path;
+ ssize_t r;
+ size_t n, p;
+ int fd;
+
+ NOFLAGS(argc != 1);
+
+ if (!*argv[0] || strchr(argv[0], '/'))
+ usage();
+
+ errno = 0;
+ user = getpwuid(getuid());
+ if (!user)
+ eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist");
+
+ path = libcontacts_get_path(argv[0], user);
+ if (!path)
+ eprintf("libcontacts_get_path %s:", argv[0]);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ eprintf("open %s O_RDONLY:", path);
+
+ for (;;) {
+ r = read(fd, buf, sizeof(buf));
+ if (r <= 0) {
+ if (!r)
+ break;
+ eprintf("read %s:", path);
+ }
+ n = (size_t)r;
+ for (p = 0; p < n; p += (size_t)r) {
+ r = write(STDOUT_FILENO, &buf[p], n - p);
+ if (r <= 0)
+ eprintf("write <stdout>:");
+ }
+ }
+
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout))
+ eprintf("printf:");
+ close(fd);
+ free(path);
+ return 0;
+}
diff --git a/remove-contact.c b/remove-contact.c
new file mode 100644
index 0000000..467abc2
--- /dev/null
+++ b/remove-contact.c
@@ -0,0 +1,32 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("contact-id");
+
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *user;
+ char *path;
+
+ NOFLAGS(argc != 1);
+
+ if (!*argv[0] || strchr(argv[0], '/'))
+ usage();
+
+ errno = 0;
+ user = getpwuid(getuid());
+ if (!user)
+ eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist");
+
+ path = libcontacts_get_path(argv[0], user);
+ if (!path)
+ eprintf("libcontacts_get_path %s:", argv[0]);
+
+ if (unlink(path))
+ eprintf("unlink %s:", path);
+
+ free(path);
+ return 0;
+}
diff --git a/set-contact-numbers.c b/set-contact-numbers.c
new file mode 100644
index 0000000..c03e175
--- /dev/null
+++ b/set-contact-numbers.c
@@ -0,0 +1,140 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-c | -n] [-F | -f] [-M | -m] contact-id context number | -u contact-id context [number] | -U contact-id [context] number");
+
+
+int
+main(int argc, char *argv[])
+{
+ int set_facsimile = -1, set_mobile = -1;
+ int update_number = 0, update_context = 0;
+ int remove_by_context = 0, remove_by_number = 0;
+ struct passwd *user;
+ struct libcontacts_contact contact;
+ struct libcontacts_number **r, **w;
+ size_t i;
+
+ ARGBEGIN {
+ case 'c':
+ update_context = 1;
+ break;
+ case 'n':
+ update_number = 1;
+ break;
+ case 'F':
+ if (set_facsimile >= 0)
+ return 0;
+ set_facsimile = 0;
+ break;
+ case 'f':
+ if (set_facsimile >= 0)
+ return 0;
+ set_facsimile = 1;
+ break;
+ case 'M':
+ if (set_mobile >= 0)
+ return 0;
+ set_mobile = 0;
+ break;
+ case 'm':
+ if (set_mobile >= 0)
+ return 0;
+ set_mobile = 1;
+ break;
+ case 'u':
+ remove_by_context = 1;
+ break;
+ case 'U':
+ remove_by_number = 1;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (update_number + update_context + remove_by_context + remove_by_number > 0)
+ usage();
+ if (argc < 3 - remove_by_context - remove_by_number || argc > 3)
+ usage();
+ if ((set_facsimile >= 0 || set_mobile >= 0) && remove_by_context + remove_by_number)
+ usage();
+
+ if (!*argv[0] || strchr(argv[0], '/'))
+ usage();
+
+ errno = 0;
+ user = getpwuid(getuid());
+ if (!user)
+ eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist");
+
+ if (libcontacts_load_contact(argv[0], &contact, user))
+ eprintf("libcontacts_load_contact %s: %s\n", argv[0], errno ? strerror(errno) : "contact file is malformatted");
+
+ i = 0;
+ if (contact.numbers) {
+ if (update_number) {
+ for (; contact.numbers[i]; i++) {
+ if (!strcmpnul(contact.numbers[i]->context, argv[1])) {
+ free(contact.numbers[i]->number);
+ contact.numbers[i]->number = estrdup(argv[2]);
+ goto save;
+ }
+ }
+ } else if (update_context) {
+ for (; contact.numbers[i]; i++) {
+ if (!strcmpnul(contact.numbers[i]->number, argv[2])) {
+ free(contact.numbers[i]->context);
+ contact.numbers[i]->context = estrdup(argv[1]);
+ goto save;
+ }
+ }
+ } else if (!remove_by_context && !remove_by_number) {
+ for (; contact.numbers[i]; i++) {
+ if (!strcmpnul(contact.numbers[i]->context, argv[1]))
+ if (!strcmpnul(contact.numbers[i]->number, argv[2]))
+ goto save;
+ }
+ } else if (argc == 3) {
+ for (; contact.numbers[i]; i++)
+ if (!strcmpnul(contact.numbers[i]->context, argv[1]))
+ if (!strcmpnul(contact.numbers[i]->number, argv[2]))
+ break;
+ } else if (remove_by_context) {
+ for (; contact.numbers[i]; i++)
+ if (!strcmpnul(contact.numbers[i]->context, argv[1]))
+ break;
+ } else {
+ for (; contact.numbers[i]; i++)
+ if (!strcmpnul(contact.numbers[i]->number, argv[1]))
+ break;
+ }
+ }
+ if (remove_by_context || remove_by_number) {
+ if (contact.numbers && contact.numbers[i]) {
+ libcontacts_number_destroy(contact.numbers[i]);
+ free(contact.numbers[i]);
+ for (r = &1[w = &contact.numbers[i]]; *r;)
+ *w++ = *r++;
+ *w = NULL;
+ }
+ } else {
+ contact.numbers = erealloc(contact.numbers, (i + 2) * sizeof(*contact.numbers));
+ contact.numbers[i + 1] = NULL;
+ contact.numbers[i] = ecalloc(1, sizeof(**contact.numbers));
+ contact.numbers[i]->context = estrdup(argv[1]);
+ contact.numbers[i]->number = estrdup(argv[2]);
+ contact.numbers[i]->is_mobile = set_mobile > 0;
+ contact.numbers[i]->is_facsimile = set_facsimile > 0;
+ }
+
+save:
+ if (set_mobile >= 0)
+ contact.numbers[i]->is_mobile = set_mobile;
+ if (set_facsimile >= 0)
+ contact.numbers[i]->is_facsimile = set_facsimile;
+ if (libcontacts_save_contact(&contact, user))
+ eprintf("libcontacts_save_contact %s:", argv[0]);
+ libcontacts_contact_destroy(&contact);
+
+ return 0;
+}