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