]> git.deb.at Git - pkg/abook.git/blob - filter.c
ldif: don't dump "email:" if the email field is empty.
[pkg/abook.git] / filter.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@users.sourceforge.net>
6  *
7  * Copyright (C) Jaakko Heinonen
8  */
9
10 #define _GNU_SOURCE
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <pwd.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include "abook_curses.h"
20 #include "filter.h"
21 #include "abook.h"
22 #include "database.h"
23 #include "edit.h"
24 #include "gettext.h"
25 #include "list.h"
26 #include "misc.h"
27 #include "options.h"
28 #include "ui.h"
29 #include "xmalloc.h"
30 #include <assert.h>
31
32 #ifdef HAVE_VFORMAT
33 #include "vcard.h"
34 #endif
35
36 extern abook_field_list *fields_list;
37 extern int fields_count;
38
39 /*
40  * function declarations
41  */
42
43 /*
44  * import filter prototypes
45  */
46
47 static int      ldif_parse_file(FILE *handle);
48 static int      mutt_parse_file(FILE *in);
49 static int      pine_parse_file(FILE *in);
50 static int      csv_parse_file(FILE *in);
51 static int      allcsv_parse_file(FILE *in);
52 static int      palmcsv_parse_file(FILE *in);
53 static int      vcard_parse_file(FILE *in);
54
55 /*
56  * export filter prototypes
57  */
58
59 static int      ldif_export_database(FILE *out, struct db_enumerator e);
60 static int      html_export_database(FILE *out, struct db_enumerator e);
61 static int      pine_export_database(FILE *out, struct db_enumerator e);
62 static int      csv_export_database(FILE *out, struct db_enumerator e);
63 static int      allcsv_export_database(FILE *out, struct db_enumerator e);
64 static int      palm_export_database(FILE *out, struct db_enumerator e);
65 static int      vcard_export_database(FILE *out, struct db_enumerator e);
66 static int      mutt_alias_export(FILE *out, struct db_enumerator e);
67 static int      mutt_query_export_database(FILE *out, struct db_enumerator e);
68 static int      elm_alias_export(FILE *out, struct db_enumerator e);
69 static int      text_export_database(FILE *out, struct db_enumerator e);
70 static int      spruce_export_database(FILE *out, struct db_enumerator e);
71 static int      wl_export_database(FILE *out, struct db_enumerator e);
72 static int      bsdcal_export_database(FILE *out, struct db_enumerator e);
73 static int      custom_export_database(FILE *out, struct db_enumerator e);
74
75 /*
76  * export filter item prototypes
77  */
78
79 void vcard_export_item(FILE *out, int item);
80 void muttq_print_item(FILE *file, int item);
81 void custom_print_item(FILE *out, int item);
82
83 /*
84  * end of function declarations
85  */
86
87 struct abook_input_filter i_filters[] = {
88         { "abook", N_("abook native format"), parse_database },
89         { "ldif", N_("ldif / Netscape addressbook"), ldif_parse_file },
90         { "mutt", N_("mutt alias"), mutt_parse_file },
91         { "pine", N_("pine addressbook"), pine_parse_file },
92         { "csv", N_("comma separated values"), csv_parse_file },
93         { "allcsv", N_("comma separated values (all fields)"), allcsv_parse_file },
94         { "palmcsv", N_("Palm comma separated values"), palmcsv_parse_file },
95         { "vcard", N_("vCard file"), vcard_parse_file },
96         { "\0", NULL, NULL }
97 };
98
99 struct abook_output_filter e_filters[] = {
100         { "abook", N_("abook native format"), write_database },
101         { "ldif", N_("ldif / Netscape addressbook (.4ld)"), ldif_export_database },
102         { "vcard", N_("vCard 2 file"), vcard_export_database },
103         { "mutt", N_("mutt alias"), mutt_alias_export },
104         { "muttq", N_("mutt query format (internal use)"), mutt_query_export_database },
105         { "html", N_("html document"), html_export_database },
106         { "pine", N_("pine addressbook"), pine_export_database },
107         { "csv", N_("comma separated values"), csv_export_database },
108         { "allcsv", N_("comma separated values (all fields)"), allcsv_export_database },
109         { "palmcsv", N_("Palm comma separated values"), palm_export_database},
110         { "elm", N_("elm alias"), elm_alias_export },
111         { "text", N_("plain text"), text_export_database },
112         { "wl", N_("Wanderlust address book"), wl_export_database },
113         { "spruce", N_("Spruce address book"), spruce_export_database },
114         { "bsdcal", N_("BSD calendar"), bsdcal_export_database },
115         { "custom", N_("Custom format"), custom_export_database },
116         { "\0", NULL, NULL }
117 };
118
119 struct abook_output_item_filter u_filters[] = {
120         { "vcard", N_("vCard 2 file"), vcard_export_item },
121         { "muttq", N_("mutt alias"), muttq_print_item },
122         { "custom", N_("Custom format"), custom_print_item },
123         { "\0", NULL }
124 };
125
126 /*
127  * common functions
128  */
129
130 void
131 print_filters()
132 {
133         int i;
134
135         puts(_("input formats:"));
136         for(i=0; *i_filters[i].filtname ; i++)
137                 printf("\t%s\t%s\n", i_filters[i].filtname,
138                         gettext(i_filters[i].desc));
139
140         putchar('\n');
141
142         puts(_("output formats:"));
143         for(i=0; *e_filters[i].filtname ; i++)
144                 printf("\t%s\t%s\n", e_filters[i].filtname,
145                         gettext(e_filters[i].desc));
146
147         putchar('\n');
148
149         puts(_("query-compatible output formats:"));
150         for(i=0; *u_filters[i].filtname ; i++)
151                 printf("\t%s\t%s\n", u_filters[i].filtname,
152                         gettext(u_filters[i].desc));
153
154         putchar('\n');
155 }
156
157 static int
158 number_of_output_filters()
159 {
160         int i;
161
162         for(i=0; *e_filters[i].filtname ; i++)
163                 ;
164
165         return i;
166 }
167
168 static int
169 number_of_input_filters()
170 {
171         int i;
172
173         for(i=0; *i_filters[i].filtname ; i++)
174                 ;
175
176         return i;
177 }
178
179 static char *
180 get_real_name()
181 {
182         char *username = getenv("USER");
183         struct passwd *pwent;
184         int rtn;
185         char *tmp;
186
187         pwent = getpwnam(username);
188
189         if((tmp = xstrdup(pwent->pw_gecos)) == NULL)
190                 return xstrdup(username);
191
192         rtn = sscanf(pwent->pw_gecos, "%[^,]", tmp);
193         if (rtn == EOF || rtn == 0) {
194                 free(tmp);
195                 return xstrdup(username);
196         } else
197                 return tmp;
198 }
199
200 /*
201  * import
202  */
203
204 static int              i_read_file(char *filename, int (*func) (FILE *in));
205
206 static void
207 import_screen()
208 {
209         int i;
210
211         clear();
212
213         refresh_statusline();
214         headerline(_("import database"));
215
216         mvaddstr(3, 1, _("please select a filter"));
217
218
219         for(i=0; *i_filters[i].filtname ; i++)
220                 mvprintw(5 + i, 6, "%c -\t%s\t%s\n", 'a' + i,
221                         i_filters[i].filtname,
222                         gettext(i_filters[i].desc));
223
224         mvprintw(6 + i, 6, _("x -\tcancel"));
225 }
226
227 int
228 import_database()
229 {
230         int filter;
231         char *filename;
232         int tmp = db_n_items();
233
234         import_screen();
235
236         filter = getch() - 'a';
237         if(filter == 'x' - 'a' ||
238                 filter >= number_of_input_filters() || filter < 0) {
239                 refresh_screen();
240                 return 1;
241         }
242
243         mvaddstr(5+filter, 2, "->");
244
245         filename = ask_filename(_("Filename: "));
246         if(!filename) {
247                 refresh_screen();
248                 return 2;
249         }
250
251         if(i_read_file(filename, i_filters[filter].func ))
252                 statusline_msg(_("Error occured while opening the file"));
253         else if(tmp == db_n_items())
254                 statusline_msg(_("File does not seem to be a valid addressbook"));
255
256         refresh_screen();
257         free(filename);
258
259         return 0;
260 }
261
262
263
264 static int
265 i_read_file(char *filename, int (*func) (FILE *in))
266 {
267         FILE *in;
268         int ret = 0;
269
270         if( (in = abook_fopen( filename, "r" )) == NULL )
271                 return 1;
272
273         ret = (*func) (in);
274
275         fclose(in);
276
277         return ret;
278 }
279
280 int
281 import_file(char filtname[FILTNAME_LEN], char *filename)
282 {
283         int i;
284         int tmp = db_n_items();
285         int ret = 0;
286
287         for(i=0;; i++) {
288                 if(! strncasecmp(i_filters[i].filtname, filtname,
289                                         FILTNAME_LEN) )
290                         break;
291                 if(! *i_filters[i].filtname) {
292                         i = -1;
293                         break;
294                 }
295         }
296
297         if(i < 0)
298                 return -1;
299
300 #ifdef HAVE_VFORMAT
301         // this is a special case for
302         // libvformat whose API expects a filename
303         if(!strcmp(filtname, "vcard")) {
304           if(!strcmp(filename, "-"))
305             ret = vcard_parse_file_libvformat("/dev/stdin");
306           else
307             ret = vcard_parse_file_libvformat(filename);
308         }
309         else
310 #endif
311
312         if(!strcmp(filename, "-")) {
313                 struct stat s;
314                 if((fstat(fileno(stdin), &s)) == -1 || S_ISDIR(s.st_mode))
315                         ret = 1;
316                 else
317                         ret = (*i_filters[i].func) (stdin);
318         } else
319                 ret =  i_read_file(filename, i_filters[i].func);
320
321         if(tmp == db_n_items())
322                 ret = 1;
323
324         return ret;
325 }
326
327 /*
328  * export
329  */
330
331 static int e_write_file(char *filename,
332                 int (*func) (FILE *in, struct db_enumerator e), int mode);
333
334 static void
335 export_screen()
336 {
337         int i;
338
339         clear();
340
341
342         refresh_statusline();
343         headerline(_("export database"));
344
345         mvaddstr(3, 1, _("please select a filter"));
346
347
348         for(i = 0; *e_filters[i].filtname ; i++)
349                 mvprintw(5 + i, 6, "%c -\t%s\t%s\n", 'a' + i,
350                         e_filters[i].filtname,
351                         gettext(e_filters[i].desc));
352
353         mvprintw(6 + i, 6, _("x -\tcancel"));
354 }
355
356 int
357 export_database()
358 {
359         int filter;
360         int enum_mode = ENUM_ALL;
361         char *filename;
362
363         export_screen();
364
365         filter = getch() - 'a';
366         if(filter == 'x' - 'a' ||
367                 filter >= number_of_output_filters() || filter < 0) {
368                 refresh_screen();
369                 return 1;
370         }
371
372         mvaddstr(5 + filter, 2, "->");
373
374         if(selected_items()) {
375                 switch(statusline_askchoice(
376                         _("Export <a>ll, export <s>elected, or <c>ancel?"),
377                         S_("keybindings:all/selected/cancel|asc"), 3)) {
378                         case 1:
379                                 break;
380                         case 2:
381                                 enum_mode = ENUM_SELECTED;
382                                 break;
383                         case 0:
384                         case 3:
385                                 refresh_screen();
386                                 return 1;
387                 }
388                 clear_statusline();
389         }
390
391         filename = ask_filename(_("Filename: "));
392         if(!filename) {
393                 refresh_screen();
394                 return 2;
395         }
396
397         if( e_write_file(filename, e_filters[filter].func, enum_mode))
398                 statusline_msg(_("Error occured while exporting"));
399
400         refresh_screen();
401         free(filename);
402
403         return 0;
404 }
405
406 struct abook_output_item_filter select_output_item_filter(char filtname[FILTNAME_LEN]) {
407         int i;
408         for(i=0;; i++) {
409                 if(!strncasecmp(u_filters[i].filtname, filtname, FILTNAME_LEN))
410                   break;
411                 if(!*u_filters[i].filtname) {
412                   i = -1;
413                   break;
414                 }
415         }
416         return u_filters[i];
417 }
418
419 void
420 e_write_item(FILE *out, int item, void (*func) (FILE *in, int item))
421 {
422   (*func) (out, item);
423 }
424
425 static int
426 e_write_file(char *filename, int (*func) (FILE *in, struct db_enumerator e),
427                 int mode)
428 {
429         FILE *out;
430         int ret = 0;
431         struct db_enumerator enumerator = init_db_enumerator(mode);
432
433         if((out = fopen(filename, "a")) == NULL)
434                 return 1;
435
436         if(ftell(out))
437                 return 1;
438
439         ret = (*func) (out, enumerator);
440
441         fclose(out);
442
443         return ret;
444 }
445
446 int
447 fexport(char filtname[FILTNAME_LEN], FILE *handle, int enum_mode)
448 {
449         int i;
450         struct db_enumerator e = init_db_enumerator(enum_mode);
451
452         for(i=0;; i++) {
453                 if(!strncasecmp(e_filters[i].filtname, filtname,
454                                         FILTNAME_LEN))
455                         break;
456                 if(!*e_filters[i].filtname) {
457                         i = -1;
458                         break;
459                 }
460         }
461
462         return (e_filters[i].func) (handle, e);
463 }
464
465
466
467 int
468 export_file(char filtname[FILTNAME_LEN], char *filename)
469 {
470         const int mode = ENUM_ALL;
471         int i;
472         int ret = 0;
473         struct db_enumerator e = init_db_enumerator(mode);
474
475         for(i=0;; i++) {
476                 if(!strncasecmp(e_filters[i].filtname, filtname,
477                                         FILTNAME_LEN))
478                         break;
479                 if(!*e_filters[i].filtname) {
480                         i = -1;
481                         break;
482                 }
483         }
484
485         if(i < 0)
486                 return -1;
487
488         if(!strcmp(filename, "-"))
489                 ret = (e_filters[i].func) (stdout, e);
490         else
491                 ret =  e_write_file(filename, e_filters[i].func, mode);
492
493         return ret;
494 }
495
496 /*
497  * end of common functions
498  */
499
500 /*
501  * ldif import
502  */
503
504 #include "ldif.h"
505
506 static void     ldif_fix_string(char *str);
507
508 #define LDIF_ITEM_FIELDS        16
509
510 typedef char *ldif_item[LDIF_ITEM_FIELDS];
511
512 static ldif_item ldif_field_names = {
513         "cn",
514         "mail",
515         "streetaddress",
516         "streetaddress2",
517         "locality",
518         "st",
519         "postalcode",
520         "countryname",
521         "homephone",
522         "description",
523         "homeurl",
524         "facsimiletelephonenumber",
525         "cellphone",
526         "xmozillaanyphone",
527         "xmozillanickname",
528         "objectclass", /* this must be the last entry */
529 };
530
531 static int ldif_conv_table[LDIF_ITEM_FIELDS] = {
532         NAME,           /* "cn" */
533         EMAIL,          /* "mail" */
534         ADDRESS,        /* "streetaddress" */
535         ADDRESS2,       /* "streetaddress2" */
536         CITY,           /* "locality" */
537         STATE,          /* "st" */
538         ZIP,            /* "postalcode" */
539         COUNTRY,        /* "countryname" */
540         PHONE,          /* "homephone" */
541         NOTES,          /* "description" */
542         URL,            /* "homeurl" */
543         FAX,            /* "facsimiletelephonenumber" */
544         MOBILEPHONE,    /* "cellphone" */
545         WORKPHONE,      /* "xmozillaanyphone" */
546         NICK,           /* "xmozillanickname" */
547         -1,             /* "objectclass" */ /* this must be the last entry */
548 };
549
550 /*
551   Handles multi-line strings.
552   If a string starts with a space, it's the continuation
553   of the previous line. Thus we need to always read ahead.
554   But for this to work with stdin, we need to stores the next
555   line for later use in case it's not a continuation of the
556   first line.
557  */
558 static char *
559 ldif_read_line(FILE *in, char **next_line)
560 {
561         char *buf = NULL;
562         char *ptr, *tmp;
563         char *line;
564
565         // buf filled with the first line
566         if(!*next_line)
567                 buf = getaline(in);
568         else {
569                 buf = xstrdup(*next_line);
570                 xfree(*next_line);
571         }
572
573         while(!feof(in)) {
574                 // if no line already read-ahead.
575                 line = getaline(in);
576                 if(!line) break;
577
578                 // this is not a continuation of what is already in buf
579                 // store it for the next round
580                 if(*line != ' ') {
581                         *next_line = line;
582                         break;
583                 }
584
585                 // starts with ' ': this is the continuation of buf
586                 ptr = line;
587                 while( *ptr == ' ')
588                         ptr++;
589
590                 tmp = buf;
591                 buf = strconcat(buf, ptr, NULL);
592                 free(tmp);
593                 free(line);
594         }
595
596         if(buf && *buf == '#' ) {
597                 free(buf);
598                 return NULL;
599         }
600
601         return buf;
602 }
603
604 static void
605 ldif_add_item(ldif_item li)
606 {
607         list_item item;
608         int i;
609
610         item = item_create();
611
612         if(!li[LDIF_ITEM_FIELDS -1])
613                 goto bail_out;
614
615
616         for(i=0; i < LDIF_ITEM_FIELDS; i++) {
617                 if(ldif_conv_table[i] >= 0 && li[i] && *li[i])
618                         item_fput(item,ldif_conv_table[i],xstrdup(li[i]));
619         }
620
621         add_item2database(item);
622
623 bail_out:
624         for(i=0; i < LDIF_ITEM_FIELDS; i++)
625                 xfree(li[i]);
626         item_free(&item);
627
628 }
629
630 static void
631 ldif_convert(ldif_item item, char *type, char *value)
632 {
633         int i;
634
635         if(!strcmp(type, "dn")) {
636                 ldif_add_item(item);
637                 return;
638         }
639
640         for(i=0; i < LDIF_ITEM_FIELDS - 1; i++) {
641                 if(!strcasecmp(ldif_field_names[i], type) && *value) {
642                         item_fput(item, i, xstrdup(value));
643                         break;
644                 }
645         }
646 }
647
648 static int
649 ldif_parse_file(FILE *handle)
650 {
651         char *line = NULL;
652         char *next_line = NULL;
653         char *type, *value;
654         int vlen;
655         ldif_item item;
656
657         memset(item, 0, sizeof(item));
658
659         do {
660                 line = ldif_read_line(handle, &next_line);
661
662                 // EOF or empty lines: continue;
663                 if(!line || *line == '\0') continue;
664
665                 if(-1 == (str_parse_line(line, &type, &value, &vlen))) {
666                         xfree(line);
667                         continue; /* just skip the errors */
668                 }
669
670                 ldif_fix_string(value);
671
672                 ldif_convert(item, type, value);
673
674                 xfree(line);
675         } while ( !feof(handle) );
676
677         ldif_convert(item, "dn", "");
678
679         return 0;
680 }
681
682 static void
683 ldif_fix_string(char *str)
684 {
685         int i, j;
686
687         for(i = 0, j = 0; j < (int)strlen(str); i++, j++)
688                 str[i] = ( str[j] == (char)0xc3 ?
689                                 (char) str[++j] + (char) 0x40 :
690                                 str[j] );
691
692         str[i] = 0;
693 }
694
695 /*
696  * end of ldif import
697  */
698
699 /*
700  * mutt alias import filter
701  */
702
703 #include "getname.h"
704
705 static int
706 mutt_read_line(FILE *in, char **groups, char **alias, char **rest)
707 {
708         char *line, *ptr;
709         char *start, *end;
710         abook_list *glist = NULL;
711
712         if( !(line = ptr = getaline(in)) )
713                 return 1; /* error / EOF */
714
715         SKIPWS(ptr);
716
717         if(strncmp("alias", ptr, 5)) {
718                 free(line);
719                 return 1;
720         }
721
722         ptr += 5;
723         SKIPWS(ptr);
724
725         /* If the group option is used, save the groups */
726         *groups = NULL;
727         start = ptr;
728         int n_groups;
729         for(n_groups = 0; 0 == strncmp("-group", ptr, 6); n_groups++) {
730                 ptr += 6;
731                 SKIPWS(ptr);
732                 start = ptr;
733                 SKIPNONWS(ptr);
734                 end = ptr;
735                 abook_list_append(&glist,xstrndup(start, end - start));
736                 SKIPWS(ptr);
737         }
738
739         if(n_groups && groups)
740                 *groups = abook_list_to_csv(glist);
741
742         abook_list_free(&glist);        
743
744         /* alias */
745         start = ptr;
746         SKIPNONWS(ptr);
747         end = ptr;
748         SKIPWS(ptr);
749         if(alias)
750                 *alias = xstrndup(start, end - start);
751
752         /* rest (email) */
753         *rest = xstrdup(ptr);
754
755         xfree(line);
756         return 0;
757 }
758
759 static void
760 mutt_fix_quoting(char *p)
761 {
762         char *escape = 0;
763
764         for(; *p; p++) {
765                 switch(*p) {
766                         case '\"':
767                                 if(escape)
768                                         *escape = ' ';
769                                 break;
770                         case '\\':
771                                 escape = p;
772                                 break;
773                         default:
774                                 escape = 0;
775                 }
776         }
777 }
778
779 static void
780 mutt_parse_email(list_item item)
781 {
782         char *line = item_fget(item, NAME);
783         char *tmp;
784         char *name, *email;
785 #if 0
786         char *start = line;
787         int i = 0;
788 #endif
789
790         mutt_fix_quoting(line);
791         tmp = strconcat("From: ", line, NULL);
792         getname(tmp, &name, &email);
793         free(tmp);
794
795         if(name)
796                 item_fput(item, NAME, name);
797         else
798                 return;
799
800         if(email)
801                 item_fput(item, EMAIL, email);
802         else
803                 return;
804
805         /*
806          * this is completely broken
807          */
808 #if 0
809         while( (start = strchr(start, ',')) && i++ < MAX_EMAILS - 1) {
810                 tmp = strconcat("From: ", ++start, NULL);
811                 getname(tmp, &name, &email);
812                 free(tmp);
813                 free(name);
814                 if(email) {
815                         if(*email) {
816                                 tmp = strconcat(item[EMAIL], ",", email, NULL);
817                                 free(item[EMAIL]);
818                                 item[EMAIL] = tmp;
819                         } else {
820                                 xfree(email);
821                         }
822                 }
823         }
824 #endif
825 }
826
827 static int
828 mutt_parse_file(FILE *in)
829 {
830         list_item item = item_create();
831
832         for(;;) {
833                 memset(item, 0, fields_count * sizeof(char *));
834
835                 if(!mutt_read_line(in,
836                         (field_id(GROUPS) != -1) ? &item[field_id(GROUPS)] : NULL,
837                         (field_id(NICK) != -1) ? &item[field_id(NICK)] : NULL,
838                         &item[field_id(NAME)]) )
839                         mutt_parse_email(item);
840
841                 if(feof(in)) {
842                         item_empty(item);
843                         break;
844                 }
845
846                 add_item2database(item);
847         }
848         item_free(&item);
849
850         return 0;
851 }
852
853 /*
854  * end of mutt alias import filter
855  */
856
857
858 /*
859  * ldif export filter
860  */
861
862 static void
863 ldif_fput_type_and_value(FILE *out,char *type, char *value )
864 {
865         char *tmp;
866
867         tmp = ldif_type_and_value(type, value, strlen(value));
868
869         fputs(tmp, out);
870
871         free(tmp);
872 }
873
874 static int
875 ldif_export_database(FILE *out, struct db_enumerator e)
876 {
877         char email[MAX_EMAILSTR_LEN];
878
879         fprintf(out, "version: 1\n");
880
881         db_enumerate_items(e) {
882                 char *tmp;
883                 int j;
884                 get_first_email(email, e.item);
885
886                 if(*email)
887                         tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email);
888                 else
889                         tmp = strdup_printf("cn=%s",db_name_get(e.item));
890
891                 ldif_fput_type_and_value(out, "dn", tmp);
892                 free(tmp);
893
894                 for(j = 0; j < LDIF_ITEM_FIELDS; j++) {
895                         if(ldif_conv_table[j] >= 0) {
896                                 if(ldif_conv_table[j] == EMAIL) {
897                                         if(*email) // don't dump en empty email field
898                                                 ldif_fput_type_and_value(out,
899                                                                          ldif_field_names[j],
900                                                                          email);
901                                 }
902                                 else if(db_fget(e.item,ldif_conv_table[j]))
903                                         ldif_fput_type_and_value(out,
904                                                 ldif_field_names[j],
905                                                 db_fget(e.item,
906                                                         ldif_conv_table[j]));
907                         }
908                 }
909
910                 fprintf(out, "objectclass: top\n"
911                                 "objectclass: person\n\n");
912         }
913
914         return 0;
915 }
916
917 /*
918  * end of ldif export filter
919  */
920
921 /*
922  * html export filter
923  */
924
925 static void            html_export_write_head(FILE *out);
926 static void            html_export_write_tail(FILE *out);
927
928 extern struct index_elem *index_elements;
929
930 static void
931 html_print_emails(FILE *out, struct list_field *f)
932 {
933         abook_list *l = csv_to_abook_list(f->data);
934
935         for(; l; l = l->next) {
936                 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
937                 if(l->next)
938                         fprintf(out, ", ");
939         }
940
941         abook_list_free(&l);
942 }
943
944 static int
945 html_export_database(FILE *out, struct db_enumerator e)
946 {
947         struct list_field f;
948         struct index_elem *cur;
949
950         if(list_is_empty())
951                 return 2;
952
953         init_index();
954
955         html_export_write_head(out);
956
957         db_enumerate_items(e) {
958                 fprintf(out, "<tr>");
959                 for(cur = index_elements; cur; cur = cur->next) {
960                         if(cur->type != INDEX_FIELD)
961                                 continue;
962
963                         get_list_field(e.item, cur, &f);
964
965                         if(f.type == FIELD_EMAILS) {
966                                 fprintf(out, "<td>");
967                                 html_print_emails(out, &f);
968                                 fprintf(out, "</td>");
969                                 continue;
970                         } else {
971                                 fprintf(out, "<td>%s</td>", safe_str(f.data));
972                         }
973                 }
974                 fprintf(out, "</tr>\n");
975         }
976
977         html_export_write_tail(out);
978
979         return 0;
980 }
981
982 static void
983 html_export_write_head(FILE *out)
984 {
985         char *realname = get_real_name(), *str;
986         struct index_elem *cur;
987
988         fprintf(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
989         fprintf(out, "<html>\n<head>\n <title>%s's addressbook</title>",
990                         realname );
991         fprintf(out, "\n</head>\n<body>\n");
992         fprintf(out, "\n<h2>%s's addressbook</h2>\n", realname );
993         fprintf(out, "<br><br>\n\n");
994
995         fprintf(out, "<table border=\"1\" align=\"center\">\n<tr>");
996         for(cur = index_elements; cur; cur = cur->next) {
997                 if(cur->type != INDEX_FIELD)
998                         continue;
999
1000                 get_field_info(cur->d.field.id, NULL, &str, NULL);
1001                 fprintf(out, "<th>%s</th>", str);
1002         }
1003         fprintf(out, "</tr>\n\n");
1004
1005         free(realname);
1006 }
1007
1008 static void
1009 html_export_write_tail(FILE *out)
1010 {
1011         fprintf(out, "\n</table>\n");
1012         fprintf(out, "\n</body>\n</html>\n");
1013 }
1014
1015 /*
1016  * end of html export filter
1017  */
1018
1019
1020 /*
1021  * pine addressbook import filter
1022  */
1023
1024 #define PINE_BUF_SIZE 2048
1025
1026 static void
1027 pine_fixbuf(char *buf)
1028 {
1029         int i,j;
1030
1031         for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1032                 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1033 }
1034
1035 static void
1036 pine_convert_emails(char *s)
1037 {
1038         int i;
1039         char *tmp;
1040
1041         if(s == NULL || *s != '(')
1042                 return;
1043
1044         for(i = 0; s[i]; i++)
1045                 s[i] = s[i + 1];
1046
1047         if( ( tmp = strchr(s,')')) )
1048                 *tmp = '\0';
1049
1050         for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1051                 if(i > MAX_LIST_ITEMS - 1) {
1052                         *tmp = '\0';
1053                         break;
1054                 }
1055
1056 }
1057
1058 static void
1059 pine_parse_buf(char *buf)
1060 {
1061         list_item item;
1062         char *start = buf;
1063         char *end;
1064         char tmp[PINE_BUF_SIZE];
1065         int i, len, last;
1066         int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1067
1068         item = item_create();
1069
1070         for(i=0, last=0; !last ; i++) {
1071                 if( !(end = strchr(start, '\t')) )
1072                         last=1;
1073
1074                 len = last ? strlen(start) : (int) (end-start);
1075                 len = min(len, PINE_BUF_SIZE - 1);
1076
1077                 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1078                                 && pine_conv_table[i] >= 0) {
1079                         strncpy(tmp, start, len);
1080                         tmp[len] = 0;
1081                         if(*tmp)
1082                                 item_fput(item, pine_conv_table[i],
1083                                                 xstrdup(tmp));
1084                 }
1085                 start = end + 1;
1086         }
1087
1088         pine_convert_emails(item_fget(item, EMAIL));
1089         add_item2database(item);
1090         item_free(&item);
1091 }
1092
1093
1094 #define LINESIZE        1024
1095
1096 static int
1097 pine_parse_file(FILE *in)
1098 {
1099         char line[LINESIZE];
1100         char *buf = NULL;
1101         char *ptr;
1102         int i;
1103
1104         fgets(line, LINESIZE, in);
1105
1106         while(!feof(in)) {
1107                 for(i = 2;;i++) {
1108                         buf = xrealloc(buf, i*LINESIZE);
1109                         if(i == 2)
1110                                 strcpy(buf, line);
1111                         fgets(line, LINESIZE, in);
1112                         ptr=(char *)&line;
1113                         if(*ptr != ' ' || feof(in))
1114                                 break;
1115                         else
1116                                 while(*ptr == ' ')
1117                                         ptr++;
1118
1119                         strcat(buf, ptr);
1120                 }
1121                 if(*buf == '#') {
1122                         xfree(buf);
1123                         continue;
1124                 }
1125                 pine_fixbuf(buf);
1126
1127                 pine_parse_buf(buf);
1128
1129                 xfree(buf);
1130         }
1131
1132         return 0;
1133 }
1134
1135 /*
1136  * end of pine addressbook import filter
1137  */
1138
1139
1140 /*
1141  * pine addressbook export filter
1142  *
1143  *  filter doesn't wrap the lines as it should but Pine seems to handle
1144  *  created files without problems - JH
1145  */
1146
1147 static int
1148 pine_export_database(FILE *out, struct db_enumerator e)
1149 {
1150         char *emails;
1151
1152         db_enumerate_items(e) {
1153                 emails = db_email_get(e.item);
1154                 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1155                                 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1156                                 safe_str(db_fget(e.item, NICK)),
1157                                 safe_str(db_name_get(e.item)),
1158                                 emails,
1159                                 safe_str(db_fget(e.item, NOTES))
1160                                 );
1161                 free(emails);
1162         }
1163
1164         return 0;
1165 }
1166
1167 /*
1168  * end of pine addressbook export filter
1169  */
1170
1171
1172 /*
1173  * csv import filter
1174  */
1175
1176 /* FIXME
1177  * these files should be parsed according to a certain
1178  * lay out, or the default if layout is not given, at
1179  * the moment only default is done...
1180  */
1181
1182 #define CSV_COMMENT_CHAR        '#'
1183 #define CSV_DUPLICATE_SEPARATOR " "
1184 #define CSV_TABLE_SIZE(t)       (sizeof (t) / sizeof *(t))
1185
1186 static int csv_conv_table[] = {
1187         NAME,
1188         EMAIL,
1189         PHONE,
1190         NOTES,
1191         NICK
1192 };
1193
1194 static int allcsv_conv_table[] = {
1195         NAME,
1196         EMAIL,
1197         ADDRESS,
1198         ADDRESS2,
1199         CITY,
1200         STATE,
1201         ZIP,
1202         COUNTRY,
1203         PHONE,
1204         WORKPHONE,
1205         FAX,
1206         MOBILEPHONE,
1207         NICK,
1208         URL,
1209         NOTES,
1210         ANNIVERSARY
1211 };
1212
1213 static int palmcsv_conv_table[] = {
1214         NAME,           /* Last name */
1215         NAME,           /* First name */
1216         NOTES,          /* Title */
1217         NICK,           /* Company */
1218         WORKPHONE,
1219         PHONE,
1220         FAX,
1221         MOBILEPHONE,
1222         EMAIL,
1223         ADDRESS,
1224         CITY,
1225         STATE,
1226         ZIP,
1227         COUNTRY,
1228         ANNIVERSARY,
1229 };
1230
1231 static void
1232 csv_convert_emails(char *s)
1233 {
1234         int i;
1235         char *tmp;
1236
1237         if(s == NULL)
1238                 return;
1239
1240         for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1241                 if(i > MAX_LIST_ITEMS - 1) {
1242                         *tmp = 0;
1243                         break;
1244                 }
1245
1246 }
1247
1248 static char *
1249 csv_remove_quotes(char *s)
1250 {
1251         char *copy, *trimmed;
1252         int len;
1253
1254         copy = trimmed = xstrdup(s);
1255         strtrim(trimmed);
1256
1257         len = strlen(trimmed);
1258         if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1259                 if(len < 3) {
1260                         xfree(copy);
1261                         return NULL;
1262                 }
1263                 trimmed[len - 1] = 0;
1264                 trimmed++;
1265                 trimmed = xstrdup(trimmed);
1266                 free(copy);
1267                 return trimmed;
1268         }
1269
1270         xfree(copy);
1271         return xstrdup(s);
1272 }
1273
1274 static int
1275 csv_field_to_item(int *table_base, size_t table_size, int field)
1276 {
1277         if(field < table_size)
1278                 return field_id(table_base[field]);
1279
1280         return -1;
1281 }
1282
1283 static void
1284 csv_store_item(list_item item, int i, char *s)
1285 {
1286         char *newstr = NULL;
1287
1288         if(!s || !*s)
1289                 return;
1290
1291         if( !(newstr = csv_remove_quotes(s)) )
1292                 return;
1293
1294         if(i >= 0) {
1295                 if (item[i] != NULL) {
1296                         char *oldstr = item[i];
1297
1298                         item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1299                                 oldstr, NULL);
1300                         xfree(newstr);
1301                         xfree(oldstr);
1302                 } else {
1303                         item[i] = newstr;
1304                 }
1305         } else {
1306                 xfree(newstr);
1307         }
1308 }
1309
1310 static int
1311 csv_is_valid_quote_end(char *p)
1312 {
1313         if(*p != '\"')
1314                 return FALSE;
1315
1316         for(p++; *p; p++) {
1317                 if(*p == ',')
1318                         return TRUE;
1319                 else if(!ISSPACE(*p))
1320                         return FALSE;
1321         }
1322
1323         return TRUE;
1324 }
1325
1326 static int
1327 csv_is_valid_quote_start(char *p)
1328 {
1329         for(; *p; p++) {
1330                 if(*p == '\"')
1331                         return TRUE;
1332                 else if(!ISSPACE(*p))
1333                         return FALSE;
1334         }
1335
1336         return FALSE;
1337 }
1338
1339 static void
1340 csv_parse_line(char *line, int *table_base, size_t table_size)
1341 {
1342         char *p, *start;
1343         int field;
1344         bool in_quote = FALSE;
1345         list_item item;
1346
1347         item = item_create();
1348
1349         for(p = start = line, field = 0; *p; p++) {
1350                 if(in_quote) {
1351                         if(csv_is_valid_quote_end(p))
1352                                 in_quote = FALSE;
1353                 } else {
1354                         if ( (((p - start) / sizeof (char)) < 2 ) &&
1355                                 csv_is_valid_quote_start(p) )
1356                                 in_quote = TRUE;
1357                 }
1358
1359                 if(*p == ',' && !in_quote) {
1360                         *p = 0;
1361                         csv_store_item(item,
1362                                 csv_field_to_item(table_base,table_size,field),
1363                                 start);
1364                         field++;
1365                         start = p + 1;
1366                 }
1367         }
1368         /*
1369          * store last field
1370          */
1371         csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1372                 start);
1373
1374         csv_convert_emails(item_fget(item, EMAIL));
1375         add_item2database(item);
1376         item_free(&item);
1377 }
1378
1379 static int
1380 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1381 {
1382         char *line = NULL;
1383
1384         while(!feof(in)) {
1385                 line = getaline(in);
1386
1387                 if(line && *line && *line != CSV_COMMENT_CHAR)
1388                         csv_parse_line(line, conv_table, table_size);
1389
1390                 xfree(line);
1391         }
1392
1393         return 0;
1394 }
1395
1396 static int
1397 csv_parse_file(FILE *in)
1398 {
1399         return csv_parse_file_common(in, csv_conv_table,
1400                 CSV_TABLE_SIZE(csv_conv_table));
1401 }
1402
1403 static int
1404 allcsv_parse_file(FILE *in)
1405 {
1406         return csv_parse_file_common(in, allcsv_conv_table,
1407                 CSV_TABLE_SIZE(allcsv_conv_table));
1408 }
1409
1410 static int
1411 palmcsv_parse_file(FILE *in)
1412 {
1413         return csv_parse_file_common(in, palmcsv_conv_table,
1414                 CSV_TABLE_SIZE(palmcsv_conv_table));
1415 }
1416
1417 /*
1418  * end of csv import filter
1419  */
1420
1421 /*
1422  * vCard import filter
1423  */
1424
1425 static char *vcard_fields[] = {
1426         "FN",                   /* FORMATTED NAME */
1427         "EMAIL",                /* EMAIL */
1428         "ADR",                  /* ADDRESS */
1429         "ADR",                  /* ADDRESS2 - not used */
1430         "ADR",                  /* CITY */
1431         "ADR",                  /* STATE */
1432         "ADR",                  /* ZIP */
1433         "ADR",                  /* COUNTRY */
1434         "TEL",                  /* PHONE */
1435         "TEL",                  /* WORKPHONE */
1436         "TEL",                  /* FAX */
1437         "TEL",                  /* MOBILEPHONE */
1438         "NICKNAME",             /* NICK */
1439         "URL",                  /* URL */
1440         "NOTE",                 /* NOTES */
1441         "N",                    /* NAME: special case/mapping in vcard_parse_line() */
1442         NULL                    /* not implemented: ANNIVERSARY, ITEM_FIELDS */
1443 };
1444
1445 /*
1446  * mappings between vCard ADR field and abook's ADDRESS
1447  * see rfc2426 section 3.2.1
1448  */
1449 static int vcard_address_fields[] = {
1450         -1,                     /* vCard(post office box) - not used */
1451         -1,                     /* vCard(the extended address) - not used */
1452         2,                      /* vCard(the street address) - ADDRESS */
1453         4,                      /* vCard(the locality) - CITY */
1454         5,                      /* vCard(the region) - STATE */
1455         6,                      /* vCard(the postal code) - ZIP */
1456         7                       /* vCard(the country name) - COUNTRY */
1457 };
1458
1459 enum {
1460         VCARD_KEY = 0,
1461         VCARD_KEY_ATTRIBUTE,
1462         VCARD_VALUE,
1463 };
1464
1465 static char *
1466 vcard_get_line_element(char *line, int element)
1467 {
1468         int i;
1469         char *line_copy = 0;
1470         char *result = 0;
1471         char *key = 0;
1472         char *key_attr = 0;
1473         char *value = 0;
1474
1475         line_copy = xstrdup(line);
1476
1477         /* change newline characters, if present, to end of string */
1478         for(i=0; line_copy[i]; i++) {
1479                 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1480                         line_copy[i] = '\0';
1481                         break;
1482                 }
1483         }
1484
1485         /* separate key from value */
1486         for(i=0; line_copy[i]; i++) {
1487                 if(line_copy[i] == ':') {
1488                         line_copy[i] = '\0';
1489                         key = line_copy;
1490                         value = &line_copy[i+1];
1491                         break;
1492                 }
1493         }
1494
1495         /* separate key from key attributes */
1496         /* works for vCard 2 as well (automagically) */
1497         if (key) {
1498                 for(i=0; key[i]; i++) {
1499                         if(key[i] == ';') {
1500                                 key[i] = '\0';
1501                                 key_attr = &key[i+1];
1502                                 break;
1503                         }
1504                 }
1505         }
1506
1507         switch(element) {
1508         case VCARD_KEY:
1509                 if(key)
1510                         result = xstrdup(key);
1511                 break;
1512         case VCARD_KEY_ATTRIBUTE:
1513                 if(key_attr)
1514                         result = xstrdup(key_attr);
1515                 break;
1516         case VCARD_VALUE:
1517                 if(value)
1518                         result = xstrdup(value);
1519                 break;
1520         }
1521
1522         xfree(line_copy);
1523         return result;
1524 }
1525
1526 static void
1527 vcard_parse_email(list_item item, char *line)
1528 {
1529         char *email;
1530
1531         email = vcard_get_line_element(line, VCARD_VALUE);
1532
1533         if(item[1]) {
1534                 item[1] = strconcat(item[1], ",", email, 0);
1535                 xfree(email);
1536         }
1537         else {
1538                 item[1] = email;
1539         }
1540 }
1541
1542 static void
1543 vcard_parse_address(list_item item, char *line)
1544 {
1545         int i;
1546         int k;
1547         char *value;
1548         char *address_field;
1549
1550         value = vcard_get_line_element(line, VCARD_VALUE);
1551         if(!value)
1552                 return;
1553
1554         address_field = value;
1555         for(i=k=0; value[i]; i++) {
1556                 if(value[i] == ';') {
1557                         value[i] = '\0';
1558                         if(vcard_address_fields[k] >= 0) {
1559                                 item[vcard_address_fields[k]] = xstrdup(address_field);
1560                         }
1561                         address_field = &value[i+1];
1562                         k++;
1563                         if((k+1)==(sizeof(vcard_address_fields)/sizeof(*vcard_address_fields)))
1564                                 break;
1565                 }
1566         }
1567         item[vcard_address_fields[k]] = xstrdup(address_field);
1568         xfree(value);
1569 }
1570
1571 static void
1572 vcard_parse_name(list_item item, char *line)
1573 {
1574         // store the "N" field into "NAME" *if* no "FN:"
1575         // value has already been stored here
1576         if(item[0]) return;
1577
1578         int i = -1;
1579         item[0] = vcard_get_line_element(line, VCARD_VALUE);
1580         // "N:" can be multivalued => replace ';' separators by ' '
1581         while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1582
1583         // http://www.daniweb.com/software-development/c/code/216919
1584         char *original = item[0], *p = original;
1585         int trimmed = 0;
1586         do {
1587           if (*original != ' ' || trimmed) {
1588             trimmed = 1; *p++ = *original;
1589           }
1590         } while(*original++);
1591 }
1592
1593 static void
1594 vcard_parse_phone(list_item item, char *line)
1595 {
1596         char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1597         char *value = vcard_get_line_element(line, VCARD_VALUE);
1598
1599         /* set the standard number */
1600         if (!type) item_fput(item, PHONE, value);
1601
1602         /*
1603          * see rfc2426 section 3.3.1
1604          * Note: we probably support both vCard 2 and 3
1605          */
1606         else {
1607                 if (strcasestr(type, "home") != NULL)
1608                         item_fput(item, PHONE, xstrdup(value));
1609                 else if (strcasestr(type, "work") != NULL)
1610                         item_fput(item, WORKPHONE, xstrdup(value));
1611                 else if (strcasestr(type, "fax") != NULL)
1612                         item_fput(item, FAX, xstrdup(value));
1613                 else if (strcasestr(type, "cell") != NULL)
1614                         item_fput(item, MOBILEPHONE, xstrdup(value));
1615
1616                 xfree(type);
1617                 xfree(value);
1618         }
1619 }
1620
1621 static void
1622 vcard_parse_line(list_item item, char *line)
1623 {
1624         int i;
1625         char *key;
1626
1627         for(i=0; vcard_fields[i]; i++) {
1628                 key = vcard_fields[i];
1629
1630                 if(0 == strncmp(key, line, strlen(key))) {
1631                         if(0 == strcmp(key, "EMAIL"))
1632                                 vcard_parse_email(item, line);
1633                         else if(i == 2)
1634                                 vcard_parse_address(item, line);
1635                         else if(0 == strcmp(key, "TEL"))
1636                                 vcard_parse_phone(item, line);
1637                         else if(0 == strcmp(key, "N"))
1638                                 vcard_parse_name(item, line);
1639                         else
1640                                 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1641                         return;
1642                 }
1643         }
1644 }
1645
1646 static void
1647 vcard_parse_item(FILE *in)
1648 {
1649         char *line = NULL;
1650         list_item item = item_create();
1651
1652         while(!feof(in)) {
1653                 line = getaline(in);
1654
1655                 if(line && !strncmp("END:VCARD", line, 9)) {
1656                         xfree(line);
1657                         break;
1658                 }
1659                 else if(line) {
1660                         vcard_parse_line(item, line);
1661                         xfree(line);
1662                 }
1663         }
1664
1665         add_item2database(item);
1666         item_free(&item);
1667 }
1668
1669 static int
1670 vcard_parse_file(FILE *in)
1671 {
1672         char *line = NULL;
1673
1674         while(!feof(in)) {
1675                 line = getaline(in);
1676
1677                 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1678                         xfree(line);
1679                         vcard_parse_item(in);
1680                 }
1681                 else if(line) {
1682                         xfree(line);
1683                 }
1684         }
1685
1686         return 0;
1687 }
1688
1689 /*
1690  * end of vCard import filter
1691  */
1692
1693 /*
1694  * csv addressbook export filters
1695  */
1696
1697 #define CSV_LAST                (-1)
1698 #define CSV_UNDEFINED           (-2)
1699 #define CSV_SPECIAL(X)          (-3 - (X))
1700 #define CSV_IS_SPECIAL(X)       ((X) <= -3)
1701
1702 static int
1703 csv_export_common(FILE *out, struct db_enumerator e,
1704                 int fields[], void (*special_func)(FILE *, int, int))
1705 {
1706         int i;
1707
1708         db_enumerate_items(e) {
1709                 for(i = 0; fields[i] != CSV_LAST; i++) {
1710                         if(fields[i] == CSV_UNDEFINED)
1711                                 fprintf(out, "\"\"");
1712                         else if(CSV_IS_SPECIAL(fields[i])) {
1713                                 if(special_func)
1714                                         (*special_func)(out, e.item, fields[i]);
1715                         } else
1716                                 /*fprintf(out,(
1717                         strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1718                         strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1719                                 "\"%s\"" : "%s",
1720                                 safe_str(database[e.item][field_idx(fields[i])])
1721                                 );*/
1722                                 fprintf(out, "\"%s\"",
1723                                         safe_str(db_fget(e.item,fields[i])));
1724
1725                         if(fields[i + 1] != CSV_LAST)
1726                                 fputc(',', out);
1727                 }
1728                 fputc('\n', out);
1729         }
1730
1731         return 0;
1732 }
1733
1734 static int
1735 csv_export_database(FILE *out, struct db_enumerator e)
1736 {
1737         int csv_export_fields[] = {
1738                 NAME,
1739                 EMAIL,
1740                 PHONE,
1741                 NOTES,
1742                 NICK,
1743                 CSV_LAST
1744         };
1745
1746         csv_export_common(out, e, csv_export_fields, NULL);
1747
1748         return 0;
1749 }
1750
1751 static int
1752 allcsv_export_database(FILE *out, struct db_enumerator e)
1753 {
1754         /*
1755          * TODO: Should get these atomatically from abook_fileds
1756          *  - JH
1757          */
1758         int allcsv_export_fields[] = {
1759                 NAME,
1760                 EMAIL,
1761                 ADDRESS,
1762                 ADDRESS2,
1763                 CITY,
1764                 STATE,
1765                 ZIP,
1766                 COUNTRY,
1767                 PHONE,
1768                 WORKPHONE,
1769                 FAX,
1770                 MOBILEPHONE,
1771                 NICK,
1772                 URL,
1773                 NOTES,
1774                 ANNIVERSARY,
1775                 GROUPS,
1776                 CSV_LAST
1777         };
1778
1779         fprintf(out, "#");
1780         fprintf(out, "\"NAME\",");
1781         fprintf(out, "\"EMAIL\",");
1782         fprintf(out, "\"ADDRESS\",");
1783         fprintf(out, "\"ADDRESS2\",");
1784         fprintf(out, "\"CITY\",");
1785         fprintf(out, "\"STATE\",");
1786         fprintf(out, "\"ZIP\",");
1787         fprintf(out, "\"COUNTRY\",");
1788         fprintf(out, "\"PHONE\",");
1789         fprintf(out, "\"WORKPHONE\",");
1790         fprintf(out, "\"FAX\",");
1791         fprintf(out, "\"MOBILEPHONE\",");
1792         fprintf(out, "\"NICK\",");
1793         fprintf(out, "\"URL\",");
1794         fprintf(out, "\"NOTES\",");
1795         fprintf(out, "\"ANNIVERSARY\",");
1796         fprintf(out, "\"GROUPS\"\n");
1797
1798         csv_export_common(out, e, allcsv_export_fields, NULL);
1799
1800         return 0;
1801 }
1802
1803 /*
1804  * palm csv
1805  */
1806
1807 #define PALM_CSV_NAME   CSV_SPECIAL(0)
1808 #define PALM_CSV_END    CSV_SPECIAL(1)
1809 #define PALM_CSV_CAT    CSV_SPECIAL(2)
1810
1811 static void
1812 palm_split_and_write_name(FILE *out, char *name)
1813 {
1814         char *p;
1815
1816         assert(name);
1817
1818         if ( (p = strchr(name, ' ')) ) {
1819                 /*
1820                  * last name first
1821                  */
1822                 fprintf(out, "\"%s\",\"" , p + 1);
1823                 fwrite((void *)name, p - name, sizeof(char), out);
1824                 fputc('\"', out);
1825         } else {
1826                 fprintf(out, "\"%s\"", safe_str(name));
1827         }
1828 }
1829
1830 static void
1831 palm_csv_handle_specials(FILE *out, int item, int field)
1832 {
1833         switch(field) {
1834                 case PALM_CSV_NAME:
1835                         palm_split_and_write_name(out, db_name_get(item));
1836                         break;
1837                 case PALM_CSV_CAT:
1838                         fprintf(out, "\"abook\"");
1839                         break;
1840                 case PALM_CSV_END:
1841                         fprintf(out, "\"0\"");
1842                         break;
1843                 default:
1844                         assert(0);
1845         }
1846 }
1847
1848 static int
1849 palm_export_database(FILE *out, struct db_enumerator e)
1850 {
1851         int palm_export_fields[] = {
1852                 PALM_CSV_NAME,          /* LASTNAME, FIRSTNAME  */
1853                 CSV_UNDEFINED,          /* TITLE                */
1854                 CSV_UNDEFINED,          /* COMPANY              */
1855                 WORKPHONE,              /* WORK PHONE           */
1856                 PHONE,                  /* HOME PHONE           */
1857                 FAX,                    /* FAX                  */
1858                 MOBILEPHONE,            /* OTHER                */
1859                 EMAIL,                  /* EMAIL                */
1860                 ADDRESS,                /* ADDRESS              */
1861                 CITY,                   /* CITY                 */
1862                 STATE,                  /* STATE                */
1863                 ZIP,                    /* ZIP                  */
1864                 COUNTRY,                /* COUNTRY              */
1865                 NICK,                   /* DEFINED 1            */
1866                 URL,                    /* DEFINED 2            */
1867                 CSV_UNDEFINED,          /* DEFINED 3            */
1868                 CSV_UNDEFINED,          /* DEFINED 4            */
1869                 NOTES,                  /* NOTE                 */
1870                 PALM_CSV_END,           /* "0"                  */
1871                 PALM_CSV_CAT,           /* CATEGORY             */
1872                 CSV_LAST
1873         };
1874
1875         csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
1876
1877         return 0;
1878 }
1879
1880 /*
1881  * end of csv export filters
1882  */
1883
1884 /*
1885  * vCard 2 addressbook export filter
1886  */
1887
1888 static int
1889 vcard_export_database(FILE *out, struct db_enumerator e)
1890 {
1891   db_enumerate_items(e)
1892     vcard_export_item(out, e.item);
1893   return 0;
1894 }
1895
1896 void
1897 vcard_export_item(FILE *out, int item)
1898 {
1899         int j;
1900         char *name, *tmp;
1901         abook_list *emails, *em;
1902         fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
1903                 safe_str(db_name_get(item)));
1904
1905         name = get_surname(db_name_get(item));
1906         for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
1907           if((db_name_get(item))[j] == ' ')
1908             break;
1909         }
1910         fprintf(out, "N:%s;%.*s\r\n",
1911                 safe_str(name),
1912                 j,
1913                 safe_str(db_name_get(item))
1914                 );
1915
1916         free(name);
1917
1918         if(db_fget(item, ADDRESS))
1919           fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n",
1920                   safe_str(db_fget(item, ADDRESS)),
1921                   safe_str(db_fget(item, ADDRESS2)),
1922                   safe_str(db_fget(item, CITY)),
1923                   safe_str(db_fget(item, STATE)),
1924                   safe_str(db_fget(item, ZIP)),
1925                   safe_str(db_fget(item, COUNTRY))
1926                   );
1927
1928         if(db_fget(item, PHONE))
1929           fprintf(out, "TEL;HOME:%s\r\n",
1930                   db_fget(item, PHONE));
1931         if(db_fget(item, WORKPHONE))
1932           fprintf(out, "TEL;WORK:%s\r\n",
1933                   db_fget(item, WORKPHONE));
1934         if(db_fget(item, FAX))
1935           fprintf(out, "TEL;FAX:%s\r\n",
1936                   db_fget(item, FAX));
1937         if(db_fget(item, MOBILEPHONE))
1938           fprintf(out, "TEL;CELL:%s\r\n",
1939                   db_fget(item, MOBILEPHONE));
1940
1941         tmp = db_email_get(item);
1942         if(*tmp) {
1943           emails = csv_to_abook_list(tmp);
1944
1945           for(em = emails; em; em = em->next)
1946             fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data);
1947
1948           abook_list_free(&emails);
1949         }
1950         free(tmp);
1951
1952         if(db_fget(item, NOTES))
1953           fprintf(out, "NOTE:%s\r\n",
1954                   db_fget(item, NOTES));
1955         if(db_fget(item, URL))
1956           fprintf(out, "URL:%s\r\n",
1957                   db_fget(item, URL));
1958
1959         fprintf(out, "END:VCARD\r\n\r\n");
1960
1961 }
1962
1963 /*
1964  * end of vCard export filter
1965  */
1966
1967
1968 /*
1969  * mutt alias export filter
1970  */
1971
1972 static char *
1973 mutt_alias_genalias(int i)
1974 {
1975         char *tmp, *pos;
1976
1977         if(db_fget(i, NICK))
1978                 return xstrdup(db_fget(i, NICK));
1979
1980         tmp = xstrdup(db_name_get(i));
1981
1982         if( ( pos = strchr(tmp, ' ') ) )
1983                 *pos = 0;
1984
1985         strlower(tmp);
1986
1987         return tmp;
1988 }
1989
1990 /*
1991  * This function is a variant of abook_list_to_csv
1992  * */
1993 static char *
1994 mutt_alias_gengroups(int i)
1995 {
1996         char *groups, *res = NULL;
1997         char groupstr[7] = "-group ";
1998         abook_list *list, *tmp;
1999
2000         groups = db_fget(i, GROUPS);
2001
2002         if(!groups)
2003                 return NULL;
2004
2005         list = csv_to_abook_list(groups);
2006         for(tmp = list; tmp; tmp = tmp->next) {
2007                 if(tmp == list) {
2008                         res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2009                         res = strcpy(res, groupstr);
2010                 } else {
2011                         res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2012                         strcat(res, " ");
2013                         strcat(res, groupstr);
2014                 }
2015                 strcat(res, tmp->data);
2016         }
2017         abook_list_free(&list);
2018         xfree(groups);
2019
2020         return res;
2021 }
2022
2023 static int
2024 mutt_alias_export(FILE *out, struct db_enumerator e)
2025 {
2026         char email[MAX_EMAIL_LEN];
2027         char *alias = NULL;
2028         char *groups = NULL;
2029         int email_addresses;
2030         char *ptr;
2031
2032         db_enumerate_items(e) {
2033                 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2034                 groups = (field_id(GROUPS) != -1) ?  mutt_alias_gengroups(e.item) : NULL;
2035                 get_first_email(email, e.item);
2036
2037                 /* do not output contacts without email address */
2038                 /* cause this does not make sense in mutt aliases */
2039                 if (*email) {
2040
2041                         /* output first email address */
2042                         fprintf(out,"alias ");
2043                         if(groups)
2044                                 fprintf(out, "%s ", groups);
2045                         if(alias)
2046                                 fprintf(out, "%s ", alias);
2047                         fprintf(out, "%s <%s>\n",
2048                                         db_name_get(e.item),
2049                                         email);
2050
2051                         /* number of email addresses */
2052                         email_addresses = 1;
2053                         ptr = db_email_get(e.item);
2054                         while (*ptr != '\0') {
2055                                 if (*ptr == ',') {
2056                                         email_addresses++;
2057                                 }
2058                                 ptr++;
2059                         }
2060
2061                         /* output other email addresses */
2062                         while (email_addresses-- > 1) {
2063                                 roll_emails(e.item, ROTATE_RIGHT);
2064                                 get_first_email(email, e.item);
2065                                 fprintf(out,"alias ");
2066                                 if( groups )
2067                                         fprintf(out, "%s ", groups);
2068                                 if(alias)
2069                                         fprintf(out, "%s__%s ", alias, email);
2070                                 else
2071                                         fprintf(out, "%s__%s ", db_name_get(e.item), email);
2072                                 fprintf(out, "%s <%s>\n",
2073                                                 db_name_get(e.item),
2074                                                 email);
2075                         }
2076                         roll_emails(e.item, ROTATE_RIGHT);
2077                         xfree(alias);
2078                         xfree(groups);
2079                 }
2080         }
2081
2082         return 0;
2083 }
2084
2085 void muttq_print_item(FILE *file, int item)
2086 {
2087         abook_list *emails, *e;
2088         char *tmp = db_email_get(item);
2089
2090         emails = csv_to_abook_list(tmp);
2091         free(tmp);
2092
2093         for(e = emails; e; e = e->next) {
2094                 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2095                                 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2096                                 );
2097                 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2098                         break;
2099         }
2100         abook_list_free(&emails);
2101 }
2102
2103 static int
2104 mutt_query_export_database(FILE *out, struct db_enumerator e)
2105 {
2106   fprintf(out, "All items\n");
2107   db_enumerate_items(e)
2108     muttq_print_item(out, e.item);
2109   return 0;
2110 }
2111
2112 /*
2113  * end of mutt alias export filter
2114  */
2115
2116
2117 /*
2118  * printable export filter
2119  */
2120
2121
2122 static void
2123 text_write_address_us(FILE *out, int i) {
2124         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2125
2126         if(db_fget(i, ADDRESS2))
2127                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2128
2129         if(db_fget(i, CITY))
2130                 fprintf(out, "\n%s", db_fget(i, CITY));
2131
2132         if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2133                 fputc('\n', out);
2134
2135                 if(db_fget(i, STATE)) {
2136                         fprintf(out, "%s", db_fget(i, STATE));
2137                         if(db_fget(i, ZIP))
2138                                 fputc(' ', out);
2139                 }
2140
2141                 if(db_fget(i, ZIP))
2142                         fprintf(out, "%s", db_fget(i, ZIP));
2143         }
2144
2145         if(db_fget(i, COUNTRY))
2146                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2147 }
2148
2149
2150 static void
2151 text_write_address_uk(FILE *out, int i) {
2152         int j;
2153
2154         for(j = ADDRESS; j <= COUNTRY; j++)
2155                 if(db_fget(i, j))
2156                         fprintf(out, "\n%s", db_fget(i, j));
2157 }
2158
2159 static void
2160 text_write_address_eu(FILE *out, int i) {
2161         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2162
2163         if(db_fget(i, ADDRESS2))
2164                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2165
2166         if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2167                 fputc('\n', out);
2168
2169                 if(db_fget(i, ZIP)) {
2170                         fprintf(out, "%s", db_fget(i, ZIP));
2171                         if(db_fget(i, CITY))
2172                                 fputc(' ', out);
2173                 }
2174
2175                 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2176         }
2177
2178         if(db_fget(i, STATE))
2179                 fprintf(out, "\n%s", db_fget(i, STATE));
2180
2181         if(db_fget(i, COUNTRY))
2182                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2183 }
2184
2185 static int
2186 text_export_database(FILE * out, struct db_enumerator e)
2187 {
2188         abook_list *emails, *em;
2189         int j;
2190         char *realname = get_real_name(), *str = NULL, *tmp;
2191         char *style = opt_get_str(STR_ADDRESS_STYLE);
2192
2193         fprintf(out,
2194                 "-----------------------------------------\n%s's address book\n"
2195                 "-----------------------------------------\n\n\n",
2196                 realname);
2197         free(realname);
2198
2199         db_enumerate_items(e) {
2200                 fprintf(out,
2201                         "-----------------------------------------\n\n");
2202                 fprintf(out, "%s", db_name_get(e.item));
2203                 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2204                         fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2205                 fprintf(out, "\n");
2206
2207                 tmp = db_email_get(e.item);
2208                 if(*tmp) {
2209                         emails = csv_to_abook_list(tmp);
2210
2211                         fprintf(out, "\n");
2212                         for(em = emails; em; em = em->next)
2213                                 fprintf(out, "%s\n", em->data);
2214
2215                         abook_list_free(&emails);
2216                 }
2217                 free(tmp);
2218                 /* Print address */
2219                 if(db_fget(e.item, ADDRESS)) {
2220                         if(!safe_strcmp(style, "us"))   /* US like */
2221                                 text_write_address_us(out, e.item);
2222                         else if(!safe_strcmp(style, "uk"))      /* UK like */
2223                                 text_write_address_uk(out, e.item);
2224                         else    /* EU like */
2225                                 text_write_address_eu(out, e.item);
2226
2227                         fprintf(out, "\n");
2228                 }
2229
2230                 if((db_fget(e.item, PHONE)) ||
2231                         (db_fget(e.item, WORKPHONE)) ||
2232                         (db_fget(e.item, FAX)) ||
2233                         (db_fget(e.item, MOBILEPHONE))) {
2234                         fprintf(out, "\n");
2235                         for(j = PHONE; j <= MOBILEPHONE; j++)
2236                                 if(db_fget(e.item, j)) {
2237                                         get_field_info(field_id(j),
2238                                                         NULL, &str, NULL);
2239                                         fprintf(out, "%s: %s\n", str,
2240                                                 db_fget(e.item, j));
2241                                 }
2242                 }
2243
2244                 if(db_fget(e.item, URL))
2245                         fprintf(out, "\n%s\n", db_fget(e.item, URL));
2246                 if(db_fget(e.item, NOTES))
2247                         fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2248
2249                 fprintf(out, "\n");
2250         }
2251
2252         fprintf(out, "-----------------------------------------\n");
2253
2254         return 0;
2255 }
2256
2257 /*
2258  * end of printable export filter
2259  */
2260
2261 /*
2262  * elm alias export filter
2263  */
2264
2265 static int
2266 elm_alias_export(FILE *out, struct db_enumerator e)
2267 {
2268         char email[MAX_EMAIL_LEN];
2269         char *alias = NULL;
2270
2271         db_enumerate_items(e) {
2272                 alias = mutt_alias_genalias(e.item);
2273                 get_first_email(email, e.item);
2274                 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2275                 xfree(alias);
2276         }
2277
2278         return 0;
2279 }
2280
2281 /*
2282  * end of elm alias export filter
2283  */
2284
2285
2286 /*
2287  * Spruce export filter
2288  */
2289
2290 static int
2291 spruce_export_database (FILE *out, struct db_enumerator e)
2292 {
2293         char email[MAX_EMAIL_LEN];
2294
2295         fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2296
2297         db_enumerate_items(e) {
2298                 get_first_email(email, e.item);
2299                 if(strcmp(email, "")) {
2300                         fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2301                                         e.item,
2302                                         db_name_get(e.item),
2303                                         email,
2304                                         safe_str(db_fget(e.item, NOTES))
2305                                         );
2306                 }
2307         }
2308
2309         fprintf (out, "# End of address book file.\n");
2310
2311         return 0;
2312 }
2313
2314 /*
2315  * end of Spruce export filter
2316  */
2317
2318 /*
2319  * wanderlust addressbook export filter
2320  */
2321
2322 static int
2323 wl_export_database(FILE *out, struct db_enumerator e)
2324 {
2325         char email[MAX_EMAIL_LEN];
2326
2327         fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2328         db_enumerate_items(e) {
2329                 get_first_email(email, e.item);
2330                 if(*email) {
2331                         fprintf(out,
2332                                 "%s\t\"%s\"\t\"%s\"\n",
2333                                 email,
2334                                 safe_str(db_fget(e.item, NICK)),
2335                                 safe_str(db_name_get(e.item))
2336                         );
2337                 }
2338         }
2339
2340         fprintf (out, "\n# End of address book file.\n");
2341
2342         return 0;
2343 }
2344
2345 /*
2346  * end of wanderlust addressbook export filter
2347  */
2348
2349 /*
2350  * BSD calendar export filter
2351  */
2352
2353 static int
2354 bsdcal_export_database(FILE *out, struct db_enumerator e)
2355 {
2356         db_enumerate_items(e) {
2357                 int year, month = 0, day = 0;
2358                 char *anniversary = db_fget(e.item, ANNIVERSARY);
2359
2360                 if(anniversary) {
2361                         if(!parse_date_string(anniversary, &day, &month, &year))
2362                                 continue;
2363
2364                         fprintf(out,
2365                                 _("%02d/%02d\tAnniversary of %s\n"),
2366                                 month,
2367                                 day,
2368                                 safe_str(db_name_get(e.item))
2369                         );
2370                 }
2371         }
2372
2373         return 0;
2374 }
2375
2376 // see enum field_types @database.h
2377 static char *conv_table[] = {
2378   "name",
2379   "email",
2380   "address",
2381   "address2",
2382   "city",
2383   "state",
2384   "zip",
2385   "country",
2386   "phone",
2387   "workphone",
2388   "fax",
2389   "mobile",
2390   "nick",
2391   "url",
2392   "notes",
2393   "anniversary",
2394   0 /* ITEM_FIELDS */
2395 };
2396
2397 static int
2398 find_field_enum(char *s) {
2399   int i = 0;
2400   while (conv_table[i]) {
2401     if(!safe_strcmp(conv_table[i], s))
2402       return i;
2403     i++;
2404   }
2405   // failed
2406   return -1;
2407 }
2408
2409 /* Convert a string with named placeholders to
2410    a *printf() compatible string.
2411    Stores the abook field values into ft. */
2412 void
2413 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2414 {
2415         if(! fmt_string || ! ft) {
2416           fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2417           exit(EXIT_FAILURE);
2418         }
2419
2420         char *p, *start, *field_name = NULL;
2421         p = start = s;
2422
2423         while(*p) {
2424                 if(*p == '{') {
2425                   start = ++p;
2426                   p = strchr(start, '}');
2427                   if(! *p) {
2428                     fprintf(stderr, _("parse_custom_format: invalid format\n"));
2429                     exit(EXIT_FAILURE);
2430                   }
2431                   strcat(fmt_string, "%s");
2432                   field_name = strndup(start, (size_t)(p-start));
2433                   *ft = find_field_enum(field_name);
2434                   if(*ft == -1) {
2435                     fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2436                     exit(EXIT_FAILURE);
2437                   }
2438
2439                   ft++;
2440                   p++;
2441                   start = p;
2442                 } else {
2443                   p = strchr(start, '{');
2444                   if(p && *p) {
2445                     strncat(fmt_string, start, (size_t)(p-start));
2446                     start = p;
2447                   }
2448                   else {
2449                     strncat( fmt_string,
2450                              start,
2451                              FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2452                     break;
2453                   }
2454                 }
2455         }
2456         *ft = 66;
2457 }
2458
2459 static int
2460 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2461
2462
2463 // used to store the format string from --outformatstr when "custom" format is used
2464 // default value overriden in export_file()
2465 extern char *parsed_custom_format;
2466 extern enum field_types *custom_format_fields;
2467
2468 /* wrapper for custom_export_item:
2469    1) avoid messing with extern pointer
2470    2) adds \n
2471    3) follow the prototype needed for an abook_output_item_filter entry */
2472 void
2473 custom_print_item(FILE *out, int item)
2474 {
2475
2476   if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2477     fprintf(out, "\n");
2478 }
2479
2480 static int
2481 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2482 {
2483   char *p, *q = 0;
2484
2485   // if the first character is '!':
2486   // we first check that all fields exist before continuing
2487   if(*fmt == '!') {
2488     enum field_types *ftp = ft;
2489     while(*ft != 66) {
2490       if(! db_fget(item, *ft) )
2491         return 1;
2492       ft++;
2493     }
2494     ft = ftp;
2495     fmt++;
2496   }
2497
2498   while (*fmt) {
2499     if(!strncmp(fmt, "%s", 2)) {
2500       fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2501       ft++;
2502       fmt+=2;
2503     } else if (*ft == 66) {
2504       fprintf(out, "%s", fmt);
2505       return 0;
2506     } else {
2507       p = strchr(fmt, '%');
2508       if(*p) {
2509         q = strndup(fmt, (size_t)(p-fmt));
2510         fprintf(out, "%s", q);
2511         free(q);
2512         fmt = p;
2513       }
2514       else {
2515         fprintf(out, "%s", fmt);
2516         return 0;
2517       }
2518     }
2519   }
2520
2521   return 0;
2522 }
2523
2524 // used to store the format string from --outformatstr when "custom" format is used
2525 // default value overriden from abook.c
2526 extern char custom_format[FORMAT_STRING_LEN];
2527
2528 static int
2529 custom_export_database(FILE *out, struct db_enumerator e)
2530 {
2531         char *format_string =
2532           (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2533
2534         enum field_types *ft =
2535           (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2536
2537         parse_custom_format(custom_format, format_string, ft);
2538
2539         db_enumerate_items(e) {
2540           if(custom_export_item(out, e.item, format_string, ft) == 0)
2541             fprintf(out, "\n");
2542         }
2543         return 0;
2544 }
2545
2546 /*
2547  * end of BSD calendar export filter
2548  */
2549