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