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