3 * $Id: database.c,v 1.39 2006/09/04 18:29:25 cduval Exp $
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 */
65 extern int first_list_item
;
67 extern char *selected
;
68 extern char *datafile
;
73 declare_standard_field(int i
)
75 abook_field
*f
= xmalloc(sizeof(abook_field
));
77 f
= memcpy(f
, &standard_fields
[i
], sizeof(abook_field
));
78 f
->name
= xstrdup(gettext(f
->name
));
80 add_field(&fields_list
, f
);
82 assert(standard_fields_indexed
[i
] == -1);
83 standard_fields_indexed
[i
] = fields_count
++;
89 find_standard_field(char *key
, int do_declare
)
93 for(i
= 0; standard_fields
[i
].key
; i
++)
94 if(0 == strcmp(standard_fields
[i
].key
, key
))
100 return do_declare
? declare_standard_field(i
) : &standard_fields
[i
];
103 /* Search for a field. Use the list of declared fields if no list specified. */
105 real_find_field(char *key
, abook_field_list
*list
, int *number
)
107 abook_field_list
*cur
;
110 for(cur
= (list
? list
: fields_list
), i
= 0; cur
; cur
= cur
->next
, i
++)
111 if(0 == strcmp(cur
->field
->key
, key
)) {
124 get_field_info(int i
, char **key
, char **name
, int *type
)
126 abook_field_list
*cur
= fields_list
;
129 assert(i
< fields_count
);
131 for(j
= 0; i
>= 0 && j
< i
; j
++, cur
= cur
->next
)
135 *key
= (i
< 0) ? NULL
: cur
->field
->key
;
137 *name
= (i
< 0) ? NULL
: cur
->field
->name
;
139 *type
= (i
< 0) ? -1 : cur
->field
->type
;
143 add_field(abook_field_list
**list
, abook_field
*f
)
145 abook_field_list
*tmp
;
147 for(tmp
= *list
; tmp
&& tmp
->next
; tmp
= tmp
->next
)
151 tmp
->next
= xmalloc(sizeof(abook_field_list
));
154 *list
= tmp
= xmalloc(sizeof(abook_field_list
));
161 declare_new_field(char *key
, char *name
, char *type
, int accept_standard
)
165 if(find_declared_field(key
))
166 return _("field already defined");
168 if(find_standard_field(key
, accept_standard
))
169 return accept_standard
? NULL
/* ok, added */ :
170 _("standard field does not need to be declared");
172 f
= xmalloc(sizeof(abook_field
));
173 f
->key
= xstrdup(key
);
174 f
->name
= xstrdup(name
);
176 if(!*type
|| (0 == strcasecmp("string", type
)))
177 f
->type
= FIELD_STRING
;
178 else if(0 == strcasecmp("emails", type
))
179 f
->type
= FIELD_EMAILS
;
180 else if(0 == strcasecmp("list", type
))
181 f
->type
= FIELD_LIST
;
182 else if(0 == strcasecmp("date", type
))
183 f
->type
= FIELD_DATE
;
185 return _("unknown type");
187 add_field(&fields_list
, f
);
194 * Declare a new field while database is already loaded
195 * making it grow accordingly
198 declare_unknown_field(char *key
)
202 declare_new_field(key
, key
, "string",
203 1 /* accept to declare "standard" fields */);
208 for(i
= 0; i
< items
; i
++)
210 database
[i
] = xrealloc(database
[i
], ITEM_SIZE
);
211 database
[i
][fields_count
- 1] = NULL
;
216 * Declare "standard" fields, thus preserving them while parsing a database,
217 * even if they won't be displayed.
220 init_standard_fields()
224 for(i
= 0; standard_fields
[i
].key
; i
++)
225 if(standard_fields_indexed
[i
] == -1)
226 declare_standard_field(i
);
229 /* Some initializations - Must be called _before_ load_opts() */
231 prepare_database_internals()
235 for(i
= 0; i
< ITEM_FIELDS
; i
++)
236 standard_fields_indexed
[i
] = -1;
238 /* the only two mandatory fields */
239 declare_standard_field(NAME
);
240 declare_standard_field(EMAIL
);
244 parse_database(FILE *in
)
251 item
= item_create();
256 if(item
[field_id(NAME
)] && sec
) {
257 add_item2database(item
);
264 if(!*line
|| *line
== '\n' || *line
== '#') {
266 } else if(*line
== '[') {
267 if(item
[field_id(NAME
)] && sec
) {
268 add_item2database(item
);
273 memset(item
, 0, ITEM_SIZE
);
274 if(!(tmp
= strchr(line
, ']')))
275 sec
= 0; /*incorrect section lines are skipped*/
276 } else if((tmp
= strchr(line
, '=') ) && sec
) {
278 find_field_number(line
, &field
);
280 item
[field
] = xstrdup(tmp
);
282 } else if(!strcasecmp(opt_get_str(STR_PRESERVE_FIELDS
),
284 declare_unknown_field(line
);
285 item
= xrealloc(item
, ITEM_SIZE
);
286 item
[fields_count
- 1] = xstrdup(tmp
);
300 load_database(char *filename
)
307 if ((in
= abook_fopen(filename
, "r")) == NULL
)
312 return (items
== 0) ? 2 : 0;
316 write_database(FILE *out
, struct db_enumerator e
)
320 abook_field_list
*cur
;
323 "# abook addressbook file\n\n"
325 "program=" PACKAGE
"\n"
326 "version=" VERSION
"\n"
330 db_enumerate_items(e
) {
331 fprintf(out
, "[%d]\n", i
);
333 for(cur
= fields_list
, j
= 0; cur
; cur
= cur
->next
, j
++) {
334 if( database
[e
.item
][j
] != NULL
&&
335 *database
[e
.item
][j
] )
336 fprintf(out
, "%s=%s\n",
354 struct db_enumerator e
= init_db_enumerator(ENUM_ALL
);
355 char *datafile_new
= strconcat(datafile
, ".new", NULL
);
356 char *datafile_old
= strconcat(datafile
, "~", NULL
);
358 if( (out
= abook_fopen(datafile_new
, "w")) == NULL
) {
365 * Possibly should check if write_database failed.
366 * Currently it returns always zero.
368 write_database(out
, e
);
372 if(access(datafile
, F_OK
) == 0 &&
373 (rename(datafile
, datafile_old
)) == -1)
376 if((rename(datafile_new
, datafile
)) == -1)
386 db_free_item(int item
)
388 item_empty(database
[item
]);
396 for(i
=0; i
<= LAST_ITEM
; i
++)
406 first_list_item
= curitem
= -1;
412 validate_item(list_item item
)
415 int i
, max_field_len
;
418 for(f
= fields_list
, i
= 0; f
; f
= f
->next
, i
++) {
421 switch(f
->field
->type
) {
423 max_field_len
= MAX_EMAILSTR_LEN
;
425 item
[i
] = xstrdup("");
428 /* TODO quote string if it contains commas */
431 max_field_len
= MAX_FIELD_LEN
;
439 if(max_field_len
&& item
[i
] &&
440 ((int)strlen(item
[i
]) > max_field_len
)) {
443 item
[i
][max_field_len
- 1] = 0;
444 item
[i
] = xstrdup(item
[i
]);
451 adjust_list_capacity()
453 if(list_capacity
< 1)
454 list_capacity
= INITIAL_LIST_CAPACITY
;
455 else if(items
>= list_capacity
)
457 else if(list_capacity
/ 2 > items
)
463 database
= xrealloc(database
,sizeof(list_item
) * list_capacity
);
464 else /* allocate memory _and_ initialize pointers to NULL */
465 database
= xmalloc0(sizeof(list_item
) * list_capacity
);
467 selected
= xrealloc(selected
, list_capacity
);
471 add_item2database(list_item item
)
473 /* 'name' field is mandatory */
474 if((item
[field_id(NAME
)] == NULL
) || ! *item
[field_id(NAME
)]) {
479 if(++items
> list_capacity
)
480 adjust_list_capacity();
484 selected
[LAST_ITEM
] = 0;
486 database
[LAST_ITEM
] = item_create();
487 item_copy(database
[LAST_ITEM
], item
);
494 remove_selected_items()
501 if(!selected_items())
502 selected
[curitem
] = 1;
504 for(j
= LAST_ITEM
; j
>= 0; j
--) {
506 db_free_item(j
); /* added for .4 data_s_ */
507 for(i
= j
; i
< LAST_ITEM
; i
++) {
508 item_copy(database
[i
], database
[i
+ 1]);
509 selected
[i
] = selected
[i
+ 1];
511 item_free(&database
[LAST_ITEM
]);
516 if(curitem
> LAST_ITEM
&& items
> 0)
519 adjust_list_capacity();
527 char *p
= s
+ strlen(s
);
531 while(p
> s
&& *(p
- 1) != ' ')
538 surnamecmp(const void *i1
, const void *i2
)
540 int ret
, idx
= field_id(NAME
);
541 char *n1
, *n2
, *s1
, *s2
;
543 n1
= (*(list_item
*)i1
)[idx
];
544 n2
= (*(list_item
*)i2
)[idx
];
546 s1
= get_surname(n1
);
547 s2
= get_surname(n2
);
549 if( !(ret
= safe_strcoll(s1
, s2
)) )
550 ret
= safe_strcoll(n1
, n2
);
558 static int sort_field
= -1;
561 namecmp(const void *i1
, const void *i2
)
565 assert(sort_field
>= 0 && sort_field
< fields_count
);
567 n1
= (*(list_item
*)i1
)[sort_field
];
568 n2
= (*(list_item
*)i2
)[sort_field
];
570 return safe_strcoll(n1
, n2
);
574 sort_by_field(char *name
)
580 name
= (name
== NULL
) ? opt_get_str(STR_SORT_FIELD
) : name
;
581 find_field_number(name
, &field
);
584 if(name
== opt_get_str(STR_SORT_FIELD
))
585 statusline_msg(_("Invalid field value defined "
586 "in configuration"));
588 statusline_msg(_("Invalid field value for sorting"));
595 qsort((void *)database
, items
, sizeof(list_item
), namecmp
);
605 qsort((void *)database
, items
, sizeof(list_item
), surnamecmp
);
610 /* TODO implement a search based on more sophisticated patterns */
612 find_item(char *str
, int start
, int search_fields
[])
615 char *findstr
= NULL
;
617 int ret
= -1; /* not found */
618 struct db_enumerator e
= init_db_enumerator(ENUM_ALL
);
620 if(list_is_empty() || !is_valid_item(start
))
621 return -2; /* error */
623 findstr
= xstrdup(str
);
624 findstr
= strlower(findstr
);
626 e
.item
= start
- 1; /* must be "real start" - 1 */
627 db_enumerate_items(e
) {
628 for(i
= 0; search_fields
[i
] >= 0; i
++) {
629 if((id
= field_id(search_fields
[i
])) == -1)
631 if(database
[e
.item
][id
] == NULL
)
633 tmp
= xstrdup(database
[e
.item
][id
]);
634 if( tmp
&& strstr(strlower(tmp
), findstr
) ) {
649 is_selected(int item
)
651 return selected
[item
];
655 is_valid_item(int item
)
657 return item
<= LAST_ITEM
&& item
>= 0;
673 real_db_enumerate_items(struct db_enumerator e
)
675 int item
= max(0, e
.item
+ 1);
684 for(i
= item
; i
<= LAST_ITEM
; i
++) {
693 fprintf(stderr
, "real_db_enumerate_items() "
694 "BUG: unknown db_enumerator mode: %d\n",
700 return (item
> LAST_ITEM
|| item
< 0) ? -1 : item
;
704 init_db_enumerator(int mode
)
706 struct db_enumerator e
;
708 e
.item
= -1; /* important - means "start from beginning" */
718 return xmalloc0(ITEM_SIZE
);
722 item_free(list_item
*item
)
730 item_empty(list_item item
)
735 for(i
= 0; i
< fields_count
; i
++)
742 item_copy(list_item dest
, list_item src
)
744 memmove(dest
, src
, ITEM_SIZE
);
748 item_duplicate(list_item dest
, list_item src
)
752 for(i
= 0; i
< fields_count
; i
++)
753 dest
[i
] = src
[i
] ? xstrdup(src
[i
]) : NULL
;
757 * Things like item[field_id(NICK)] should never be used, since besides NAME
758 * and EMAIL, none of the standard fields can be assumed to be existing.
760 * Prefer the functions item_fput(), item_fget(), db_fput() and db_fget()
761 * to access fields in items and database.
764 /* quick lookup by "standard" field number */
768 assert((i
>= 0) && (i
< ITEM_FIELDS
));
769 return standard_fields_indexed
[i
];
773 item_fput(list_item item
, int i
, char *val
)
775 int id
= field_id(i
);
786 item_fget(list_item item
, int i
)
788 int id
= field_id(i
);
797 real_db_field_put(int item
, int i
, int std
, char *val
)
801 assert(database
[item
]);
803 id
= std
? field_id(i
) : i
;
806 database
[item
][id
] = val
;
814 real_db_field_get(int item
, int i
, int std
)
818 assert(database
[item
]);
820 id
= std
? field_id(i
) : i
;
823 return database
[item
][id
];
834 /* Fetch addresses from all fields of FIELD_EMAILS type */
835 /* Memory has to be freed by the caller */
837 db_email_get(int item
)
841 abook_field_list
*cur
;
842 abook_list
*emails
= NULL
;
844 for(cur
= fields_list
, i
= 0; cur
; cur
= cur
->next
, i
++)
845 if(cur
->field
->type
== FIELD_EMAILS
&& *database
[item
][i
])
846 abook_list_append(&emails
, database
[item
][i
]);
848 res
= abook_list_to_csv(emails
);
849 abook_list_free(&emails
);
850 return res
? res
: xstrdup("");