]> git.deb.at Git - pkg/abook.git/blob - filter.c
ldif: cleanup ldif_convert()
[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 static char *
552 ldif_read_line(FILE *in)
553 {
554         char *buf = NULL;
555         char *ptr, *tmp;
556         long pos;
557         int i;
558
559         for(i = 1;;i++) {
560                 char *line;
561
562                 pos = ftell(in);
563                 line = getaline(in);
564
565                 if(feof(in) || !line)
566                         break;
567
568                 if(i == 1) {
569                         buf = line;
570                         continue;
571                 }
572
573                 if(*line != ' ') {
574                         fseek(in, pos, SEEK_SET); /* fixme ! */
575                         free(line);
576                         break;
577                 }
578
579                 ptr = line;
580                 while( *ptr == ' ')
581                         ptr++;
582
583                 tmp = buf;
584                 buf = strconcat(buf, ptr, NULL);
585                 free(tmp);
586                 free(line);
587         }
588
589         if(buf && *buf == '#' ) {
590                 free(buf);
591                 return NULL;
592         }
593
594         return buf;
595 }
596
597 static void
598 ldif_add_item(ldif_item li)
599 {
600         list_item item;
601         int i;
602
603         item = item_create();
604
605         if(!li[LDIF_ITEM_FIELDS -1])
606                 goto bail_out;
607
608
609         for(i=0; i < LDIF_ITEM_FIELDS; i++) {
610                 if(ldif_conv_table[i] >= 0 && li[i] && *li[i])
611                         item_fput(item,ldif_conv_table[i],xstrdup(li[i]));
612         }
613
614         add_item2database(item);
615
616 bail_out:
617         for(i=0; i < LDIF_ITEM_FIELDS; i++)
618                 xfree(li[i]);
619         item_free(&item);
620
621 }
622
623 static void
624 ldif_convert(ldif_item item, char *type, char *value)
625 {
626         int i;
627
628         if(!strcmp(type, "dn")) {
629                 ldif_add_item(item);
630                 return;
631         }
632
633         for(i=0; i < LDIF_ITEM_FIELDS - 1; i++) {
634                 if(!strcasecmp(ldif_field_names[i], type) && *value) {
635                         item_fput(item, i, xstrdup(value));
636                         break;
637                 }
638         }
639 }
640
641 static int
642 ldif_parse_file(FILE *handle)
643 {
644         char *line = NULL;
645         char *type, *value;
646         int vlen;
647         ldif_item item;
648
649         memset(item, 0, sizeof(item));
650
651         do {
652                 if( !(line = ldif_read_line(handle)) )
653                         continue;
654
655                 if(-1 == (str_parse_line(line, &type, &value, &vlen))) {
656                         xfree(line);
657                         continue; /* just skip the errors */
658                 }
659
660                 ldif_fix_string(value);
661
662                 ldif_convert(item, type, value);
663
664                 xfree(line);
665         } while ( !feof(handle) );
666
667         ldif_convert(item, "dn", "");
668
669         return 0;
670 }
671
672 static void
673 ldif_fix_string(char *str)
674 {
675         int i, j;
676
677         for(i = 0, j = 0; j < (int)strlen(str); i++, j++)
678                 str[i] = ( str[j] == (char)0xc3 ?
679                                 (char) str[++j] + (char) 0x40 :
680                                 str[j] );
681
682         str[i] = 0;
683 }
684
685 /*
686  * end of ldif import
687  */
688
689 /*
690  * mutt alias import filter
691  */
692
693 #include "getname.h"
694
695 static int
696 mutt_read_line(FILE *in, char **groups, char **alias, char **rest)
697 {
698         char *line, *ptr;
699         char *start, *end;
700         abook_list *glist = NULL;
701
702         if( !(line = ptr = getaline(in)) )
703                 return 1; /* error / EOF */
704
705         SKIPWS(ptr);
706
707         if(strncmp("alias", ptr, 5)) {
708                 free(line);
709                 return 1;
710         }
711
712         ptr += 5;
713         SKIPWS(ptr);
714
715         /* If the group option is used, save the groups */
716         *groups = NULL;
717         start = ptr;
718         int n_groups;
719         for(n_groups = 0; 0 == strncmp("-group", ptr, 6); n_groups++) {
720                 ptr += 6;
721                 SKIPWS(ptr);
722                 start = ptr;
723                 SKIPNONWS(ptr);
724                 end = ptr;
725                 abook_list_append(&glist,xstrndup(start, end - start));
726                 SKIPWS(ptr);
727         }
728
729         if(n_groups && groups)
730                 *groups = abook_list_to_csv(glist);
731
732         abook_list_free(&glist);        
733
734         /* alias */
735         start = ptr;
736         SKIPNONWS(ptr);
737         end = ptr;
738         SKIPWS(ptr);
739         if(alias)
740                 *alias = xstrndup(start, end - start);
741
742         /* rest (email) */
743         *rest = xstrdup(ptr);
744
745         xfree(line);
746         return 0;
747 }
748
749 static void
750 mutt_fix_quoting(char *p)
751 {
752         char *escape = 0;
753
754         for(; *p; p++) {
755                 switch(*p) {
756                         case '\"':
757                                 if(escape)
758                                         *escape = ' ';
759                                 break;
760                         case '\\':
761                                 escape = p;
762                                 break;
763                         default:
764                                 escape = 0;
765                 }
766         }
767 }
768
769 static void
770 mutt_parse_email(list_item item)
771 {
772         char *line = item_fget(item, NAME);
773         char *tmp;
774         char *name, *email;
775 #if 0
776         char *start = line;
777         int i = 0;
778 #endif
779
780         mutt_fix_quoting(line);
781         tmp = strconcat("From: ", line, NULL);
782         getname(tmp, &name, &email);
783         free(tmp);
784
785         if(name)
786                 item_fput(item, NAME, name);
787         else
788                 return;
789
790         if(email)
791                 item_fput(item, EMAIL, email);
792         else
793                 return;
794
795         /*
796          * this is completely broken
797          */
798 #if 0
799         while( (start = strchr(start, ',')) && i++ < MAX_EMAILS - 1) {
800                 tmp = strconcat("From: ", ++start, NULL);
801                 getname(tmp, &name, &email);
802                 free(tmp);
803                 free(name);
804                 if(email) {
805                         if(*email) {
806                                 tmp = strconcat(item[EMAIL], ",", email, NULL);
807                                 free(item[EMAIL]);
808                                 item[EMAIL] = tmp;
809                         } else {
810                                 xfree(email);
811                         }
812                 }
813         }
814 #endif
815 }
816
817 static int
818 mutt_parse_file(FILE *in)
819 {
820         list_item item = item_create();
821
822         for(;;) {
823                 memset(item, 0, fields_count * sizeof(char *));
824
825                 if(!mutt_read_line(in,
826                         (field_id(GROUPS) != -1) ? &item[field_id(GROUPS)] : NULL,
827                         (field_id(NICK) != -1) ? &item[field_id(NICK)] : NULL,
828                         &item[field_id(NAME)]) )
829                         mutt_parse_email(item);
830
831                 if(feof(in)) {
832                         item_empty(item);
833                         break;
834                 }
835
836                 add_item2database(item);
837         }
838         item_free(&item);
839
840         return 0;
841 }
842
843 /*
844  * end of mutt alias import filter
845  */
846
847
848 /*
849  * ldif export filter
850  */
851
852 static void
853 ldif_fput_type_and_value(FILE *out,char *type, char *value )
854 {
855         char *tmp;
856
857         tmp = ldif_type_and_value(type, value, strlen(value));
858
859         fputs(tmp, out);
860
861         free(tmp);
862 }
863
864 static int
865 ldif_export_database(FILE *out, struct db_enumerator e)
866 {
867         char email[MAX_EMAILSTR_LEN];
868
869         fprintf(out, "version: 1\n");
870
871         db_enumerate_items(e) {
872                 char *tmp;
873                 int j;
874                 get_first_email(email, e.item);
875
876                 tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email);
877
878                 ldif_fput_type_and_value(out, "dn", tmp);
879                 free(tmp);
880
881                 for(j = 0; j < LDIF_ITEM_FIELDS; j++) {
882                         if(ldif_conv_table[j] >= 0) {
883                                 if(ldif_conv_table[j] == EMAIL)
884                                         ldif_fput_type_and_value(out,
885                                                 ldif_field_names[j], email);
886                                 else if(db_fget(e.item,ldif_conv_table[j]))
887                                         ldif_fput_type_and_value(out,
888                                                 ldif_field_names[j],
889                                                 db_fget(e.item,
890                                                         ldif_conv_table[j]));
891                         }
892                 }
893
894                 fprintf(out, "objectclass: top\n"
895                                 "objectclass: person\n\n");
896         }
897
898         return 0;
899 }
900
901 /*
902  * end of ldif export filter
903  */
904
905 /*
906  * html export filter
907  */
908
909 static void            html_export_write_head(FILE *out);
910 static void            html_export_write_tail(FILE *out);
911
912 extern struct index_elem *index_elements;
913
914 static void
915 html_print_emails(FILE *out, struct list_field *f)
916 {
917         abook_list *l = csv_to_abook_list(f->data);
918
919         for(; l; l = l->next) {
920                 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
921                 if(l->next)
922                         fprintf(out, ", ");
923         }
924
925         abook_list_free(&l);
926 }
927
928 static int
929 html_export_database(FILE *out, struct db_enumerator e)
930 {
931         struct list_field f;
932         struct index_elem *cur;
933
934         if(list_is_empty())
935                 return 2;
936
937         init_index();
938
939         html_export_write_head(out);
940
941         db_enumerate_items(e) {
942                 fprintf(out, "<tr>");
943                 for(cur = index_elements; cur; cur = cur->next) {
944                         if(cur->type != INDEX_FIELD)
945                                 continue;
946
947                         get_list_field(e.item, cur, &f);
948
949                         if(f.type == FIELD_EMAILS) {
950                                 fprintf(out, "<td>");
951                                 html_print_emails(out, &f);
952                                 fprintf(out, "</td>");
953                                 continue;
954                         } else {
955                                 fprintf(out, "<td>%s</td>", safe_str(f.data));
956                         }
957                 }
958                 fprintf(out, "</tr>\n");
959         }
960
961         html_export_write_tail(out);
962
963         return 0;
964 }
965
966 static void
967 html_export_write_head(FILE *out)
968 {
969         char *realname = get_real_name(), *str;
970         struct index_elem *cur;
971
972         fprintf(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
973         fprintf(out, "<html>\n<head>\n <title>%s's addressbook</title>",
974                         realname );
975         fprintf(out, "\n</head>\n<body>\n");
976         fprintf(out, "\n<h2>%s's addressbook</h2>\n", realname );
977         fprintf(out, "<br><br>\n\n");
978
979         fprintf(out, "<table border=\"1\" align=\"center\">\n<tr>");
980         for(cur = index_elements; cur; cur = cur->next) {
981                 if(cur->type != INDEX_FIELD)
982                         continue;
983
984                 get_field_info(cur->d.field.id, NULL, &str, NULL);
985                 fprintf(out, "<th>%s</th>", str);
986         }
987         fprintf(out, "</tr>\n\n");
988
989         free(realname);
990 }
991
992 static void
993 html_export_write_tail(FILE *out)
994 {
995         fprintf(out, "\n</table>\n");
996         fprintf(out, "\n</body>\n</html>\n");
997 }
998
999 /*
1000  * end of html export filter
1001  */
1002
1003
1004 /*
1005  * pine addressbook import filter
1006  */
1007
1008 #define PINE_BUF_SIZE 2048
1009
1010 static void
1011 pine_fixbuf(char *buf)
1012 {
1013         int i,j;
1014
1015         for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1016                 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1017 }
1018
1019 static void
1020 pine_convert_emails(char *s)
1021 {
1022         int i;
1023         char *tmp;
1024
1025         if(s == NULL || *s != '(')
1026                 return;
1027
1028         for(i = 0; s[i]; i++)
1029                 s[i] = s[i + 1];
1030
1031         if( ( tmp = strchr(s,')')) )
1032                 *tmp = '\0';
1033
1034         for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1035                 if(i > MAX_LIST_ITEMS - 1) {
1036                         *tmp = '\0';
1037                         break;
1038                 }
1039
1040 }
1041
1042 static void
1043 pine_parse_buf(char *buf)
1044 {
1045         list_item item;
1046         char *start = buf;
1047         char *end;
1048         char tmp[PINE_BUF_SIZE];
1049         int i, len, last;
1050         int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1051
1052         item = item_create();
1053
1054         for(i=0, last=0; !last ; i++) {
1055                 if( !(end = strchr(start, '\t')) )
1056                         last=1;
1057
1058                 len = last ? strlen(start) : (int) (end-start);
1059                 len = min(len, PINE_BUF_SIZE - 1);
1060
1061                 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1062                                 && pine_conv_table[i] >= 0) {
1063                         strncpy(tmp, start, len);
1064                         tmp[len] = 0;
1065                         if(*tmp)
1066                                 item_fput(item, pine_conv_table[i],
1067                                                 xstrdup(tmp));
1068                 }
1069                 start = end + 1;
1070         }
1071
1072         pine_convert_emails(item_fget(item, EMAIL));
1073         add_item2database(item);
1074         item_free(&item);
1075 }
1076
1077
1078 #define LINESIZE        1024
1079
1080 static int
1081 pine_parse_file(FILE *in)
1082 {
1083         char line[LINESIZE];
1084         char *buf = NULL;
1085         char *ptr;
1086         int i;
1087
1088         fgets(line, LINESIZE, in);
1089
1090         while(!feof(in)) {
1091                 for(i = 2;;i++) {
1092                         buf = xrealloc(buf, i*LINESIZE);
1093                         if(i == 2)
1094                                 strcpy(buf, line);
1095                         fgets(line, LINESIZE, in);
1096                         ptr=(char *)&line;
1097                         if(*ptr != ' ' || feof(in))
1098                                 break;
1099                         else
1100                                 while(*ptr == ' ')
1101                                         ptr++;
1102
1103                         strcat(buf, ptr);
1104                 }
1105                 if(*buf == '#') {
1106                         xfree(buf);
1107                         continue;
1108                 }
1109                 pine_fixbuf(buf);
1110
1111                 pine_parse_buf(buf);
1112
1113                 xfree(buf);
1114         }
1115
1116         return 0;
1117 }
1118
1119 /*
1120  * end of pine addressbook import filter
1121  */
1122
1123
1124 /*
1125  * pine addressbook export filter
1126  *
1127  *  filter doesn't wrap the lines as it should but Pine seems to handle
1128  *  created files without problems - JH
1129  */
1130
1131 static int
1132 pine_export_database(FILE *out, struct db_enumerator e)
1133 {
1134         char *emails;
1135
1136         db_enumerate_items(e) {
1137                 emails = db_email_get(e.item);
1138                 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1139                                 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1140                                 safe_str(db_fget(e.item, NICK)),
1141                                 safe_str(db_name_get(e.item)),
1142                                 emails,
1143                                 safe_str(db_fget(e.item, NOTES))
1144                                 );
1145                 free(emails);
1146         }
1147
1148         return 0;
1149 }
1150
1151 /*
1152  * end of pine addressbook export filter
1153  */
1154
1155
1156 /*
1157  * csv import filter
1158  */
1159
1160 /* FIXME
1161  * these files should be parsed according to a certain
1162  * lay out, or the default if layout is not given, at
1163  * the moment only default is done...
1164  */
1165
1166 #define CSV_COMMENT_CHAR        '#'
1167 #define CSV_DUPLICATE_SEPARATOR " "
1168 #define CSV_TABLE_SIZE(t)       (sizeof (t) / sizeof *(t))
1169
1170 static int csv_conv_table[] = {
1171         NAME,
1172         EMAIL,
1173         PHONE,
1174         NOTES,
1175         NICK
1176 };
1177
1178 static int allcsv_conv_table[] = {
1179         NAME,
1180         EMAIL,
1181         ADDRESS,
1182         ADDRESS2,
1183         CITY,
1184         STATE,
1185         ZIP,
1186         COUNTRY,
1187         PHONE,
1188         WORKPHONE,
1189         FAX,
1190         MOBILEPHONE,
1191         NICK,
1192         URL,
1193         NOTES,
1194         ANNIVERSARY
1195 };
1196
1197 static int palmcsv_conv_table[] = {
1198         NAME,           /* Last name */
1199         NAME,           /* First name */
1200         NOTES,          /* Title */
1201         NICK,           /* Company */
1202         WORKPHONE,
1203         PHONE,
1204         FAX,
1205         MOBILEPHONE,
1206         EMAIL,
1207         ADDRESS,
1208         CITY,
1209         STATE,
1210         ZIP,
1211         COUNTRY,
1212         ANNIVERSARY,
1213 };
1214
1215 static void
1216 csv_convert_emails(char *s)
1217 {
1218         int i;
1219         char *tmp;
1220
1221         if(s == NULL)
1222                 return;
1223
1224         for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1225                 if(i > MAX_LIST_ITEMS - 1) {
1226                         *tmp = 0;
1227                         break;
1228                 }
1229
1230 }
1231
1232 static char *
1233 csv_remove_quotes(char *s)
1234 {
1235         char *copy, *trimmed;
1236         int len;
1237
1238         copy = trimmed = xstrdup(s);
1239         strtrim(trimmed);
1240
1241         len = strlen(trimmed);
1242         if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1243                 if(len < 3) {
1244                         xfree(copy);
1245                         return NULL;
1246                 }
1247                 trimmed[len - 1] = 0;
1248                 trimmed++;
1249                 trimmed = xstrdup(trimmed);
1250                 free(copy);
1251                 return trimmed;
1252         }
1253
1254         xfree(copy);
1255         return xstrdup(s);
1256 }
1257
1258 static int
1259 csv_field_to_item(int *table_base, size_t table_size, int field)
1260 {
1261         if(field < table_size)
1262                 return field_id(table_base[field]);
1263
1264         return -1;
1265 }
1266
1267 static void
1268 csv_store_item(list_item item, int i, char *s)
1269 {
1270         char *newstr = NULL;
1271
1272         if(!s || !*s)
1273                 return;
1274
1275         if( !(newstr = csv_remove_quotes(s)) )
1276                 return;
1277
1278         if(i >= 0) {
1279                 if (item[i] != NULL) {
1280                         char *oldstr = item[i];
1281
1282                         item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1283                                 oldstr, NULL);
1284                         xfree(newstr);
1285                         xfree(oldstr);
1286                 } else {
1287                         item[i] = newstr;
1288                 }
1289         } else {
1290                 xfree(newstr);
1291         }
1292 }
1293
1294 static int
1295 csv_is_valid_quote_end(char *p)
1296 {
1297         if(*p != '\"')
1298                 return FALSE;
1299
1300         for(p++; *p; p++) {
1301                 if(*p == ',')
1302                         return TRUE;
1303                 else if(!ISSPACE(*p))
1304                         return FALSE;
1305         }
1306
1307         return TRUE;
1308 }
1309
1310 static int
1311 csv_is_valid_quote_start(char *p)
1312 {
1313         for(; *p; p++) {
1314                 if(*p == '\"')
1315                         return TRUE;
1316                 else if(!ISSPACE(*p))
1317                         return FALSE;
1318         }
1319
1320         return FALSE;
1321 }
1322
1323 static void
1324 csv_parse_line(char *line, int *table_base, size_t table_size)
1325 {
1326         char *p, *start;
1327         int field;
1328         bool in_quote = FALSE;
1329         list_item item;
1330
1331         item = item_create();
1332
1333         for(p = start = line, field = 0; *p; p++) {
1334                 if(in_quote) {
1335                         if(csv_is_valid_quote_end(p))
1336                                 in_quote = FALSE;
1337                 } else {
1338                         if ( (((p - start) / sizeof (char)) < 2 ) &&
1339                                 csv_is_valid_quote_start(p) )
1340                                 in_quote = TRUE;
1341                 }
1342
1343                 if(*p == ',' && !in_quote) {
1344                         *p = 0;
1345                         csv_store_item(item,
1346                                 csv_field_to_item(table_base,table_size,field),
1347                                 start);
1348                         field++;
1349                         start = p + 1;
1350                 }
1351         }
1352         /*
1353          * store last field
1354          */
1355         csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1356                 start);
1357
1358         csv_convert_emails(item_fget(item, EMAIL));
1359         add_item2database(item);
1360         item_free(&item);
1361 }
1362
1363 static int
1364 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1365 {
1366         char *line = NULL;
1367
1368         while(!feof(in)) {
1369                 line = getaline(in);
1370
1371                 if(line && *line && *line != CSV_COMMENT_CHAR)
1372                         csv_parse_line(line, conv_table, table_size);
1373
1374                 xfree(line);
1375         }
1376
1377         return 0;
1378 }
1379
1380 static int
1381 csv_parse_file(FILE *in)
1382 {
1383         return csv_parse_file_common(in, csv_conv_table,
1384                 CSV_TABLE_SIZE(csv_conv_table));
1385 }
1386
1387 static int
1388 allcsv_parse_file(FILE *in)
1389 {
1390         return csv_parse_file_common(in, allcsv_conv_table,
1391                 CSV_TABLE_SIZE(allcsv_conv_table));
1392 }
1393
1394 static int
1395 palmcsv_parse_file(FILE *in)
1396 {
1397         return csv_parse_file_common(in, palmcsv_conv_table,
1398                 CSV_TABLE_SIZE(palmcsv_conv_table));
1399 }
1400
1401 /*
1402  * end of csv import filter
1403  */
1404
1405 /*
1406  * vCard import filter
1407  */
1408
1409 static char *vcard_fields[] = {
1410         "FN",                   /* FORMATTED NAME */
1411         "EMAIL",                /* EMAIL */
1412         "ADR",                  /* ADDRESS */
1413         "ADR",                  /* ADDRESS2 - not used */
1414         "ADR",                  /* CITY */
1415         "ADR",                  /* STATE */
1416         "ADR",                  /* ZIP */
1417         "ADR",                  /* COUNTRY */
1418         "TEL",                  /* PHONE */
1419         "TEL",                  /* WORKPHONE */
1420         "TEL",                  /* FAX */
1421         "TEL",                  /* MOBILEPHONE */
1422         "NICKNAME",             /* NICK */
1423         "URL",                  /* URL */
1424         "NOTE",                 /* NOTES */
1425         "N",                    /* NAME: special case/mapping in vcard_parse_line() */
1426         NULL                    /* not implemented: ANNIVERSARY, ITEM_FIELDS */
1427 };
1428
1429 /*
1430  * mappings between vCard ADR field and abook's ADDRESS
1431  * see rfc2426 section 3.2.1
1432  */
1433 static int vcard_address_fields[] = {
1434         -1,                     /* vCard(post office box) - not used */
1435         -1,                     /* vCard(the extended address) - not used */
1436         2,                      /* vCard(the street address) - ADDRESS */
1437         4,                      /* vCard(the locality) - CITY */
1438         5,                      /* vCard(the region) - STATE */
1439         6,                      /* vCard(the postal code) - ZIP */
1440         7                       /* vCard(the country name) - COUNTRY */
1441 };
1442
1443 enum {
1444         VCARD_KEY = 0,
1445         VCARD_KEY_ATTRIBUTE,
1446         VCARD_VALUE,
1447 };
1448
1449 static char *
1450 vcard_get_line_element(char *line, int element)
1451 {
1452         int i;
1453         char *line_copy = 0;
1454         char *result = 0;
1455         char *key = 0;
1456         char *key_attr = 0;
1457         char *value = 0;
1458
1459         line_copy = xstrdup(line);
1460
1461         /* change newline characters, if present, to end of string */
1462         for(i=0; line_copy[i]; i++) {
1463                 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1464                         line_copy[i] = '\0';
1465                         break;
1466                 }
1467         }
1468
1469         /* separate key from value */
1470         for(i=0; line_copy[i]; i++) {
1471                 if(line_copy[i] == ':') {
1472                         line_copy[i] = '\0';
1473                         key = line_copy;
1474                         value = &line_copy[i+1];
1475                         break;
1476                 }
1477         }
1478
1479         /* separate key from key attributes */
1480         /* works for vCard 2 as well (automagically) */
1481         if (key) {
1482                 for(i=0; key[i]; i++) {
1483                         if(key[i] == ';') {
1484                                 key[i] = '\0';
1485                                 key_attr = &key[i+1];
1486                                 break;
1487                         }
1488                 }
1489         }
1490
1491         switch(element) {
1492         case VCARD_KEY:
1493                 if(key)
1494                         result = xstrdup(key);
1495                 break;
1496         case VCARD_KEY_ATTRIBUTE:
1497                 if(key_attr)
1498                         result = xstrdup(key_attr);
1499                 break;
1500         case VCARD_VALUE:
1501                 if(value)
1502                         result = xstrdup(value);
1503                 break;
1504         }
1505
1506         xfree(line_copy);
1507         return result;
1508 }
1509
1510 static void
1511 vcard_parse_email(list_item item, char *line)
1512 {
1513         char *email;
1514
1515         email = vcard_get_line_element(line, VCARD_VALUE);
1516
1517         if(item[1]) {
1518                 item[1] = strconcat(item[1], ",", email, 0);
1519                 xfree(email);
1520         }
1521         else {
1522                 item[1] = email;
1523         }
1524 }
1525
1526 static void
1527 vcard_parse_address(list_item item, char *line)
1528 {
1529         int i;
1530         int k;
1531         char *value;
1532         char *address_field;
1533
1534         value = vcard_get_line_element(line, VCARD_VALUE);
1535         if(!value)
1536                 return;
1537
1538         address_field = value;
1539         for(i=k=0; value[i]; i++) {
1540                 if(value[i] == ';') {
1541                         value[i] = '\0';
1542                         if(vcard_address_fields[k] >= 0) {
1543                                 item[vcard_address_fields[k]] = xstrdup(address_field);
1544                         }
1545                         address_field = &value[i+1];
1546                         k++;
1547                         if((k+1)==(sizeof(vcard_address_fields)/sizeof(*vcard_address_fields)))
1548                                 break;
1549                 }
1550         }
1551         item[vcard_address_fields[k]] = xstrdup(address_field);
1552         xfree(value);
1553 }
1554
1555 static void
1556 vcard_parse_name(list_item item, char *line)
1557 {
1558         // store the "N" field into "NAME" *if* no "FN:"
1559         // value has already been stored here
1560         if(item[0]) return;
1561
1562         int i = -1;
1563         item[0] = vcard_get_line_element(line, VCARD_VALUE);
1564         // "N:" can be multivalued => replace ';' separators by ' '
1565         while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1566
1567         // http://www.daniweb.com/software-development/c/code/216919
1568         char *original = item[0], *p = original;
1569         int trimmed = 0;
1570         do {
1571           if (*original != ' ' || trimmed) {
1572             trimmed = 1; *p++ = *original;
1573           }
1574         } while(*original++);
1575 }
1576
1577 static void
1578 vcard_parse_phone(list_item item, char *line)
1579 {
1580         char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1581         char *value = vcard_get_line_element(line, VCARD_VALUE);
1582
1583         /* set the standard number */
1584         if (!type) item_fput(item, PHONE, value);
1585
1586         /*
1587          * see rfc2426 section 3.3.1
1588          * Note: we probably support both vCard 2 and 3
1589          */
1590         else {
1591                 if (strcasestr(type, "home") != NULL)
1592                         item_fput(item, PHONE, xstrdup(value));
1593                 else if (strcasestr(type, "work") != NULL)
1594                         item_fput(item, WORKPHONE, xstrdup(value));
1595                 else if (strcasestr(type, "fax") != NULL)
1596                         item_fput(item, FAX, xstrdup(value));
1597                 else if (strcasestr(type, "cell") != NULL)
1598                         item_fput(item, MOBILEPHONE, xstrdup(value));
1599
1600                 xfree(type);
1601                 xfree(value);
1602         }
1603 }
1604
1605 static void
1606 vcard_parse_line(list_item item, char *line)
1607 {
1608         int i;
1609         char *key;
1610
1611         for(i=0; vcard_fields[i]; i++) {
1612                 key = vcard_fields[i];
1613
1614                 if(0 == strncmp(key, line, strlen(key))) {
1615                         if(0 == strcmp(key, "EMAIL"))
1616                                 vcard_parse_email(item, line);
1617                         else if(i == 2)
1618                                 vcard_parse_address(item, line);
1619                         else if(0 == strcmp(key, "TEL"))
1620                                 vcard_parse_phone(item, line);
1621                         else if(0 == strcmp(key, "N"))
1622                                 vcard_parse_name(item, line);
1623                         else
1624                                 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1625                         return;
1626                 }
1627         }
1628 }
1629
1630 static void
1631 vcard_parse_item(FILE *in)
1632 {
1633         char *line = NULL;
1634         list_item item = item_create();
1635
1636         while(!feof(in)) {
1637                 line = getaline(in);
1638
1639                 if(line && !strncmp("END:VCARD", line, 9)) {
1640                         xfree(line);
1641                         break;
1642                 }
1643                 else if(line) {
1644                         vcard_parse_line(item, line);
1645                         xfree(line);
1646                 }
1647         }
1648
1649         add_item2database(item);
1650         item_free(&item);
1651 }
1652
1653 static int
1654 vcard_parse_file(FILE *in)
1655 {
1656         char *line = NULL;
1657
1658         while(!feof(in)) {
1659                 line = getaline(in);
1660
1661                 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1662                         xfree(line);
1663                         vcard_parse_item(in);
1664                 }
1665                 else if(line) {
1666                         xfree(line);
1667                 }
1668         }
1669
1670         return 0;
1671 }
1672
1673 /*
1674  * end of vCard import filter
1675  */
1676
1677 /*
1678  * csv addressbook export filters
1679  */
1680
1681 #define CSV_LAST                (-1)
1682 #define CSV_UNDEFINED           (-2)
1683 #define CSV_SPECIAL(X)          (-3 - (X))
1684 #define CSV_IS_SPECIAL(X)       ((X) <= -3)
1685
1686 static int
1687 csv_export_common(FILE *out, struct db_enumerator e,
1688                 int fields[], void (*special_func)(FILE *, int, int))
1689 {
1690         int i;
1691
1692         db_enumerate_items(e) {
1693                 for(i = 0; fields[i] != CSV_LAST; i++) {
1694                         if(fields[i] == CSV_UNDEFINED)
1695                                 fprintf(out, "\"\"");
1696                         else if(CSV_IS_SPECIAL(fields[i])) {
1697                                 if(special_func)
1698                                         (*special_func)(out, e.item, fields[i]);
1699                         } else
1700                                 /*fprintf(out,(
1701                         strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1702                         strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1703                                 "\"%s\"" : "%s",
1704                                 safe_str(database[e.item][field_idx(fields[i])])
1705                                 );*/
1706                                 fprintf(out, "\"%s\"",
1707                                         safe_str(db_fget(e.item,fields[i])));
1708
1709                         if(fields[i + 1] != CSV_LAST)
1710                                 fputc(',', out);
1711                 }
1712                 fputc('\n', out);
1713         }
1714
1715         return 0;
1716 }
1717
1718 static int
1719 csv_export_database(FILE *out, struct db_enumerator e)
1720 {
1721         int csv_export_fields[] = {
1722                 NAME,
1723                 EMAIL,
1724                 PHONE,
1725                 NOTES,
1726                 NICK,
1727                 CSV_LAST
1728         };
1729
1730         csv_export_common(out, e, csv_export_fields, NULL);
1731
1732         return 0;
1733 }
1734
1735 static int
1736 allcsv_export_database(FILE *out, struct db_enumerator e)
1737 {
1738         /*
1739          * TODO: Should get these atomatically from abook_fileds
1740          *  - JH
1741          */
1742         int allcsv_export_fields[] = {
1743                 NAME,
1744                 EMAIL,
1745                 ADDRESS,
1746                 ADDRESS2,
1747                 CITY,
1748                 STATE,
1749                 ZIP,
1750                 COUNTRY,
1751                 PHONE,
1752                 WORKPHONE,
1753                 FAX,
1754                 MOBILEPHONE,
1755                 NICK,
1756                 URL,
1757                 NOTES,
1758                 ANNIVERSARY,
1759                 GROUPS,
1760                 CSV_LAST
1761         };
1762
1763         fprintf(out, "#");
1764         fprintf(out, "\"NAME\",");
1765         fprintf(out, "\"EMAIL\",");
1766         fprintf(out, "\"ADDRESS\",");
1767         fprintf(out, "\"ADDRESS2\",");
1768         fprintf(out, "\"CITY\",");
1769         fprintf(out, "\"STATE\",");
1770         fprintf(out, "\"ZIP\",");
1771         fprintf(out, "\"COUNTRY\",");
1772         fprintf(out, "\"PHONE\",");
1773         fprintf(out, "\"WORKPHONE\",");
1774         fprintf(out, "\"FAX\",");
1775         fprintf(out, "\"MOBILEPHONE\",");
1776         fprintf(out, "\"NICK\",");
1777         fprintf(out, "\"URL\",");
1778         fprintf(out, "\"NOTES\",");
1779         fprintf(out, "\"ANNIVERSARY\",");
1780         fprintf(out, "\"GROUPS\"\n");
1781
1782         csv_export_common(out, e, allcsv_export_fields, NULL);
1783
1784         return 0;
1785 }
1786
1787 /*
1788  * palm csv
1789  */
1790
1791 #define PALM_CSV_NAME   CSV_SPECIAL(0)
1792 #define PALM_CSV_END    CSV_SPECIAL(1)
1793 #define PALM_CSV_CAT    CSV_SPECIAL(2)
1794
1795 static void
1796 palm_split_and_write_name(FILE *out, char *name)
1797 {
1798         char *p;
1799
1800         assert(name);
1801
1802         if ( (p = strchr(name, ' ')) ) {
1803                 /*
1804                  * last name first
1805                  */
1806                 fprintf(out, "\"%s\",\"" , p + 1);
1807                 fwrite((void *)name, p - name, sizeof(char), out);
1808                 fputc('\"', out);
1809         } else {
1810                 fprintf(out, "\"%s\"", safe_str(name));
1811         }
1812 }
1813
1814 static void
1815 palm_csv_handle_specials(FILE *out, int item, int field)
1816 {
1817         switch(field) {
1818                 case PALM_CSV_NAME:
1819                         palm_split_and_write_name(out, db_name_get(item));
1820                         break;
1821                 case PALM_CSV_CAT:
1822                         fprintf(out, "\"abook\"");
1823                         break;
1824                 case PALM_CSV_END:
1825                         fprintf(out, "\"0\"");
1826                         break;
1827                 default:
1828                         assert(0);
1829         }
1830 }
1831
1832 static int
1833 palm_export_database(FILE *out, struct db_enumerator e)
1834 {
1835         int palm_export_fields[] = {
1836                 PALM_CSV_NAME,          /* LASTNAME, FIRSTNAME  */
1837                 CSV_UNDEFINED,          /* TITLE                */
1838                 CSV_UNDEFINED,          /* COMPANY              */
1839                 WORKPHONE,              /* WORK PHONE           */
1840                 PHONE,                  /* HOME PHONE           */
1841                 FAX,                    /* FAX                  */
1842                 MOBILEPHONE,            /* OTHER                */
1843                 EMAIL,                  /* EMAIL                */
1844                 ADDRESS,                /* ADDRESS              */
1845                 CITY,                   /* CITY                 */
1846                 STATE,                  /* STATE                */
1847                 ZIP,                    /* ZIP                  */
1848                 COUNTRY,                /* COUNTRY              */
1849                 NICK,                   /* DEFINED 1            */
1850                 URL,                    /* DEFINED 2            */
1851                 CSV_UNDEFINED,          /* DEFINED 3            */
1852                 CSV_UNDEFINED,          /* DEFINED 4            */
1853                 NOTES,                  /* NOTE                 */
1854                 PALM_CSV_END,           /* "0"                  */
1855                 PALM_CSV_CAT,           /* CATEGORY             */
1856                 CSV_LAST
1857         };
1858
1859         csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
1860
1861         return 0;
1862 }
1863
1864 /*
1865  * end of csv export filters
1866  */
1867
1868 /*
1869  * vCard 2 addressbook export filter
1870  */
1871
1872 static int
1873 vcard_export_database(FILE *out, struct db_enumerator e)
1874 {
1875   db_enumerate_items(e)
1876     vcard_export_item(out, e.item);
1877   return 0;
1878 }
1879
1880 void
1881 vcard_export_item(FILE *out, int item)
1882 {
1883         int j;
1884         char *name, *tmp;
1885         abook_list *emails, *em;
1886         fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
1887                 safe_str(db_name_get(item)));
1888
1889         name = get_surname(db_name_get(item));
1890         for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
1891           if((db_name_get(item))[j] == ' ')
1892             break;
1893         }
1894         fprintf(out, "N:%s;%.*s\r\n",
1895                 safe_str(name),
1896                 j,
1897                 safe_str(db_name_get(item))
1898                 );
1899
1900         free(name);
1901
1902         if(db_fget(item, ADDRESS))
1903           fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n",
1904                   safe_str(db_fget(item, ADDRESS)),
1905                   safe_str(db_fget(item, ADDRESS2)),
1906                   safe_str(db_fget(item, CITY)),
1907                   safe_str(db_fget(item, STATE)),
1908                   safe_str(db_fget(item, ZIP)),
1909                   safe_str(db_fget(item, COUNTRY))
1910                   );
1911
1912         if(db_fget(item, PHONE))
1913           fprintf(out, "TEL;HOME:%s\r\n",
1914                   db_fget(item, PHONE));
1915         if(db_fget(item, WORKPHONE))
1916           fprintf(out, "TEL;WORK:%s\r\n",
1917                   db_fget(item, WORKPHONE));
1918         if(db_fget(item, FAX))
1919           fprintf(out, "TEL;FAX:%s\r\n",
1920                   db_fget(item, FAX));
1921         if(db_fget(item, MOBILEPHONE))
1922           fprintf(out, "TEL;CELL:%s\r\n",
1923                   db_fget(item, MOBILEPHONE));
1924
1925         tmp = db_email_get(item);
1926         if(*tmp) {
1927           emails = csv_to_abook_list(tmp);
1928
1929           for(em = emails; em; em = em->next)
1930             fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data);
1931
1932           abook_list_free(&emails);
1933         }
1934         free(tmp);
1935
1936         if(db_fget(item, NOTES))
1937           fprintf(out, "NOTE:%s\r\n",
1938                   db_fget(item, NOTES));
1939         if(db_fget(item, URL))
1940           fprintf(out, "URL:%s\r\n",
1941                   db_fget(item, URL));
1942
1943         fprintf(out, "END:VCARD\r\n\r\n");
1944
1945 }
1946
1947 /*
1948  * end of vCard export filter
1949  */
1950
1951
1952 /*
1953  * mutt alias export filter
1954  */
1955
1956 static char *
1957 mutt_alias_genalias(int i)
1958 {
1959         char *tmp, *pos;
1960
1961         if(db_fget(i, NICK))
1962                 return xstrdup(db_fget(i, NICK));
1963
1964         tmp = xstrdup(db_name_get(i));
1965
1966         if( ( pos = strchr(tmp, ' ') ) )
1967                 *pos = 0;
1968
1969         strlower(tmp);
1970
1971         return tmp;
1972 }
1973
1974 /*
1975  * This function is a variant of abook_list_to_csv
1976  * */
1977 static char *
1978 mutt_alias_gengroups(int i)
1979 {
1980         char *groups, *res = NULL;
1981         char groupstr[7] = "-group ";
1982         abook_list *list, *tmp;
1983
1984         groups = db_fget(i, GROUPS);
1985
1986         if(!groups)
1987                 return NULL;
1988
1989         list = csv_to_abook_list(groups);
1990         for(tmp = list; tmp; tmp = tmp->next) {
1991                 if(tmp == list) {
1992                         res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
1993                         res = strcpy(res, groupstr);
1994                 } else {
1995                         res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
1996                         strcat(res, " ");
1997                         strcat(res, groupstr);
1998                 }
1999                 strcat(res, tmp->data);
2000         }
2001         abook_list_free(&list);
2002         xfree(groups);
2003
2004         return res;
2005 }
2006
2007 static int
2008 mutt_alias_export(FILE *out, struct db_enumerator e)
2009 {
2010         char email[MAX_EMAIL_LEN];
2011         char *alias = NULL;
2012         char *groups = NULL;
2013         int email_addresses;
2014         char *ptr;
2015
2016         db_enumerate_items(e) {
2017                 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2018                 groups = (field_id(GROUPS) != -1) ?  mutt_alias_gengroups(e.item) : NULL;
2019                 get_first_email(email, e.item);
2020
2021                 /* do not output contacts without email address */
2022                 /* cause this does not make sense in mutt aliases */
2023                 if (*email) {
2024
2025                         /* output first email address */
2026                         fprintf(out,"alias ");
2027                         if(groups)
2028                                 fprintf(out, "%s ", groups);
2029                         if(alias)
2030                                 fprintf(out, "%s ", alias);
2031                         fprintf(out, "%s <%s>\n",
2032                                         db_name_get(e.item),
2033                                         email);
2034
2035                         /* number of email addresses */
2036                         email_addresses = 1;
2037                         ptr = db_email_get(e.item);
2038                         while (*ptr != '\0') {
2039                                 if (*ptr == ',') {
2040                                         email_addresses++;
2041                                 }
2042                                 ptr++;
2043                         }
2044
2045                         /* output other email addresses */
2046                         while (email_addresses-- > 1) {
2047                                 roll_emails(e.item, ROTATE_RIGHT);
2048                                 get_first_email(email, e.item);
2049                                 fprintf(out,"alias ");
2050                                 if( groups )
2051                                         fprintf(out, "%s ", groups);
2052                                 if(alias)
2053                                         fprintf(out, "%s__%s ", alias, email);
2054                                 else
2055                                         fprintf(out, "%s__%s ", db_name_get(e.item), email);
2056                                 fprintf(out, "%s <%s>\n",
2057                                                 db_name_get(e.item),
2058                                                 email);
2059                         }
2060                         roll_emails(e.item, ROTATE_RIGHT);
2061                         xfree(alias);
2062                         xfree(groups);
2063                 }
2064         }
2065
2066         return 0;
2067 }
2068
2069 void muttq_print_item(FILE *file, int item)
2070 {
2071         abook_list *emails, *e;
2072         char *tmp = db_email_get(item);
2073
2074         emails = csv_to_abook_list(tmp);
2075         free(tmp);
2076
2077         for(e = emails; e; e = e->next) {
2078                 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2079                                 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2080                                 );
2081                 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2082                         break;
2083         }
2084         abook_list_free(&emails);
2085 }
2086
2087 static int
2088 mutt_query_export_database(FILE *out, struct db_enumerator e)
2089 {
2090   fprintf(out, "All items\n");
2091   db_enumerate_items(e)
2092     muttq_print_item(out, e.item);
2093   return 0;
2094 }
2095
2096 /*
2097  * end of mutt alias export filter
2098  */
2099
2100
2101 /*
2102  * printable export filter
2103  */
2104
2105
2106 static void
2107 text_write_address_us(FILE *out, int i) {
2108         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2109
2110         if(db_fget(i, ADDRESS2))
2111                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2112
2113         if(db_fget(i, CITY))
2114                 fprintf(out, "\n%s", db_fget(i, CITY));
2115
2116         if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2117                 fputc('\n', out);
2118
2119                 if(db_fget(i, STATE)) {
2120                         fprintf(out, "%s", db_fget(i, STATE));
2121                         if(db_fget(i, ZIP))
2122                                 fputc(' ', out);
2123                 }
2124
2125                 if(db_fget(i, ZIP))
2126                         fprintf(out, "%s", db_fget(i, ZIP));
2127         }
2128
2129         if(db_fget(i, COUNTRY))
2130                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2131 }
2132
2133
2134 static void
2135 text_write_address_uk(FILE *out, int i) {
2136         int j;
2137
2138         for(j = ADDRESS; j <= COUNTRY; j++)
2139                 if(db_fget(i, j))
2140                         fprintf(out, "\n%s", db_fget(i, j));
2141 }
2142
2143 static void
2144 text_write_address_eu(FILE *out, int i) {
2145         fprintf(out, "\n%s", db_fget(i, ADDRESS));
2146
2147         if(db_fget(i, ADDRESS2))
2148                 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2149
2150         if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2151                 fputc('\n', out);
2152
2153                 if(db_fget(i, ZIP)) {
2154                         fprintf(out, "%s", db_fget(i, ZIP));
2155                         if(db_fget(i, CITY))
2156                                 fputc(' ', out);
2157                 }
2158
2159                 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2160         }
2161
2162         if(db_fget(i, STATE))
2163                 fprintf(out, "\n%s", db_fget(i, STATE));
2164
2165         if(db_fget(i, COUNTRY))
2166                 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2167 }
2168
2169 static int
2170 text_export_database(FILE * out, struct db_enumerator e)
2171 {
2172         abook_list *emails, *em;
2173         int j;
2174         char *realname = get_real_name(), *str = NULL, *tmp;
2175         char *style = opt_get_str(STR_ADDRESS_STYLE);
2176
2177         fprintf(out,
2178                 "-----------------------------------------\n%s's address book\n"
2179                 "-----------------------------------------\n\n\n",
2180                 realname);
2181         free(realname);
2182
2183         db_enumerate_items(e) {
2184                 fprintf(out,
2185                         "-----------------------------------------\n\n");
2186                 fprintf(out, "%s", db_name_get(e.item));
2187                 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2188                         fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2189                 fprintf(out, "\n");
2190
2191                 tmp = db_email_get(e.item);
2192                 if(*tmp) {
2193                         emails = csv_to_abook_list(tmp);
2194
2195                         fprintf(out, "\n");
2196                         for(em = emails; em; em = em->next)
2197                                 fprintf(out, "%s\n", em->data);
2198
2199                         abook_list_free(&emails);
2200                 }
2201                 free(tmp);
2202                 /* Print address */
2203                 if(db_fget(e.item, ADDRESS)) {
2204                         if(!safe_strcmp(style, "us"))   /* US like */
2205                                 text_write_address_us(out, e.item);
2206                         else if(!safe_strcmp(style, "uk"))      /* UK like */
2207                                 text_write_address_uk(out, e.item);
2208                         else    /* EU like */
2209                                 text_write_address_eu(out, e.item);
2210
2211                         fprintf(out, "\n");
2212                 }
2213
2214                 if((db_fget(e.item, PHONE)) ||
2215                         (db_fget(e.item, WORKPHONE)) ||
2216                         (db_fget(e.item, FAX)) ||
2217                         (db_fget(e.item, MOBILEPHONE))) {
2218                         fprintf(out, "\n");
2219                         for(j = PHONE; j <= MOBILEPHONE; j++)
2220                                 if(db_fget(e.item, j)) {
2221                                         get_field_info(field_id(j),
2222                                                         NULL, &str, NULL);
2223                                         fprintf(out, "%s: %s\n", str,
2224                                                 db_fget(e.item, j));
2225                                 }
2226                 }
2227
2228                 if(db_fget(e.item, URL))
2229                         fprintf(out, "\n%s\n", db_fget(e.item, URL));
2230                 if(db_fget(e.item, NOTES))
2231                         fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2232
2233                 fprintf(out, "\n");
2234         }
2235
2236         fprintf(out, "-----------------------------------------\n");
2237
2238         return 0;
2239 }
2240
2241 /*
2242  * end of printable export filter
2243  */
2244
2245 /*
2246  * elm alias export filter
2247  */
2248
2249 static int
2250 elm_alias_export(FILE *out, struct db_enumerator e)
2251 {
2252         char email[MAX_EMAIL_LEN];
2253         char *alias = NULL;
2254
2255         db_enumerate_items(e) {
2256                 alias = mutt_alias_genalias(e.item);
2257                 get_first_email(email, e.item);
2258                 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2259                 xfree(alias);
2260         }
2261
2262         return 0;
2263 }
2264
2265 /*
2266  * end of elm alias export filter
2267  */
2268
2269
2270 /*
2271  * Spruce export filter
2272  */
2273
2274 static int
2275 spruce_export_database (FILE *out, struct db_enumerator e)
2276 {
2277         char email[MAX_EMAIL_LEN];
2278
2279         fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2280
2281         db_enumerate_items(e) {
2282                 get_first_email(email, e.item);
2283                 if(strcmp(email, "")) {
2284                         fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2285                                         e.item,
2286                                         db_name_get(e.item),
2287                                         email,
2288                                         safe_str(db_fget(e.item, NOTES))
2289                                         );
2290                 }
2291         }
2292
2293         fprintf (out, "# End of address book file.\n");
2294
2295         return 0;
2296 }
2297
2298 /*
2299  * end of Spruce export filter
2300  */
2301
2302 /*
2303  * wanderlust addressbook export filter
2304  */
2305
2306 static int
2307 wl_export_database(FILE *out, struct db_enumerator e)
2308 {
2309         char email[MAX_EMAIL_LEN];
2310
2311         fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2312         db_enumerate_items(e) {
2313                 get_first_email(email, e.item);
2314                 if(*email) {
2315                         fprintf(out,
2316                                 "%s\t\"%s\"\t\"%s\"\n",
2317                                 email,
2318                                 safe_str(db_fget(e.item, NICK)),
2319                                 safe_str(db_name_get(e.item))
2320                         );
2321                 }
2322         }
2323
2324         fprintf (out, "\n# End of address book file.\n");
2325
2326         return 0;
2327 }
2328
2329 /*
2330  * end of wanderlust addressbook export filter
2331  */
2332
2333 /*
2334  * BSD calendar export filter
2335  */
2336
2337 static int
2338 bsdcal_export_database(FILE *out, struct db_enumerator e)
2339 {
2340         db_enumerate_items(e) {
2341                 int year, month = 0, day = 0;
2342                 char *anniversary = db_fget(e.item, ANNIVERSARY);
2343
2344                 if(anniversary) {
2345                         if(!parse_date_string(anniversary, &day, &month, &year))
2346                                 continue;
2347
2348                         fprintf(out,
2349                                 _("%02d/%02d\tAnniversary of %s\n"),
2350                                 month,
2351                                 day,
2352                                 safe_str(db_name_get(e.item))
2353                         );
2354                 }
2355         }
2356
2357         return 0;
2358 }
2359
2360 // see enum field_types @database.h
2361 static char *conv_table[] = {
2362   "name",
2363   "email",
2364   "address",
2365   "address2",
2366   "city",
2367   "state",
2368   "zip",
2369   "country",
2370   "phone",
2371   "workphone",
2372   "fax",
2373   "mobile",
2374   "nick",
2375   "url",
2376   "notes",
2377   "anniversary",
2378   0 /* ITEM_FIELDS */
2379 };
2380
2381 static int
2382 find_field_enum(char *s) {
2383   int i = 0;
2384   while (conv_table[i]) {
2385     if(!safe_strcmp(conv_table[i], s))
2386       return i;
2387     i++;
2388   }
2389   // failed
2390   return -1;
2391 }
2392
2393 /* Convert a string with named placeholders to
2394    a *printf() compatible string.
2395    Stores the abook field values into ft. */
2396 void
2397 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2398 {
2399         if(! fmt_string || ! ft) {
2400           fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2401           exit(EXIT_FAILURE);
2402         }
2403
2404         char *p, *start, *field_name = NULL;
2405         p = start = s;
2406
2407         while(*p) {
2408                 if(*p == '{') {
2409                   start = ++p;
2410                   p = strchr(start, '}');
2411                   if(! *p) {
2412                     fprintf(stderr, _("parse_custom_format: invalid format\n"));
2413                     exit(EXIT_FAILURE);
2414                   }
2415                   strcat(fmt_string, "%s");
2416                   field_name = strndup(start, (size_t)(p-start));
2417                   *ft = find_field_enum(field_name);
2418                   if(*ft == -1) {
2419                     fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2420                     exit(EXIT_FAILURE);
2421                   }
2422
2423                   ft++;
2424                   p++;
2425                   start = p;
2426                 } else {
2427                   p = strchr(start, '{');
2428                   if(p && *p) {
2429                     strncat(fmt_string, start, (size_t)(p-start));
2430                     start = p;
2431                   }
2432                   else {
2433                     strncat( fmt_string,
2434                              start,
2435                              FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2436                     break;
2437                   }
2438                 }
2439         }
2440         *ft = 66;
2441 }
2442
2443 static int
2444 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2445
2446
2447 // used to store the format string from --outformatstr when "custom" format is used
2448 // default value overriden in export_file()
2449 extern char *parsed_custom_format;
2450 extern enum field_types *custom_format_fields;
2451
2452 /* wrapper for custom_export_item:
2453    1) avoid messing with extern pointer
2454    2) adds \n
2455    3) follow the prototype needed for an abook_output_item_filter entry */
2456 void
2457 custom_print_item(FILE *out, int item)
2458 {
2459
2460   if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2461     fprintf(out, "\n");
2462 }
2463
2464 static int
2465 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2466 {
2467   char *p, *q = 0;
2468
2469   // if the first character is '!':
2470   // we first check that all fields exist before continuing
2471   if(*fmt == '!') {
2472     enum field_types *ftp = ft;
2473     while(*ft != 66) {
2474       if(! db_fget(item, *ft) )
2475         return 1;
2476       ft++;
2477     }
2478     ft = ftp;
2479     fmt++;
2480   }
2481
2482   while (*fmt) {
2483     if(!strncmp(fmt, "%s", 2)) {
2484       fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2485       ft++;
2486       fmt+=2;
2487     } else if (*ft == 66) {
2488       fprintf(out, "%s", fmt);
2489       return 0;
2490     } else {
2491       p = strchr(fmt, '%');
2492       if(*p) {
2493         q = strndup(fmt, (size_t)(p-fmt));
2494         fprintf(out, "%s", q);
2495         free(q);
2496         fmt = p;
2497       }
2498       else {
2499         fprintf(out, "%s", fmt);
2500         return 0;
2501       }
2502     }
2503   }
2504
2505   return 0;
2506 }
2507
2508 // used to store the format string from --outformatstr when "custom" format is used
2509 // default value overriden from abook.c
2510 extern char custom_format[FORMAT_STRING_LEN];
2511
2512 static int
2513 custom_export_database(FILE *out, struct db_enumerator e)
2514 {
2515         char *format_string =
2516           (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2517
2518         enum field_types *ft =
2519           (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2520
2521         parse_custom_format(custom_format, format_string, ft);
2522
2523         db_enumerate_items(e) {
2524           if(custom_export_item(out, e.item, format_string, ft) == 0)
2525             fprintf(out, "\n");
2526         }
2527         return 0;
2528 }
2529
2530 /*
2531  * end of BSD calendar export filter
2532  */
2533