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