5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
25 abook_field_list *fields_list = NULL;
28 list_item *database = NULL;
31 #define ITEM_SIZE (fields_count * sizeof(char *))
32 #define LAST_ITEM (items - 1)
34 #define INITIAL_LIST_CAPACITY 30
35 static int list_capacity = 0;
37 int standard_fields_indexed[ITEM_FIELDS];
40 * notes about adding predefined "standard" fields:
41 * - leave alone "name" and "email"
42 * - reorganize the field numbers in database.h
44 abook_field standard_fields[] = {
45 {"name", N_("Name"), FIELD_STRING}, /* NAME */
46 {"email", N_("E-mail addresses"), FIELD_EMAILS}, /* EMAIL */
47 {"address", N_("Address"), FIELD_STRING}, /* ADDRESS */
48 {"address2", N_("Address2"), FIELD_STRING}, /* ADDRESS2 */
49 {"city", N_("City"), FIELD_STRING}, /* CITY */
50 {"state", N_("State/Province"), FIELD_STRING}, /* STATE */
51 {"zip", N_("ZIP/Postal Code"), FIELD_STRING}, /* ZIP */
52 {"country", N_("Country"), FIELD_STRING}, /* COUNTRY */
53 {"phone", N_("Home Phone"), FIELD_STRING}, /* PHONE */
54 {"workphone", N_("Work Phone"), FIELD_STRING}, /* WORKPHONE */
55 {"fax", N_("Fax"), FIELD_STRING}, /* FAX */
56 {"mobile", N_("Mobile"), FIELD_STRING}, /* MOBILEPHONE */
57 {"nick", N_("Nickname/Alias"), FIELD_STRING}, /* NICK */
58 {"url", N_("URL"), FIELD_STRING}, /* URL */
59 {"notes", N_("Notes"), FIELD_STRING}, /* NOTES */
60 {"anniversary", N_("Anniversary day"), FIELD_DATE}, /* ANNIVERSARY */
61 {"groups", N_("Groups"), FIELD_LIST}, /* GROUPS */
66 extern int first_list_item;
68 extern char *selected;
69 extern char *datafile;
74 declare_standard_field(int i)
76 abook_field *f = xmalloc(sizeof(abook_field));
78 f = memcpy(f, &standard_fields[i], sizeof(abook_field));
79 f->name = xstrdup(gettext(f->name));
81 add_field(&fields_list, f);
83 assert(standard_fields_indexed[i] == -1);
84 standard_fields_indexed[i] = fields_count++;
90 find_standard_field(char *key, int do_declare)
94 for(i = 0; standard_fields[i].key; i++)
95 if(0 == strcmp(standard_fields[i].key, key))
101 return do_declare ? declare_standard_field(i) : &standard_fields[i];
104 /* Search for a field. Use the list of declared fields if no list specified. */
106 real_find_field(char *key, abook_field_list *list, int *number)
108 abook_field_list *cur;
111 for(cur = (list ? list : fields_list), i = 0; cur; cur = cur->next, i++)
112 if(0 == strcmp(cur->field->key, key)) {
125 get_field_info(int i, char **key, char **name, int *type)
127 abook_field_list *cur = fields_list;
130 assert(i < fields_count);
132 for(j = 0; i >= 0 && j < i; j++, cur = cur->next)
136 *key = (i < 0) ? NULL : cur->field->key;
138 *name = (i < 0) ? NULL : cur->field->name;
140 *type = (i < 0) ? -1 : cur->field->type;
144 add_field(abook_field_list **list, abook_field *f)
146 abook_field_list *tmp;
148 for(tmp = *list; tmp && tmp->next; tmp = tmp->next)
152 tmp->next = xmalloc(sizeof(abook_field_list));
155 *list = tmp = xmalloc(sizeof(abook_field_list));
162 declare_new_field(char *key, char *name, char *type, int accept_standard)
166 if(find_declared_field(key))
167 return _("field already defined");
169 if(find_standard_field(key, accept_standard))
170 return accept_standard ? NULL /* ok, added */ :
171 _("standard field does not need to be declared");
173 f = xmalloc(sizeof(abook_field));
174 f->key = xstrdup(key);
175 f->name = xstrdup(name);
177 if(!*type || (0 == strcasecmp("string", type)))
178 f->type = FIELD_STRING;
179 else if(0 == strcasecmp("emails", type))
180 f->type = FIELD_EMAILS;
181 else if(0 == strcasecmp("list", type))
182 f->type = FIELD_LIST;
183 else if(0 == strcasecmp("date", type))
184 f->type = FIELD_DATE;
186 return _("unknown type");
188 add_field(&fields_list, f);
195 * Declare a new field while database is already loaded
196 * making it grow accordingly
199 declare_unknown_field(char *key)
203 declare_new_field(key, key, "string",
204 1 /* accept to declare "standard" fields */);
209 for(i = 0; i < items; i++)
211 database[i] = xrealloc(database[i], ITEM_SIZE);
212 database[i][fields_count - 1] = NULL;
217 * Declare "standard" fields, thus preserving them while parsing a database,
218 * even if they won't be displayed.
221 init_standard_fields()
225 for(i = 0; standard_fields[i].key; i++)
226 if(standard_fields_indexed[i] == -1)
227 declare_standard_field(i);
230 /* Some initializations - Must be called _before_ load_opts() */
232 prepare_database_internals()
236 for(i = 0; i < ITEM_FIELDS; i++)
237 standard_fields_indexed[i] = -1;
239 /* the only two mandatory fields */
240 declare_standard_field(NAME);
241 declare_standard_field(EMAIL);
245 parse_database(FILE *in)
252 item = item_create();
257 if(item[field_id(NAME)] && sec) {
258 add_item2database(item);
265 if(!*line || *line == '\n' || *line == '#') {
267 } else if(*line == '[') {
268 if(item[field_id(NAME)] && sec ) {
269 add_item2database(item);
274 memset(item, 0, ITEM_SIZE);
275 if(!(tmp = strchr(line, ']')))
276 sec = 0; /*incorrect section lines are skipped*/
277 } else if((tmp = strchr(line, '=') ) && sec) {
279 find_field_number(line, &field);
281 item[field] = xstrdup(tmp);
283 } else if(!strcasecmp(opt_get_str(STR_PRESERVE_FIELDS),
285 declare_unknown_field(line);
286 item = xrealloc(item, ITEM_SIZE);
287 item[fields_count - 1] = xstrdup(tmp);
301 load_database(char *filename)
308 if ((in = abook_fopen(filename, "r")) == NULL)
313 return (items == 0) ? 2 : 0;
317 write_database(FILE *out, struct db_enumerator e)
321 abook_field_list *cur;
324 "# abook addressbook file\n\n"
326 "program=" PACKAGE "\n"
327 "version=" VERSION "\n"
331 db_enumerate_items(e) {
332 fprintf(out, "[%d]\n", i);
334 for(cur = fields_list, j = 0; cur; cur = cur->next, j++) {
335 if( database[e.item][j] != NULL &&
336 *database[e.item][j] )
337 fprintf(out, "%s=%s\n",
355 struct db_enumerator e = init_db_enumerator(ENUM_ALL);
356 char *datafile_new = strconcat(datafile, ".new", NULL);
357 char *datafile_old = strconcat(datafile, "~", NULL);
359 if( (out = abook_fopen(datafile_new, "w")) == NULL ) {
366 * Possibly should check if write_database failed.
367 * Currently it returns always zero.
369 write_database(out, e);
373 if(access(datafile, F_OK) == 0 &&
374 (rename(datafile, datafile_old)) == -1)
377 if((rename(datafile_new, datafile)) == -1)
387 db_free_item(int item)
389 item_empty(database[item]);
397 for(i=0; i <= LAST_ITEM; i++)
407 first_list_item = curitem = -1;
413 validate_item(list_item item)
416 int i, max_field_len;
419 for(f = fields_list, i = 0; f; f = f->next, i++) {
422 switch(f->field->type) {
424 max_field_len = MAX_EMAILSTR_LEN;
426 item[i] = xstrdup("");
429 /* TODO quote string if it contains commas */
432 max_field_len = MAX_FIELD_LEN;
440 if(max_field_len && item[i] &&
441 ((int)strlen(item[i]) > max_field_len)) {
444 item[i][max_field_len - 1] = 0;
445 item[i] = xstrdup(item[i]);
452 adjust_list_capacity()
454 if(list_capacity < 1)
455 list_capacity = INITIAL_LIST_CAPACITY;
456 else if(items >= list_capacity)
458 else if(list_capacity / 2 > items)
464 database = xrealloc(database,sizeof(list_item) * list_capacity);
465 else /* allocate memory _and_ initialize pointers to NULL */
466 database = xmalloc0(sizeof(list_item) * list_capacity);
468 selected = xrealloc(selected, list_capacity);
472 add_item2database(list_item item)
474 /* 'name' field is mandatory */
475 if((item[field_id(NAME)] == NULL) || ! *item[field_id(NAME)]) {
480 if(++items > list_capacity)
481 adjust_list_capacity();
485 selected[LAST_ITEM] = 0;
487 database[LAST_ITEM] = item_create();
488 item_copy(database[LAST_ITEM], item);
495 remove_selected_items()
502 if(!selected_items())
503 selected[curitem] = 1;
505 for(j = LAST_ITEM; j >= 0; j--) {
507 db_free_item(j); /* added for .4 data_s_ */
508 for(i = j; i < LAST_ITEM; i++) {
509 item_copy(database[i], database[i + 1]);
510 selected[i] = selected[i + 1];
512 item_free(&database[LAST_ITEM]);
517 if(curitem > LAST_ITEM && items > 0)
520 adjust_list_capacity();
525 void merge_selected_items()
530 if((list_is_empty()) || (selected_items() < 2))
533 /* Find the top item */
534 for(j=0; destitem < 0; j++)
539 for(j = LAST_ITEM; j > destitem; j--) {
541 item_merge(database[destitem],database[j]);
542 for(i = j; i < LAST_ITEM; i++) {
543 /* TODO: this can be done by moving pointers */
544 item_copy(database[i], database[i + 1]);
545 selected[i] = selected[i + 1];
547 item_free(&database[LAST_ITEM]);
552 if(curitem > LAST_ITEM && items > 0)
555 adjust_list_capacity();
560 void remove_duplicates()
567 /* Scan from the last one */
568 for(j = LAST_ITEM - 1; j >= 0; j--) {
569 tmpj = db_name_get(j);
570 for(i = LAST_ITEM; i > j; i--)
571 /* Check name and merge if dups */
572 if (0 == strcmp(tmpj,db_name_get(i))) {
573 item_merge(database[j],database[i]);
574 if (curitem == i) curitem--;
575 for(k = i; k < LAST_ITEM; k++) {
576 item_copy(database[k], database[k + 1]);
578 item_free(&database[LAST_ITEM]);
583 adjust_list_capacity();
590 char *p = s + strlen(s);
594 while(p > s && *(p - 1) != ' ')
601 surnamecmp(const void *i1, const void *i2)
603 int ret, idx = field_id(NAME);
604 char *n1, *n2, *s1, *s2;
606 n1 = (*(list_item *)i1)[idx];
607 n2 = (*(list_item *)i2)[idx];
609 s1 = get_surname(n1);
610 s2 = get_surname(n2);
612 if( !(ret = safe_strcoll(s1, s2)) )
613 ret = safe_strcoll(n1, n2);
621 static int sort_field = -1;
624 namecmp(const void *i1, const void *i2)
628 assert(sort_field >= 0 && sort_field < fields_count);
630 n1 = (*(list_item *)i1)[sort_field];
631 n2 = (*(list_item *)i2)[sort_field];
633 return safe_strcoll(n1, n2);
637 sort_by_field(char *name)
643 name = (name == NULL) ? opt_get_str(STR_SORT_FIELD) : name;
644 find_field_number(name, &field);
647 if(name == opt_get_str(STR_SORT_FIELD))
648 statusline_msg(_("Invalid field value defined "
649 "in configuration"));
651 statusline_msg(_("Invalid field value for sorting"));
658 qsort((void *)database, items, sizeof(list_item), namecmp);
668 qsort((void *)database, items, sizeof(list_item), surnamecmp);
673 /* TODO implement a search based on more sophisticated patterns */
675 find_item(char *str, int start, int search_fields[])
678 char *findstr = NULL;
680 int ret = -1; /* not found */
681 struct db_enumerator e = init_db_enumerator(ENUM_ALL);
683 if(list_is_empty() || !is_valid_item(start))
684 return -2; /* error */
686 findstr = xstrdup(str);
687 findstr = strlower(findstr);
689 e.item = start - 1; /* must be "real start" - 1 */
690 db_enumerate_items(e) {
691 for(i = 0; search_fields[i] >= 0; i++) {
692 if((id = field_id(search_fields[i])) == -1)
694 if(database[e.item][id] == NULL)
696 tmp = xstrdup(database[e.item][id]);
697 if( tmp && strstr(strlower(tmp), findstr) ) {
712 is_selected(int item)
714 return selected[item];
718 is_valid_item(int item)
720 return item <= LAST_ITEM && item >= 0;
736 real_db_enumerate_items(struct db_enumerator e)
738 int item = max(0, e.item + 1);
747 for(i = item; i <= LAST_ITEM; i++) {
756 fprintf(stderr, "real_db_enumerate_items() "
757 "BUG: unknown db_enumerator mode: %d\n",
763 return (item > LAST_ITEM || item < 0) ? -1 : item;
767 init_db_enumerator(int mode)
769 struct db_enumerator e;
771 e.item = -1; /* important - means "start from beginning" */
781 return xmalloc0(ITEM_SIZE);
785 item_free(list_item *item)
793 item_empty(list_item item)
798 for(i = 0; i < fields_count; i++)
805 item_copy(list_item dest, list_item src)
807 memmove(dest, src, ITEM_SIZE);
811 item_duplicate(list_item dest, list_item src)
815 for(i = 0; i < fields_count; i++)
816 dest[i] = src[i] ? xstrdup(src[i]) : NULL;
820 * Merging works as follows:
821 * - fields present only in source are copied over to dest
822 * - multi-fields (email, groups) are checked for dupes ad merged
825 item_merge(list_item dest, list_item src)
828 abook_list *dfield, *sfield, *ed, *es;
830 for(i = 0; i < fields_count; i++)
833 dest[i] = xstrdup(src[i]);
834 else if((i == field_id(EMAIL)) || (i == field_id(GROUPS))) {
835 dfield = csv_to_abook_list(dest[i]);
836 sfield = csv_to_abook_list(src[i]);
837 for(es = sfield; es; es = es->next) {
838 for(found=0, ed = dfield; (!found) && ed; ed = ed->next)
839 found = (0 == strcmp(es->data,ed->data));
841 abook_list_append(&dfield, es->data);
844 dest[i] = abook_list_to_csv(dfield);
845 abook_list_free(&dfield);
846 abook_list_free(&sfield);
854 * Things like item[field_id(NICK)] should never be used, since besides NAME
855 * and EMAIL, none of the standard fields can be assumed to be existing.
857 * Prefer the functions item_fput(), item_fget(), db_fput() and db_fget()
858 * to access fields in items and database.
861 /* quick lookup by "standard" field number */
865 assert((i >= 0) && (i < ITEM_FIELDS));
866 return standard_fields_indexed[i];
870 item_fput(list_item item, int i, char *val)
872 int id = field_id(i);
883 item_fget(list_item item, int i)
885 int id = field_id(i);
894 real_db_field_put(int item, int i, int std, char *val)
898 assert(database[item]);
900 id = std ? field_id(i) : i;
903 database[item][id] = val;
911 real_db_field_get(int item, int i, int std)
915 assert(database[item]);
917 id = std ? field_id(i) : i;
920 return database[item][id];
931 /* Fetch addresses from all fields of FIELD_EMAILS type */
932 /* Memory has to be freed by the caller */
934 db_email_get(int item)
938 abook_field_list *cur;
939 abook_list *emails = NULL;
941 for(cur = fields_list, i = 0; cur; cur = cur->next, i++)
942 if(cur->field->type == FIELD_EMAILS && *database[item][i])
943 abook_list_append(&emails, database[item][i]);
945 res = abook_list_to_csv(emails);
946 abook_list_free(&emails);
947 return res ? res : xstrdup("");