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