]> git.deb.at Git - pkg/abook.git/blob - database.c
Upload 0.6.1-2 to unstable
[pkg/abook.git] / database.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@users.sourceforge.net>
6  *
7  * Copyright (C) Jaakko Heinonen
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <assert.h>
15 #ifdef HAVE_CONFIG_H
16 #       include "config.h"
17 #endif
18 #include "abook.h"
19 #include "database.h"
20 #include "gettext.h"
21 #include "list.h"
22 #include "misc.h"
23 #include "xmalloc.h"
24
25 abook_field_list *fields_list = NULL;
26 int fields_count = 0;
27
28 list_item *database = NULL;
29 static int items = 0;
30
31 #define ITEM_SIZE (fields_count * sizeof(char *))
32 #define LAST_ITEM (items - 1)
33
34 #define INITIAL_LIST_CAPACITY   30
35 static int list_capacity = 0;
36
37 int standard_fields_indexed[ITEM_FIELDS];
38
39 /*
40  * notes about adding predefined "standard" fields:
41  *      - leave alone "name" and "email"
42  *      - reorganize the field numbers in database.h
43  */
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 */
62         {0} /* ITEM_FIELDS */
63 };
64
65
66 extern int first_list_item;
67 extern int curitem;
68 extern char *selected;
69 extern char *datafile;
70
71
72
73 static abook_field *
74 declare_standard_field(int i)
75 {
76         abook_field *f = xmalloc(sizeof(abook_field));
77
78         f = memcpy(f, &standard_fields[i], sizeof(abook_field));
79         f->name = xstrdup(gettext(f->name));
80
81         add_field(&fields_list, f);
82
83         assert(standard_fields_indexed[i] == -1);
84         standard_fields_indexed[i] = fields_count++;
85
86         return f;
87 }
88
89 abook_field *
90 find_standard_field(char *key, int do_declare)
91 {
92         int i;
93
94         for(i = 0; standard_fields[i].key; i++)
95                 if(0 == strcmp(standard_fields[i].key, key))
96                         goto found;
97
98         return NULL;
99
100 found:
101         return do_declare ? declare_standard_field(i) : &standard_fields[i];
102 }
103
104 /* Search for a field. Use the list of declared fields if no list specified. */
105 abook_field *
106 real_find_field(char *key, abook_field_list *list, int *number)
107 {
108         abook_field_list *cur;
109         int i;
110
111         for(cur = (list ? list : fields_list), i = 0; cur; cur = cur->next, i++)
112                 if(0 == strcmp(cur->field->key, key)) {
113                         if(number)
114                                 *number = i;
115                         return cur->field;
116                 }
117
118         if(number)
119                 *number = -1;
120
121         return NULL;
122 }
123
124 void
125 get_field_info(int i, char **key, char **name, int *type)
126 {
127         abook_field_list *cur = fields_list;
128         int j;
129
130         assert(i < fields_count);
131
132         for(j = 0; i >= 0 && j < i; j++, cur = cur->next)
133                 ;
134
135         if(key)
136                 *key = (i < 0) ? NULL : cur->field->key;
137         if(name)
138                 *name = (i < 0) ? NULL : cur->field->name;
139         if(type)
140                 *type = (i < 0) ? -1 : cur->field->type;
141 }
142
143 void
144 add_field(abook_field_list **list, abook_field *f)
145 {
146         abook_field_list *tmp;
147
148         for(tmp = *list; tmp && tmp->next; tmp = tmp->next)
149                 ;
150
151         if(tmp) {
152                 tmp->next = xmalloc(sizeof(abook_field_list));
153                 tmp = tmp->next;
154         } else
155                 *list = tmp = xmalloc(sizeof(abook_field_list));
156
157         tmp->field = f;
158         tmp->next = NULL;
159 }
160
161 char *
162 declare_new_field(char *key, char *name, char *type, int accept_standard)
163 {
164         abook_field *f;
165
166         if(find_declared_field(key))
167                 return _("field already defined");
168
169         if(find_standard_field(key, accept_standard))
170                 return accept_standard ? NULL /* ok, added */ :
171                         _("standard field does not need to be declared");
172
173         f = xmalloc(sizeof(abook_field));
174         f->key = xstrdup(key);
175         f->name = xstrdup(name);
176
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;
185         else
186                 return _("unknown type");
187
188         add_field(&fields_list, f);
189         fields_count++;
190
191         return NULL;
192 }
193
194 /*
195  * Declare a new field while database is already loaded
196  * making it grow accordingly
197  */
198 static void
199 declare_unknown_field(char *key)
200 {
201         int i;
202
203         declare_new_field(key, key, "string",
204                         1 /* accept to declare "standard" fields */);
205
206         if(!database)
207                 return;
208
209         for(i = 0; i < items; i++)
210                 if(database[i]) {
211                         database[i] = xrealloc(database[i], ITEM_SIZE);
212                         database[i][fields_count - 1] = NULL;
213                 }
214 }
215
216 /*
217  * Declare "standard" fields, thus preserving them while parsing a database,
218  * even if they won't be displayed.
219  */
220 void
221 init_standard_fields()
222 {
223         int i;
224
225         for(i = 0; standard_fields[i].key; i++)
226                 if(standard_fields_indexed[i] == -1)
227                         declare_standard_field(i);
228 }
229
230 /* Some initializations - Must be called _before_ load_opts() */
231 void
232 prepare_database_internals()
233 {
234         int i;
235
236         for(i = 0; i < ITEM_FIELDS; i++)
237                 standard_fields_indexed[i] = -1;
238
239         /* the only two mandatory fields */
240         declare_standard_field(NAME);
241         declare_standard_field(EMAIL);
242 }
243
244 int
245 parse_database(FILE *in)
246 {
247         char *line = NULL;
248         char *tmp;
249         int sec=0, field;
250         list_item item;
251
252         item = item_create();
253
254         for(;;) {
255                 line = getaline(in);
256                 if(feof(in)) {
257                         if(item[field_id(NAME)] && sec) {
258                                 add_item2database(item);
259                         } else {
260                                 item_empty(item);
261                         }
262                         break;
263                 }
264
265                 if(!*line || *line == '\n' || *line == '#') {
266                         goto next;
267                 } else if(*line == '[') {
268                         if(item[field_id(NAME)] && sec ) {
269                                 add_item2database(item);
270                         } else {
271                                 item_empty(item);
272                         }
273                         sec = 1;
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) {
278                         *tmp++ = '\0';
279                         find_field_number(line, &field);
280                         if(field != -1) {
281                                 item[field] = xstrdup(tmp);
282                                 goto next;
283                         } else if(!strcasecmp(opt_get_str(STR_PRESERVE_FIELDS),
284                                                 "all")){
285                                 declare_unknown_field(line);
286                                 item = xrealloc(item, ITEM_SIZE);
287                                 item[fields_count - 1] = xstrdup(tmp);
288                                 goto next;
289                         }
290                 }
291 next:
292                 xfree(line);
293         }
294
295         xfree(line);
296         item_free(&item);
297         return 0;
298 }
299
300 int
301 load_database(char *filename)
302 {
303         FILE *in;
304
305         if(database != NULL)
306                 close_database();
307
308         if ((in = abook_fopen(filename, "r")) == NULL)
309                 return -1;
310
311         parse_database(in);
312
313         return (items == 0) ? 2 : 0;
314 }
315
316 int
317 write_database(FILE *out, struct db_enumerator e)
318 {
319         int j;
320         int i = 0;
321         abook_field_list *cur;
322
323         fprintf(out,
324                 "# abook addressbook file\n\n"
325                 "[format]\n"
326                 "program=" PACKAGE "\n"
327                 "version=" VERSION "\n"
328                 "\n\n"
329         );
330
331         db_enumerate_items(e) {
332                 fprintf(out, "[%d]\n", i);
333
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",
338                                         cur->field->key,
339                                         database[e.item][j]
340                                         );
341                 }
342
343                 fputc('\n', out);
344                 i++;
345         }
346
347         return 0;
348 }
349
350 int
351 save_database()
352 {
353         FILE *out;
354         int ret = 0;
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);
358
359         if( (out = abook_fopen(datafile_new, "w")) == NULL ) {
360                 ret = -1;
361                 goto out;
362         }
363
364         if(!list_is_empty())
365                 /*
366                  * Possibly should check if write_database failed.
367                  * Currently it returns always zero.
368                  */
369                 write_database(out, e);
370
371         fclose(out);
372
373         if(access(datafile, F_OK) == 0 &&
374                         (rename(datafile, datafile_old)) == -1)
375                 ret = -1;
376         
377         if((rename(datafile_new, datafile)) == -1)
378                 ret = -1;
379
380 out:
381         free(datafile_new);
382         free(datafile_old);
383         return ret;
384 }
385
386 static void
387 db_free_item(int item)
388 {
389         item_empty(database[item]);
390 }
391
392 void
393 close_database()
394 {
395         int i;
396
397         for(i=0; i <= LAST_ITEM; i++)
398                 db_free_item(i);
399
400         xfree(database);
401         free(selected);
402
403         database = NULL;
404         selected = NULL;
405
406         items = 0;
407         first_list_item = curitem = -1;
408         list_capacity = 0;
409 }
410
411
412 static void
413 validate_item(list_item item)
414 {
415         abook_field_list *f;
416         int i, max_field_len;
417         char *tmp;
418
419         for(f = fields_list, i = 0; f; f = f->next, i++) {
420                 max_field_len = 0;
421
422                 switch(f->field->type) {
423                         case FIELD_EMAILS:
424                                 max_field_len = MAX_EMAILSTR_LEN;
425                                 if(item[i] == NULL)
426                                         item[i] = xstrdup("");
427                                 break;
428                         case FIELD_LIST:
429                                 /* TODO quote string if it contains commas */
430                                 break;
431                         case FIELD_STRING:
432                                 max_field_len = MAX_FIELD_LEN;
433                                 break;
434                         case FIELD_DATE:
435                                 break;
436                         default:
437                                 assert(0);
438                 }
439
440                 if(max_field_len && item[i] &&
441                                 ((int)strlen(item[i]) > max_field_len)) {
442                         /* truncate field */
443                         tmp = item[i];
444                         item[i][max_field_len - 1] = 0;
445                         item[i] = xstrdup(item[i]);
446                         free(tmp);
447                 }
448         }
449 }
450
451 static void
452 adjust_list_capacity()
453 {
454         if(list_capacity < 1)
455                 list_capacity = INITIAL_LIST_CAPACITY;
456         else if(items >= list_capacity)
457                 list_capacity *= 2;
458         else if(list_capacity / 2 > items)
459                 list_capacity /= 2;
460         else
461                 return;
462
463         if(database)
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);
467
468         selected = xrealloc(selected, list_capacity);
469 }
470
471 int
472 add_item2database(list_item item)
473 {
474         /* 'name' field is mandatory */
475         if((item[field_id(NAME)] == NULL) || ! *item[field_id(NAME)]) {
476                 item_empty(item);
477                 return 1;
478         }
479
480         if(++items > list_capacity)
481                 adjust_list_capacity();
482
483         validate_item(item);
484
485         selected[LAST_ITEM] = 0;
486
487         database[LAST_ITEM] = item_create();
488         item_copy(database[LAST_ITEM], item);
489
490         return 0;
491 }
492         
493
494 void
495 remove_selected_items()
496 {
497         int i, j;
498
499         if(list_is_empty())
500                 return;
501
502         if(!selected_items())
503                 selected[curitem] = 1;
504
505         for(j = LAST_ITEM; j >= 0; j--) {
506                 if(selected[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];
511                         }
512                         item_free(&database[LAST_ITEM]);
513                         items--;
514                 }
515         }
516
517         if(curitem > LAST_ITEM && items > 0)
518                 curitem = LAST_ITEM;
519
520         adjust_list_capacity();
521
522         select_none();
523 }
524
525 void merge_selected_items()
526 {
527         int i, j;
528         int destitem = -1;
529
530         if((list_is_empty()) || (selected_items() < 2))
531                 return;
532
533         /* Find the top item */
534         for(j=0; destitem < 0; j++)
535                 if(selected[j])
536                         destitem = j;
537
538         /* Merge pairwise */
539         for(j = LAST_ITEM; j > destitem; j--) {
540                 if(selected[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];
546                         }
547                         item_free(&database[LAST_ITEM]);
548                         items--;
549                 }
550         }
551
552         if(curitem > LAST_ITEM && items > 0)
553                 curitem = LAST_ITEM;
554
555         adjust_list_capacity();
556
557         select_none();
558 }
559
560 void remove_duplicates()
561 {
562         int i,j,k;
563         char *tmpj;
564         if(list_is_empty())
565                 return;
566
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]);
577                                 }
578                                 item_free(&database[LAST_ITEM]);
579                                 items--;
580                         }
581         }
582
583         adjust_list_capacity();
584 }
585
586
587 char *
588 get_surname(char *s)
589 {
590         char *p = s + strlen(s);
591
592         assert(s != NULL);
593
594         while(p > s && *(p - 1) != ' ')
595                 p--;
596
597         return xstrdup(p);
598 }
599
600 static int
601 surnamecmp(const void *i1, const void *i2)
602 {
603         int ret, idx = field_id(NAME);
604         char *n1, *n2, *s1, *s2;
605
606         n1 = (*(list_item *)i1)[idx];
607         n2 = (*(list_item *)i2)[idx];
608         
609         s1 = get_surname(n1);
610         s2 = get_surname(n2);
611
612         if( !(ret = safe_strcoll(s1, s2)) )
613                 ret = safe_strcoll(n1, n2);
614
615         free(s1);
616         free(s2);
617
618         return ret;
619 }
620
621 static int sort_field = -1;
622
623 static int
624 namecmp(const void *i1, const void *i2)
625 {
626         char *n1, *n2;
627
628         assert(sort_field >= 0 && sort_field < fields_count);
629
630         n1 = (*(list_item *)i1)[sort_field];
631         n2 = (*(list_item *)i2)[sort_field];
632
633         return safe_strcoll(n1, n2);
634 }
635
636 void
637 sort_by_field(char *name)
638 {
639         int field;
640
641         select_none();
642
643         name = (name == NULL) ? opt_get_str(STR_SORT_FIELD) : name;
644         find_field_number(name, &field);
645
646         if(field < 0) {
647                 if(name == opt_get_str(STR_SORT_FIELD))
648                         statusline_msg(_("Invalid field value defined "
649                                 "in configuration"));
650                 else
651                         statusline_msg(_("Invalid field value for sorting"));
652
653                 return;
654         }
655
656         sort_field = field;
657
658         qsort((void *)database, items, sizeof(list_item), namecmp);
659
660         refresh_screen();
661 }
662
663 void
664 sort_surname()
665 {
666         select_none();
667
668         qsort((void *)database, items, sizeof(list_item), surnamecmp);
669
670         refresh_screen();
671 }
672
673 /* TODO implement a search based on more sophisticated patterns */
674 int
675 find_item(char *str, int start, int search_fields[])
676 {
677         int i, id;
678         char *findstr = NULL;
679         char *tmp = NULL;
680         int ret = -1; /* not found */
681         struct db_enumerator e = init_db_enumerator(ENUM_ALL);
682
683         if(list_is_empty() || !is_valid_item(start))
684                 return -2; /* error */
685
686         findstr = xstrdup(str);
687         findstr = strlower(findstr);
688
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)
693                                 continue;
694                         if(database[e.item][id] == NULL)
695                                 continue;
696                         tmp = xstrdup(database[e.item][id]);
697                         if( tmp && strstr(strlower(tmp), findstr) ) {
698                                 ret = e.item;
699                                 goto out;
700                         }
701                         xfree(tmp);
702                 }
703         }
704
705 out:
706         free(findstr);
707         free(tmp);
708         return ret;
709 }
710
711 int
712 is_selected(int item)
713 {
714         return selected[item];
715 }
716
717 int
718 is_valid_item(int item)
719 {
720         return item <= LAST_ITEM && item >= 0;
721 }
722
723 int
724 last_item()
725 {
726         return LAST_ITEM;
727 }
728
729 int
730 db_n_items()
731 {
732         return items;
733 }
734
735 int
736 real_db_enumerate_items(struct db_enumerator e)
737 {
738         int item = max(0, e.item + 1);
739         int i;
740
741         switch(e.mode) {
742 #ifdef DEBUG
743                 case ENUM_ALL:
744                         break;
745 #endif
746                 case ENUM_SELECTED:
747                         for(i = item; i <= LAST_ITEM; i++) {
748                                 if(is_selected(i)) {
749                                         item = i;
750                                         goto out;
751                                 }
752                         }
753                         return -1;
754 #ifdef DEBUG
755                 default:
756                         fprintf(stderr, "real_db_enumerate_items() "
757                                         "BUG: unknown db_enumerator mode: %d\n",
758                                         e.mode);
759                         break;
760 #endif
761         }
762 out:
763         return (item > LAST_ITEM || item < 0) ? -1 : item;
764 }
765
766 struct db_enumerator
767 init_db_enumerator(int mode)
768 {
769         struct db_enumerator e;
770
771         e.item = -1; /* important - means "start from beginning" */
772         e.mode = mode;
773
774         return e;
775 }
776
777
778 list_item
779 item_create()
780 {
781         return xmalloc0(ITEM_SIZE);
782 }
783
784 void
785 item_free(list_item *item)
786 {
787         assert(item);
788
789         xfree(*item);
790 }
791
792 void
793 item_empty(list_item item)
794 {       int i;
795
796         assert(item);
797
798         for(i = 0; i < fields_count; i++)
799                 if(item[i])
800                         xfree(item[i]);
801
802 }
803
804 void
805 item_copy(list_item dest, list_item src)
806 {
807         memmove(dest, src, ITEM_SIZE);
808 }
809
810 void
811 item_duplicate(list_item dest, list_item src)
812 {
813         int i;
814
815         for(i = 0; i < fields_count; i++)
816                 dest[i] = src[i] ? xstrdup(src[i]) : NULL;
817 }
818  
819 /*
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
823  * */
824 void
825 item_merge(list_item dest, list_item src)
826 {
827         int i, found = 0;
828         abook_list *dfield, *sfield, *ed, *es;
829
830         for(i = 0; i < fields_count; i++)
831                 if (src[i]) {
832                         if (!dest[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));
840                                         if (!found)
841                                                 abook_list_append(&dfield, es->data);
842                                 }
843                                 xfree(dest[i]);
844                                 dest[i] = abook_list_to_csv(dfield);
845                                 abook_list_free(&dfield);
846                                 abook_list_free(&sfield);
847                         }
848                 }
849
850         item_empty(src);
851 }
852
853 /* 
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.
856  *
857  * Prefer the functions item_fput(), item_fget(), db_fput() and db_fget()
858  * to access fields in items and database.
859  */
860
861 /* quick lookup by "standard" field number */
862 inline int
863 field_id(int i)
864 {
865         assert((i >= 0) && (i < ITEM_FIELDS));
866         return standard_fields_indexed[i];
867 }
868
869 int
870 item_fput(list_item item, int i, char *val)
871 {
872         int id = field_id(i);
873
874         if(id != -1) {
875                 item[id] = val;
876                 return 1;
877         }
878
879         return 0;
880 }
881
882 char *
883 item_fget(list_item item, int i)
884 {
885         int id = field_id(i);
886
887         if(id != -1)
888                 return item[id];
889         else
890                 return NULL;
891 }
892
893 int
894 real_db_field_put(int item, int i, int std, char *val)
895 {
896         int id;
897
898         assert(database[item]);
899
900         id = std ? field_id(i) : i;
901
902         if(id != -1) {
903                 database[item][id] = val;
904                 return 1;
905         }
906
907         return 0;
908 }
909
910 char *
911 real_db_field_get(int item, int i, int std)
912 {
913         int id;
914
915         assert(database[item]);
916
917         id = std ? field_id(i) : i;
918
919         if(id != -1)
920                 return database[item][id];
921         else
922                 return NULL;
923 }
924
925 list_item
926 db_item_get(int i)
927 {
928         return database[i];
929 }
930
931 /* Fetch addresses from all fields of FIELD_EMAILS type */
932 /* Memory has to be freed by the caller */
933 char *
934 db_email_get(int item)
935 {
936         int i;
937         char *res;
938         abook_field_list *cur;
939         abook_list *emails = NULL;
940
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]);
944
945         res = abook_list_to_csv(emails);
946         abook_list_free(&emails);
947         return res ? res : xstrdup("");
948 }
949