int fields_count = 0;
list_item *database = NULL;
-int items = 0;
+static int items = 0;
#define ITEM_SIZE (fields_count * sizeof(char *))
+#define LAST_ITEM (items - 1)
#define INITIAL_LIST_CAPACITY 30
static int list_capacity = 0;
{"nick", N_("Nickname/Alias"), FIELD_STRING}, /* NICK */
{"url", N_("URL"), FIELD_STRING}, /* URL */
{"notes", N_("Notes"), FIELD_STRING}, /* NOTES */
- {"anniversary", N_("Anniversary day"), FIELD_DAY}, /* ANNIVERSARY */
+ {"anniversary", N_("Anniversary day"), FIELD_DATE}, /* ANNIVERSARY */
+ {"groups", N_("Groups"), FIELD_LIST}, /* GROUPS */
{0} /* ITEM_FIELDS */
};
}
void
-get_field_keyname(int i, char **key, char **name)
+get_field_info(int i, char **key, char **name, int *type)
{
abook_field_list *cur = fields_list;
int j;
*key = (i < 0) ? NULL : cur->field->key;
if(name)
*name = (i < 0) ? NULL : cur->field->name;
+ if(type)
+ *type = (i < 0) ? -1 : cur->field->type;
}
void
f->type = FIELD_EMAILS;
else if(0 == strcasecmp("list", type))
f->type = FIELD_LIST;
- else if(0 == strcasecmp("day", type))
- f->type = FIELD_DAY;
+ else if(0 == strcasecmp("date", type))
+ f->type = FIELD_DATE;
else
return _("unknown type");
if(!database)
return;
- for(i = 0; i < fields_count; i++)
- if(database[i])
+ for(i = 0; i < items; i++)
+ if(database[i]) {
database[i] = xrealloc(database[i], ITEM_SIZE);
+ database[i][fields_count - 1] = NULL;
+ }
}
/*
save_database()
{
FILE *out;
+ int ret = 0;
struct db_enumerator e = init_db_enumerator(ENUM_ALL);
+ char *datafile_new = strconcat(datafile, ".new", NULL);
+ char *datafile_old = strconcat(datafile, "~", NULL);
- if( (out = abook_fopen(datafile, "w")) == NULL )
- return -1;
-
- if(list_is_empty()) {
- fclose(out);
- unlink(datafile);
- return 1;
+ if( (out = abook_fopen(datafile_new, "w")) == NULL ) {
+ ret = -1;
+ goto out;
}
-
- write_database(out, e);
+ if(!list_is_empty())
+ /*
+ * Possibly should check if write_database failed.
+ * Currently it returns always zero.
+ */
+ write_database(out, e);
fclose(out);
- return 0;
+ if(access(datafile, F_OK) == 0 &&
+ (rename(datafile, datafile_old)) == -1)
+ ret = -1;
+
+ if((rename(datafile_new, datafile)) == -1)
+ ret = -1;
+
+out:
+ free(datafile_new);
+ free(datafile_old);
+ return ret;
}
static void
case FIELD_STRING:
max_field_len = MAX_FIELD_LEN;
break;
- case FIELD_DAY:
+ case FIELD_DATE:
break;
default:
assert(0);
select_none();
}
+void merge_selected_items()
+{
+ int i, j;
+ int destitem = -1;
+
+ if((list_is_empty()) || (selected_items() < 2))
+ return;
+
+ /* Find the top item */
+ for(j=0; destitem < 0; j++)
+ if(selected[j])
+ destitem = j;
+
+ /* Merge pairwise */
+ for(j = LAST_ITEM; j > destitem; j--) {
+ if(selected[j]) {
+ item_merge(database[destitem],database[j]);
+ for(i = j; i < LAST_ITEM; i++) {
+ /* TODO: this can be done by moving pointers */
+ item_copy(database[i], database[i + 1]);
+ selected[i] = selected[i + 1];
+ }
+ item_free(&database[LAST_ITEM]);
+ items--;
+ }
+ }
+
+ if(curitem > LAST_ITEM && items > 0)
+ curitem = LAST_ITEM;
+
+ adjust_list_capacity();
+
+ select_none();
+}
+
+void remove_duplicates()
+{
+ int i,j,k;
+ char *tmpj;
+ if(list_is_empty())
+ return;
+
+ /* Scan from the last one */
+ for(j = LAST_ITEM - 1; j >= 0; j--) {
+ tmpj = db_name_get(j);
+ for(i = LAST_ITEM; i > j; i--)
+ /* Check name and merge if dups */
+ if (0 == strcmp(tmpj,db_name_get(i))) {
+ item_merge(database[j],database[i]);
+ if (curitem == i) curitem--;
+ for(k = i; k < LAST_ITEM; k++) {
+ item_copy(database[k], database[k + 1]);
+ }
+ item_free(&database[LAST_ITEM]);
+ items--;
+ }
+ }
+
+ adjust_list_capacity();
+}
+
+
char *
get_surname(char *s)
{
int ret, idx = field_id(NAME);
char *n1, *n2, *s1, *s2;
- if(idx == 0)
- return 0; /* no 'name' field */
-
n1 = (*(list_item *)i1)[idx];
n2 = (*(list_item *)i2)[idx];
return item <= LAST_ITEM && item >= 0;
}
+int
+last_item()
+{
+ return LAST_ITEM;
+}
+
+int
+db_n_items()
+{
+ return items;
+}
int
real_db_enumerate_items(struct db_enumerator e)
for(i = 0; i < fields_count; i++)
dest[i] = src[i] ? xstrdup(src[i]) : NULL;
}
+
+/*
+ * Merging works as follows:
+ * - fields present only in source are copied over to dest
+ * - multi-fields (email, groups) are checked for dupes ad merged
+ * */
+void
+item_merge(list_item dest, list_item src)
+{
+ int i, found = 0;
+ abook_list *dfield, *sfield, *ed, *es;
+
+ for(i = 0; i < fields_count; i++)
+ if (src[i]) {
+ if (!dest[i])
+ dest[i] = xstrdup(src[i]);
+ else if((i == field_id(EMAIL)) || (i == field_id(GROUPS))) {
+ dfield = csv_to_abook_list(dest[i]);
+ sfield = csv_to_abook_list(src[i]);
+ for(es = sfield; es; es = es->next) {
+ for(found=0, ed = dfield; (!found) && ed; ed = ed->next)
+ found = (0 == strcmp(es->data,ed->data));
+ if (!found)
+ abook_list_append(&dfield, es->data);
+ }
+ xfree(dest[i]);
+ dest[i] = abook_list_to_csv(dfield);
+ abook_list_free(&dfield);
+ abook_list_free(&sfield);
+ }
+ }
+
+ item_empty(src);
+}
/*
* Things like item[field_id(NICK)] should never be used, since besides NAME
return database[i];
}
+/* Fetch addresses from all fields of FIELD_EMAILS type */
+/* Memory has to be freed by the caller */
+char *
+db_email_get(int item)
+{
+ int i;
+ char *res;
+ abook_field_list *cur;
+ abook_list *emails = NULL;
+
+ for(cur = fields_list, i = 0; cur; cur = cur->next, i++)
+ if(cur->field->type == FIELD_EMAILS && *database[item][i])
+ abook_list_append(&emails, database[item][i]);
+
+ res = abook_list_to_csv(emails);
+ abook_list_free(&emails);
+ return res ? res : xstrdup("");
+}
+