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