5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
18 #include <sys/types.h>
19 #include "abook_curses.h"
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[];
42 * function declarations
46 * import filter prototypes
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);
58 * export filter prototypes
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);
78 * export filter item prototypes
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);
86 * end of function declarations
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 },
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 },
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 },
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));
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));
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));
160 number_of_output_filters()
164 for(i=0; *e_filters[i].filtname ; i++)
171 number_of_input_filters()
175 for(i=0; *i_filters[i].filtname ; i++)
184 char *username = getenv("USER");
185 struct passwd *pwent;
189 pwent = getpwnam(username);
191 if((tmp = xstrdup(pwent->pw_gecos)) == NULL)
192 return xstrdup(username);
194 rtn = sscanf(pwent->pw_gecos, "%[^,]", tmp);
195 if (rtn == EOF || rtn == 0) {
197 return xstrdup(username);
206 static int i_read_file(char *filename, int (*func) (FILE *in));
215 refresh_statusline();
216 headerline(_("import database"));
218 mvaddstr(3, 1, _("please select a filter"));
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));
226 mvprintw(6 + i, 6, _("x -\tcancel"));
234 int tmp = db_n_items();
238 filter = getch() - 'a';
239 if(filter == 'x' - 'a' ||
240 filter >= number_of_input_filters() || filter < 0) {
245 mvaddstr(5+filter, 2, "->");
247 filename = ask_filename(_("Filename: "));
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"));
267 i_read_file(char *filename, int (*func) (FILE *in))
272 if( (in = abook_fopen( filename, "r" )) == NULL )
283 import_file(char filtname[FILTNAME_LEN], char *filename)
286 int tmp = db_n_items();
290 if(! strncasecmp(i_filters[i].filtname, filtname,
293 if(! *i_filters[i].filtname) {
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");
309 ret = vcard_parse_file_libvformat(filename);
314 if(!strcmp(filename, "-")) {
316 if((fstat(fileno(stdin), &s)) == -1 || S_ISDIR(s.st_mode))
319 ret = (*i_filters[i].func) (stdin);
321 ret = i_read_file(filename, i_filters[i].func);
323 if(tmp == db_n_items())
333 static int e_write_file(char *filename,
334 int (*func) (FILE *in, struct db_enumerator e), int mode);
344 refresh_statusline();
345 headerline(_("export database"));
347 mvaddstr(3, 1, _("please select a filter"));
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));
355 mvprintw(6 + i, 6, _("x -\tcancel"));
362 int enum_mode = ENUM_ALL;
367 filter = getch() - 'a';
368 if(filter == 'x' - 'a' ||
369 filter >= number_of_output_filters() || filter < 0) {
374 mvaddstr(5 + filter, 2, "->");
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)) {
383 enum_mode = ENUM_SELECTED;
393 filename = ask_filename(_("Filename: "));
399 if( e_write_file(filename, e_filters[filter].func, enum_mode))
400 statusline_msg(_("Error occured while exporting"));
408 struct abook_output_item_filter select_output_item_filter(char filtname[FILTNAME_LEN]) {
411 if(!strncasecmp(u_filters[i].filtname, filtname, FILTNAME_LEN))
413 if(!*u_filters[i].filtname) {
422 e_write_item(FILE *out, int item, void (*func) (FILE *in, int item))
428 e_write_file(char *filename, int (*func) (FILE *in, struct db_enumerator e),
433 struct db_enumerator enumerator = init_db_enumerator(mode);
435 if((out = fopen(filename, "a")) == NULL)
441 ret = (*func) (out, enumerator);
449 fexport(char filtname[FILTNAME_LEN], FILE *handle, int enum_mode)
452 struct db_enumerator e = init_db_enumerator(enum_mode);
455 if(!strncasecmp(e_filters[i].filtname, filtname,
458 if(!*e_filters[i].filtname) {
464 return (e_filters[i].func) (handle, e);
470 export_file(char filtname[FILTNAME_LEN], char *filename)
472 const int mode = ENUM_ALL;
475 struct db_enumerator e = init_db_enumerator(mode);
478 if(!strncasecmp(e_filters[i].filtname, filtname,
481 if(!*e_filters[i].filtname) {
490 if(!strcmp(filename, "-"))
491 ret = (e_filters[i].func) (stdout, e);
493 ret = e_write_file(filename, e_filters[i].func, mode);
499 * end of common functions
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[]. */
516 LDIF_OBJECTCLASS = ITEM_FIELDS + 1
519 #define LDIF_ITEM_FIELDS LDIF_OBJECTCLASS
521 typedef char *ldif_item[LDIF_ITEM_FIELDS];
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 = {
530 "streetaddress", // ADDRESS
531 "streetaddress2", // ADDRESS2
535 "countryname", // COUNTRY
536 "homephone", // PHONE
537 "telephonenumber", // WORKPHONE
538 "facsimiletelephonenumber", // FAX
539 "cellphone", // MOBILEPHONE
540 "xmozillanickname", // NICK
542 "description", // NOTES
543 "anniversary", // ANNIVERSARY
547 /* Used to map LDIF attr names from input to
548 the abook restricted set of standard fields. */
552 } ldif_available_items;
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 */
562 {"streetaddress", ADDRESS},
563 {"streetaddress2", ADDRESS2},
567 {"countryname", COUNTRY},
568 {"homephone", PHONE},
569 {"telephonenumber", WORKPHONE}, // workphone, according to Mozilla
570 {"facsimiletelephonenumber", FAX},
571 {"cellphone", MOBILEPHONE},
572 {"mozillanickname", NICK},
574 {"description", NOTES},
575 {"anniversary", ANNIVERSARY},
576 {"ou", GROUPS}, // 16
578 // here comes a couple of aliases
579 {"mozillasecondemail", EMAIL},
583 {"xmozillaanyphone", WORKPHONE}, // ever used ?
584 {"workphone", WORKPHONE},
586 {"telexnumber", FAX},
587 {"mobilephone", MOBILEPHONE},
588 {"mobile", MOBILEPHONE},
589 {"xmozillanickname", NICK},
592 {"birthday", ANNIVERSARY},
593 {"category", GROUPS},
596 "sn": append to lastname ?
597 "surname": append to lastname ?
598 "givenname": prepend to firstname ? */
600 /* here starts dummy fields:
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. */
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 */
612 // used to check valid LDIF records:
613 {"objectclass", LDIF_OBJECTCLASS}
615 const int LDIF_IMPORTABLE_ITEM_FIELDS = (int)sizeof(ldif_conv_table)/sizeof(*ldif_conv_table);
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
626 ldif_read_line(FILE *in, char **next_line)
632 // buf filled with the first line
636 buf = xstrdup(*next_line);
641 // if no line already read-ahead.
645 // this is not a continuation of what is already in buf
646 // store it for the next round
652 // starts with ' ': this is the continuation of buf
658 buf = strconcat(buf, ptr, NULL);
663 if(buf && *buf == '#' ) {
672 ldif_add_item(ldif_item li)
677 /* if there's no value for "objectclass"
678 it's probably a buggy record */
679 if(!li[LDIF_OBJECTCLASS])
682 /* just copy from our extended ldif_item to a regular
684 TODO: API could be changed so db_fput_byid() is usable */
685 item = item_create();
686 for(i=0; i < ITEM_FIELDS; i++) {
688 item[i] = xstrdup(li[i]);
691 add_item2database(item);
695 for(i=0; i < LDIF_ITEM_FIELDS; i++)
700 ldif_convert(ldif_item item, char *type, char *value)
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")) {
713 for( i=0; i < LDIF_IMPORTABLE_ITEM_FIELDS; i++ ) {
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
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));
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". */
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)) {
740 strconcat(item_fget(item, EMAIL), ",", value, 0));
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));
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) */
755 item[index] = xstrdup(value);
758 // matching attr found and field filled:
759 // no further attr search is needed for `type`
766 ldif_parse_file(FILE *handle)
769 char *next_line = NULL;
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() */
779 memset(item, 0, sizeof(item));
782 line = ldif_read_line(handle, &next_line);
784 // EOF or empty lines: continue;
785 if(!line || *line == '\0') continue;
787 if(-1 == (str_parse_line(line, &type, &value, &vlen))) {
789 continue; /* just skip the errors */
792 ldif_convert(item, type, value);
795 } while ( !feof(handle) );
797 // force registration (= ldif_add_item()) of the last LDIF entry
798 ldif_convert(item, "dn", "");
808 * mutt alias import filter
814 mutt_read_line(FILE *in, char **groups, char **alias, char **rest)
818 abook_list *glist = NULL;
820 if( !(line = ptr = getaline(in)) )
821 return 1; /* error / EOF */
825 if(strncmp("alias", ptr, 5)) {
833 /* If the group option is used, save the groups */
837 for(n_groups = 0; 0 == strncmp("-group", ptr, 6); n_groups++) {
843 abook_list_append(&glist,xstrndup(start, end - start));
847 if(n_groups && groups)
848 *groups = abook_list_to_csv(glist);
850 abook_list_free(&glist);
858 *alias = xstrndup(start, end - start);
861 *rest = xstrdup(ptr);
868 mutt_fix_quoting(char *p)
888 mutt_parse_email(list_item item)
890 char *line = item_fget(item, NAME);
898 mutt_fix_quoting(line);
899 tmp = strconcat("From: ", line, NULL);
900 getname(tmp, &name, &email);
904 item_fput(item, NAME, name);
909 item_fput(item, EMAIL, email);
914 * this is completely broken
917 while( (start = strchr(start, ',')) && i++ < MAX_EMAILS - 1) {
918 tmp = strconcat("From: ", ++start, NULL);
919 getname(tmp, &name, &email);
924 tmp = strconcat(item[EMAIL], ",", email, NULL);
936 mutt_parse_file(FILE *in)
938 list_item item = item_create();
941 memset(item, 0, fields_count * sizeof(char *));
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);
954 add_item2database(item);
962 * end of mutt alias import filter
971 ldif_fput_type_and_value(FILE *out,char *type, char *value )
975 tmp = ldif_type_and_value(type, value, strlen(value));
983 ldif_export_database(FILE *out, struct db_enumerator e)
985 char email[MAX_EMAILSTR_LEN];
986 abook_list *emails, *em;
988 fprintf(out, "version: 1\n");
990 db_enumerate_items(e) {
993 get_first_email(email, e.item);
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 */
1000 tmp = strdup_printf("cn=%s",db_name_get(e.item));
1002 ldif_fput_type_and_value(out, "dn", tmp);
1005 for(j = 0; j < ITEM_FIELDS; j++) {
1008 tmp = db_email_get(e.item);
1009 emails = csv_to_abook_list(tmp);
1011 for(em = emails; em; em = em->next)
1012 ldif_fput_type_and_value(out,
1013 ldif_field_names[EMAIL],
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));
1024 fprintf(out, "objectclass: top\n"
1025 "objectclass: person\n\n");
1032 * end of ldif export filter
1036 * html export filter
1039 static void html_export_write_head(FILE *out);
1040 static void html_export_write_tail(FILE *out);
1042 extern struct index_elem *index_elements;
1045 html_print_emails(FILE *out, struct list_field *f)
1047 abook_list *l = csv_to_abook_list(f->data);
1049 for(; l; l = l->next) {
1050 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
1055 abook_list_free(&l);
1059 html_export_database(FILE *out, struct db_enumerator e)
1061 struct list_field f;
1062 struct index_elem *cur;
1069 html_export_write_head(out);
1071 db_enumerate_items(e) {
1072 fprintf(out, " <tr>\n");
1073 for(cur = index_elements; cur; cur = cur->next) {
1074 if(cur->type != INDEX_FIELD)
1077 get_list_field(e.item, cur, &f);
1079 fprintf(out, " <td>");
1081 if(f.type == FIELD_EMAILS) {
1082 html_print_emails(out, &f);
1084 if (strcmp(safe_str(f.data), "") == 0)
1085 fprintf(out, " ");
1087 fprintf(out, "%s", safe_str(f.data));
1089 fprintf(out, "</td>\n");
1091 fprintf(out, " </tr>\n");
1094 html_export_write_tail(out);
1100 html_export_write_head(FILE *out)
1102 char *realname = get_real_name(), *str;
1103 struct index_elem *cur;
1105 fprintf(out, "<!DOCTYPE html>\n");
1106 fprintf(out, "<html>\n");
1107 fprintf(out, "<head>\n");
1108 fprintf(out, " <meta charset=\"utf-8\" />\n");
1109 fprintf(out, " <title>");
1110 fprintf(out, _("%s's addressbook"), realname );
1111 fprintf(out, "</title>\n");
1112 fprintf(out, " <style type=\"text/css\">\n");
1113 fprintf(out, " table {border-collapse: collapse ; border: 1px solid #000;}\n");
1114 fprintf(out, " table th, table td {text-align: left; border: 1px solid #000; padding: 0.33em;}\n");
1115 fprintf(out, " table th {border-bottom: 3px double #000; background-color: #ccc;}\n");
1116 fprintf(out, " </style>\n");
1117 fprintf(out, "</head>\n");
1118 fprintf(out, "<body>\n");
1119 fprintf(out, "<h1>");
1120 fprintf(out, _("%s's addressbook"), realname);
1121 fprintf(out, "</h1>\n");
1123 fprintf(out, "<table>\n");
1124 fprintf(out, "<thead>\n");
1125 fprintf(out, " <tr>\n");
1126 for(cur = index_elements; cur; cur = cur->next) {
1127 if(cur->type != INDEX_FIELD)
1130 get_field_info(cur->d.field.id, NULL, &str, NULL);
1132 fprintf(out, " <th>");
1134 if (strcmp(str, "") == 0)
1135 fprintf(out, " ");
1137 fprintf(out, "%s", str);
1139 fprintf(out, "</th>\n");
1141 fprintf(out, " </tr>\n");
1142 fprintf(out, "<thead>\n");
1143 fprintf(out, "<tbody>\n");
1149 html_export_write_tail(FILE *out)
1151 fprintf(out, "</table>\n");
1152 fprintf(out, "</body>\n");
1153 fprintf(out, "</html>");
1157 * end of html export filter
1162 * pine addressbook import filter
1165 #define PINE_BUF_SIZE 2048
1168 pine_fixbuf(char *buf)
1172 for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1173 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1177 pine_convert_emails(char *s)
1182 if(s == NULL || *s != '(')
1185 for(i = 0; s[i]; i++)
1188 if( ( tmp = strchr(s,')')) )
1191 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1192 if(i > MAX_LIST_ITEMS - 1) {
1200 pine_parse_buf(char *buf)
1205 char tmp[PINE_BUF_SIZE];
1207 int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1209 item = item_create();
1211 for(i=0, last=0; !last ; i++) {
1212 if( !(end = strchr(start, '\t')) )
1215 len = last ? strlen(start) : (int) (end-start);
1216 len = min(len, PINE_BUF_SIZE - 1);
1218 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1219 && pine_conv_table[i] >= 0) {
1220 strncpy(tmp, start, len);
1223 item_fput(item, pine_conv_table[i],
1229 pine_convert_emails(item_fget(item, EMAIL));
1230 add_item2database(item);
1235 #define LINESIZE 1024
1238 pine_parse_file(FILE *in)
1240 char line[LINESIZE];
1245 fgets(line, LINESIZE, in);
1249 buf = xrealloc(buf, i*LINESIZE);
1252 fgets(line, LINESIZE, in);
1254 if(*ptr != ' ' || feof(in))
1268 pine_parse_buf(buf);
1277 * end of pine addressbook import filter
1282 * pine addressbook export filter
1284 * filter doesn't wrap the lines as it should but Pine seems to handle
1285 * created files without problems - JH
1289 pine_export_database(FILE *out, struct db_enumerator e)
1293 db_enumerate_items(e) {
1294 emails = db_email_get(e.item);
1295 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1296 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1297 safe_str(db_fget(e.item, NICK)),
1298 safe_str(db_name_get(e.item)),
1300 safe_str(db_fget(e.item, NOTES))
1309 * end of pine addressbook export filter
1317 /* This is used by both allcsv_export_database() and csv_export_common()
1318 to distinguish between standard and defined fields.
1319 To avoid confusions this should stay > ITEM_FIELDS */
1320 #define CUSTOM_FIELD_START_INDEX (ITEM_FIELDS + 10)
1323 * these files should be parsed according to a certain
1324 * lay out, or the default if layout is not given, at
1325 * the moment only default is done...
1328 #define CSV_COMMENT_CHAR '#'
1329 #define CSV_DUPLICATE_SEPARATOR " "
1330 #define CSV_TABLE_SIZE(t) (sizeof (t) / sizeof *(t))
1332 static int csv_conv_table[] = {
1340 static int allcsv_conv_table[] = {
1359 static int palmcsv_conv_table[] = {
1360 NAME, /* Last name */
1361 NAME, /* First name */
1378 csv_convert_emails(char *s)
1386 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1387 if(i > MAX_LIST_ITEMS - 1) {
1395 csv_remove_quotes(char *s)
1397 char *copy, *trimmed;
1400 copy = trimmed = xstrdup(s);
1403 len = strlen(trimmed);
1404 if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1409 trimmed[len - 1] = 0;
1411 trimmed = xstrdup(trimmed);
1421 csv_field_to_item(int *table_base, size_t table_size, int field)
1423 if(field < table_size)
1424 return field_id(table_base[field]);
1430 csv_store_item(list_item item, int i, char *s)
1432 char *newstr = NULL;
1437 if( !(newstr = csv_remove_quotes(s)) )
1441 if (item[i] != NULL) {
1442 char *oldstr = item[i];
1444 item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1457 csv_is_valid_quote_end(char *p)
1465 else if(!ISSPACE(*p))
1473 csv_is_valid_quote_start(char *p)
1478 else if(!ISSPACE(*p))
1486 csv_parse_line(char *line, int *table_base, size_t table_size)
1490 bool in_quote = FALSE;
1493 item = item_create();
1495 for(p = start = line, field = 0; *p; p++) {
1497 if(csv_is_valid_quote_end(p))
1500 if ( (((p - start) / sizeof (char)) < 2 ) &&
1501 csv_is_valid_quote_start(p) )
1505 if(*p == ',' && !in_quote) {
1507 csv_store_item(item,
1508 csv_field_to_item(table_base,table_size,field),
1517 csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1520 csv_convert_emails(item_fget(item, EMAIL));
1521 add_item2database(item);
1526 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1531 line = getaline(in);
1533 if(line && *line && *line != CSV_COMMENT_CHAR)
1534 csv_parse_line(line, conv_table, table_size);
1543 csv_parse_file(FILE *in)
1545 return csv_parse_file_common(in, csv_conv_table,
1546 CSV_TABLE_SIZE(csv_conv_table));
1550 allcsv_parse_file(FILE *in)
1552 return csv_parse_file_common(in, allcsv_conv_table,
1553 CSV_TABLE_SIZE(allcsv_conv_table));
1557 palmcsv_parse_file(FILE *in)
1559 return csv_parse_file_common(in, palmcsv_conv_table,
1560 CSV_TABLE_SIZE(palmcsv_conv_table));
1564 * end of csv import filter
1568 * vCard import filter
1571 static char *vcard_fields[] = {
1572 "FN", /* FORMATTED NAME */
1573 "EMAIL", /* EMAIL */
1574 "ADR", /* ADDRESS */
1575 "ADR", /* ADDRESS2 */
1579 "ADR", /* COUNTRY */
1581 "TEL", /* WORKPHONE */
1583 "TEL", /* MOBILEPHONE */
1584 "NICKNAME", /* NICK */
1587 "BDAY", /* ANNIVERSARY */
1588 "N", /* NAME: special case/mapping in vcard_parse_line() */
1589 NULL /* ITEM_FIELDS */
1594 VCARD_KEY_ATTRIBUTE,
1599 vcard_get_line_element(char *line, int element)
1602 char *line_copy = 0;
1608 line_copy = xstrdup(line);
1610 /* change newline characters, if present, to end of string */
1611 for(i=0; line_copy[i]; i++) {
1612 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1613 line_copy[i] = '\0';
1618 /* separate key from value */
1619 for(i=0; line_copy[i]; i++) {
1620 if(line_copy[i] == ':') {
1621 line_copy[i] = '\0';
1623 value = &line_copy[i+1];
1628 /* separate key from key attributes */
1629 /* works for vCard 2 as well (automagically) */
1631 for(i=0; key[i]; i++) {
1634 key_attr = &key[i+1];
1643 result = xstrdup(key);
1645 case VCARD_KEY_ATTRIBUTE:
1647 result = xstrdup(key_attr);
1651 result = xstrdup(value);
1660 vcard_parse_email(list_item item, char *line)
1664 email = vcard_get_line_element(line, VCARD_VALUE);
1667 item[1] = strconcat(item[1], ",", email, 0);
1677 * mappings between vCard ADR field and abook's ADDRESS
1678 * see rfc2426 section 3.2.1
1681 vcard_parse_address(list_item item, char *line)
1685 value = vcard_get_line_element(line, VCARD_VALUE);
1689 // vCard(post office box) - not used
1690 strsep(&value, ";");
1693 // vCard(the extended address)
1694 item_fput(item, ADDRESS2, xstrdup(strsep(&value, ";")));
1697 // vCard(the street address)
1698 item_fput(item, ADDRESS, xstrdup(strsep(&value, ";")));
1701 // vCard(the locality)
1702 item_fput(item, CITY, xstrdup(strsep(&value, ";")));
1705 // vCard(the region)
1706 item_fput(item, STATE, xstrdup(strsep(&value, ";")));
1709 // vCard(the postal code)
1710 item_fput(item, ZIP, xstrdup(strsep(&value, ";")));
1713 // vCard(the country name)
1714 item_fput(item, COUNTRY, xstrdup(strsep(&value, ";")));
1716 // support of optional trailing ";" to the ADR field
1717 if(value && *value) xfree(value);
1721 vcard_parse_name(list_item item, char *line)
1723 // store the "N" field into "NAME" *if* no "FN:"
1724 // value has already been stored here
1728 item[0] = vcard_get_line_element(line, VCARD_VALUE);
1729 // "N:" can be multivalued => replace ';' separators by ' '
1730 while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1732 // http://www.daniweb.com/software-development/c/code/216919
1733 char *original = item[0], *p = original;
1736 if (*original != ' ' || trimmed) {
1737 trimmed = 1; *p++ = *original;
1739 } while(*original++);
1743 vcard_parse_phone(list_item item, char *line)
1745 char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1746 char *value = vcard_get_line_element(line, VCARD_VALUE);
1748 /* set the standard number */
1749 if (!type) item_fput(item, PHONE, value);
1752 * see rfc2426 section 3.3.1
1753 * Note: we probably support both vCard 2 and 3
1756 if (strcasestr(type, "home") != NULL)
1757 item_fput(item, PHONE, xstrdup(value));
1758 else if (strcasestr(type, "work") != NULL)
1759 item_fput(item, WORKPHONE, xstrdup(value));
1760 else if (strcasestr(type, "fax") != NULL)
1761 item_fput(item, FAX, xstrdup(value));
1762 else if (strcasestr(type, "cell") != NULL)
1763 item_fput(item, MOBILEPHONE, xstrdup(value));
1771 vcard_parse_line(list_item item, char *line)
1776 for(i=0; vcard_fields[i]; i++) {
1777 key = vcard_fields[i];
1779 if(0 == strncmp(key, line, strlen(key))) {
1780 if(0 == strcmp(key, "EMAIL"))
1781 vcard_parse_email(item, line);
1783 vcard_parse_address(item, line);
1784 else if(0 == strcmp(key, "TEL"))
1785 vcard_parse_phone(item, line);
1786 else if(0 == strcmp(key, "N"))
1787 vcard_parse_name(item, line);
1789 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1796 vcard_parse_item(FILE *in)
1799 list_item item = item_create();
1802 line = getaline(in);
1804 if(line && !strncmp("END:VCARD", line, 9)) {
1809 vcard_parse_line(item, line);
1814 add_item2database(item);
1819 vcard_parse_file(FILE *in)
1824 line = getaline(in);
1826 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1828 vcard_parse_item(in);
1839 * end of vCard import filter
1843 * csv addressbook export filters
1846 #define CSV_LAST (-1)
1847 #define CSV_UNDEFINED (-2)
1848 #define CSV_SPECIAL(X) (-3 - (X))
1849 #define CSV_IS_SPECIAL(X) ((X) <= -3)
1852 csv_export_common(FILE *out, struct db_enumerator e,
1853 int fields[], void (*special_func)(FILE *, int, int))
1857 db_enumerate_items(e) {
1858 for(i = 0; fields[i] != CSV_LAST; i++) {
1859 if(fields[i] == CSV_UNDEFINED)
1860 fprintf(out, "\"\"");
1861 else if(CSV_IS_SPECIAL(fields[i])) {
1863 (*special_func)(out, e.item, fields[i]);
1865 else if(fields[i] >= CUSTOM_FIELD_START_INDEX) {
1866 fprintf(out, "\"%s\"",
1867 safe_str(db_fget_byid(e.item, fields[i] - CUSTOM_FIELD_START_INDEX)));
1871 strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1872 strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1874 safe_str(database[e.item][field_idx(fields[i])])
1876 fprintf(out, "\"%s\"",
1877 safe_str(db_fget(e.item,fields[i])));
1879 if(fields[i + 1] != CSV_LAST)
1889 csv_export_database(FILE *out, struct db_enumerator e)
1891 int csv_export_fields[] = {
1900 csv_export_common(out, e, csv_export_fields, NULL);
1906 allcsv_export_database(FILE *out, struct db_enumerator e)
1909 * TODO: Should get these atomatically from abook_fileds
1912 int allcsv_export_fields[ITEM_FIELDS + 6] = { // only the 5 custom fields are allowed so far
1924 MOBILEPHONE, // spelt "mobile" in standard_fields
1935 while(allcsv_export_fields[i+1] != CSV_LAST) {
1936 fprintf(out, "\"%s\",", standard_fields[i++].key);
1938 fprintf(out, "\"%s\"", standard_fields[i].key);
1941 Custom fields handling:
1942 This loop appends custom fields' id at the end of allcsv_export_fields and shift
1943 the CSV_LAST sentinel value each time one is found.
1944 CUSTOM_FIELD_START_INDEX is added to these index values so csv_export_common()
1945 can later recognize them and call db_fget_byid() instead of the traditional db_fget()
1947 It only search for defined the [legacy?] "custom" fields.
1950 // pointer to the end of the field list
1951 int append_field = ITEM_FIELDS;
1952 // custom field's trailing number (between 1 and 5)
1954 // full custom field name, eg "custom4"
1955 char custom_field_key[8];
1956 // index used by custom_field_key
1958 // name of the defined field <field_no> as chosen by the user
1959 char *custom_field_name;
1961 for (j = 1; j <= 5; j++) {
1962 snprintf(custom_field_key, 8, "custom%d", j++);
1963 if(find_declared_field(custom_field_key)) {
1964 find_field_number(custom_field_key, &field_no);
1965 get_field_info(field_no, NULL, &custom_field_name, NULL);
1966 // append the field to the list
1967 allcsv_export_fields[append_field] = field_no + CUSTOM_FIELD_START_INDEX;
1968 allcsv_export_fields[++append_field] = CSV_LAST;
1969 // print column name
1970 fprintf(out, ",\"%s\"", custom_field_name);
1973 free(custom_field_name);
1976 csv_export_common(out, e, allcsv_export_fields, NULL);
1985 #define PALM_CSV_NAME CSV_SPECIAL(0)
1986 #define PALM_CSV_END CSV_SPECIAL(1)
1987 #define PALM_CSV_CAT CSV_SPECIAL(2)
1990 palm_split_and_write_name(FILE *out, char *name)
1996 if ( (p = strchr(name, ' ')) ) {
2000 fprintf(out, "\"%s\",\"" , p + 1);
2001 fwrite((void *)name, p - name, sizeof(char), out);
2004 fprintf(out, "\"%s\"", safe_str(name));
2009 palm_csv_handle_specials(FILE *out, int item, int field)
2013 palm_split_and_write_name(out, db_name_get(item));
2016 fprintf(out, "\"abook\"");
2019 fprintf(out, "\"0\"");
2027 palm_export_database(FILE *out, struct db_enumerator e)
2029 int palm_export_fields[] = {
2030 PALM_CSV_NAME, /* LASTNAME, FIRSTNAME */
2031 CSV_UNDEFINED, /* TITLE */
2032 CSV_UNDEFINED, /* COMPANY */
2033 WORKPHONE, /* WORK PHONE */
2034 PHONE, /* HOME PHONE */
2036 MOBILEPHONE, /* OTHER */
2038 ADDRESS, /* ADDRESS */
2042 COUNTRY, /* COUNTRY */
2043 NICK, /* DEFINED 1 */
2044 URL, /* DEFINED 2 */
2045 CSV_UNDEFINED, /* DEFINED 3 */
2046 CSV_UNDEFINED, /* DEFINED 4 */
2048 PALM_CSV_END, /* "0" */
2049 PALM_CSV_CAT, /* CATEGORY */
2053 csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
2059 * end of csv export filters
2063 * vCard 2 addressbook export filter
2067 vcard_export_database(FILE *out, struct db_enumerator e)
2069 db_enumerate_items(e)
2070 vcard_export_item(out, e.item);
2075 vcard_export_item(FILE *out, int item)
2079 abook_list *emails, *em;
2080 fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
2081 safe_str(db_name_get(item)));
2083 name = get_surname(db_name_get(item));
2084 for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
2085 if((db_name_get(item))[j] == ' ')
2088 fprintf(out, "N:%s;%.*s\r\n",
2091 safe_str(db_name_get(item))
2096 if(db_fget(item, NICK))
2097 fprintf(out, "NICKNAME:%s\r\n",
2098 safe_str(db_fget(item, NICK)));
2099 if(db_fget(item, ANNIVERSARY))
2100 fprintf(out, "BDAY:%s\r\n",
2101 safe_str(db_fget(item, ANNIVERSARY)));
2103 // see rfc6350 section 6.3.1
2104 if(db_fget(item, ADDRESS)) {
2105 fprintf(out, "ADR:;%s;%s;%s;%s;%s;%s\r\n",
2106 // pobox (unsupported)
2107 safe_str(db_fget(item, ADDRESS2)), // ext (n°, ...)
2108 safe_str(db_fget(item, ADDRESS)), // street
2109 safe_str(db_fget(item, CITY)), // locality
2110 safe_str(db_fget(item, STATE)), // region
2111 safe_str(db_fget(item, ZIP)), // code (postal)
2112 safe_str(db_fget(item, COUNTRY)) // country
2116 if(db_fget(item, PHONE))
2117 fprintf(out, "TEL;HOME:%s\r\n",
2118 db_fget(item, PHONE));
2119 if(db_fget(item, WORKPHONE))
2120 fprintf(out, "TEL;WORK:%s\r\n",
2121 db_fget(item, WORKPHONE));
2122 if(db_fget(item, FAX))
2123 fprintf(out, "TEL;FAX:%s\r\n",
2124 db_fget(item, FAX));
2125 if(db_fget(item, MOBILEPHONE))
2126 fprintf(out, "TEL;CELL:%s\r\n",
2127 db_fget(item, MOBILEPHONE));
2129 tmp = db_email_get(item);
2131 emails = csv_to_abook_list(tmp);
2132 fprintf(out, "EMAIL;PREF;INTERNET:%s\r\n", emails->data);
2134 for(em = emails->next; em; em = em->next, email_no++ )
2135 fprintf(out, "EMAIL;%d;INTERNET:%s\r\n", email_no, em->data);
2137 abook_list_free(&emails);
2141 if(db_fget(item, NOTES))
2142 fprintf(out, "NOTE:%s\r\n",
2143 db_fget(item, NOTES));
2144 if(db_fget(item, URL))
2145 fprintf(out, "URL:%s\r\n",
2146 db_fget(item, URL));
2148 fprintf(out, "END:VCARD\r\n\r\n");
2153 * end of vCard export filter
2158 * mutt alias export filter
2162 mutt_alias_genalias(int i)
2166 if(db_fget(i, NICK))
2167 return xstrdup(db_fget(i, NICK));
2169 tmp = xstrdup(db_name_get(i));
2171 if( ( pos = strchr(tmp, ' ') ) )
2180 * This function is a variant of abook_list_to_csv
2183 mutt_alias_gengroups(int i)
2185 char *groups, *res = NULL;
2186 char groupstr[7] = "-group ";
2187 abook_list *list, *tmp;
2189 groups = db_fget(i, GROUPS);
2194 list = csv_to_abook_list(groups);
2195 for(tmp = list; tmp; tmp = tmp->next) {
2197 res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2198 res = strcpy(res, groupstr);
2200 res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2202 strcat(res, groupstr);
2204 strcat(res, tmp->data);
2206 abook_list_free(&list);
2213 mutt_alias_export(FILE *out, struct db_enumerator e)
2215 char email[MAX_EMAIL_LEN];
2217 char *groups = NULL;
2218 int email_addresses;
2221 db_enumerate_items(e) {
2222 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2223 groups = (field_id(GROUPS) != -1) ? mutt_alias_gengroups(e.item) : NULL;
2224 get_first_email(email, e.item);
2226 /* do not output contacts without email address */
2227 /* cause this does not make sense in mutt aliases */
2230 /* output first email address */
2231 fprintf(out,"alias ");
2233 fprintf(out, "%s ", groups);
2235 fprintf(out, "%s ", alias);
2236 fprintf(out, "%s <%s>\n",
2237 db_name_get(e.item),
2240 /* number of email addresses */
2241 email_addresses = 1;
2242 ptr = db_email_get(e.item);
2243 while (*ptr != '\0') {
2250 /* output other email addresses */
2251 while (email_addresses-- > 1) {
2252 roll_emails(e.item, ROTATE_RIGHT);
2253 get_first_email(email, e.item);
2254 fprintf(out,"alias ");
2256 fprintf(out, "%s ", groups);
2258 fprintf(out, "%s__%s ", alias, email);
2260 fprintf(out, "%s__%s ", db_name_get(e.item), email);
2261 fprintf(out, "%s <%s>\n",
2262 db_name_get(e.item),
2265 roll_emails(e.item, ROTATE_RIGHT);
2274 void muttq_print_item(FILE *file, int item)
2276 abook_list *emails, *e;
2277 char *tmp = db_email_get(item);
2279 emails = csv_to_abook_list(tmp);
2282 for(e = emails; e; e = e->next) {
2283 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2284 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2286 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2289 abook_list_free(&emails);
2293 mutt_query_export_database(FILE *out, struct db_enumerator e)
2295 fprintf(out, "All items\n");
2296 db_enumerate_items(e)
2297 muttq_print_item(out, e.item);
2302 * end of mutt alias export filter
2307 * printable export filter
2312 text_write_address_us(FILE *out, int i) {
2313 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2315 if(db_fget(i, ADDRESS2))
2316 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2318 if(db_fget(i, CITY))
2319 fprintf(out, "\n%s", db_fget(i, CITY));
2321 if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2324 if(db_fget(i, STATE)) {
2325 fprintf(out, "%s", db_fget(i, STATE));
2331 fprintf(out, "%s", db_fget(i, ZIP));
2334 if(db_fget(i, COUNTRY))
2335 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2340 text_write_address_uk(FILE *out, int i) {
2343 for(j = ADDRESS; j <= COUNTRY; j++)
2345 fprintf(out, "\n%s", db_fget(i, j));
2349 text_write_address_eu(FILE *out, int i) {
2350 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2352 if(db_fget(i, ADDRESS2))
2353 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2355 if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2358 if(db_fget(i, ZIP)) {
2359 fprintf(out, "%s", db_fget(i, ZIP));
2360 if(db_fget(i, CITY))
2364 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2367 if(db_fget(i, STATE))
2368 fprintf(out, "\n%s", db_fget(i, STATE));
2370 if(db_fget(i, COUNTRY))
2371 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2375 text_export_database(FILE * out, struct db_enumerator e)
2377 abook_list *emails, *em;
2379 char *realname = get_real_name(), *str = NULL, *tmp;
2380 char *style = opt_get_str(STR_ADDRESS_STYLE);
2383 "-----------------------------------------\n%s's address book\n"
2384 "-----------------------------------------\n\n\n",
2388 db_enumerate_items(e) {
2390 "-----------------------------------------\n\n");
2391 fprintf(out, "%s", db_name_get(e.item));
2392 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2393 fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2396 tmp = db_email_get(e.item);
2398 emails = csv_to_abook_list(tmp);
2401 for(em = emails; em; em = em->next)
2402 fprintf(out, "%s\n", em->data);
2404 abook_list_free(&emails);
2408 if(db_fget(e.item, ADDRESS)) {
2409 if(!safe_strcmp(style, "us")) /* US like */
2410 text_write_address_us(out, e.item);
2411 else if(!safe_strcmp(style, "uk")) /* UK like */
2412 text_write_address_uk(out, e.item);
2414 text_write_address_eu(out, e.item);
2419 if((db_fget(e.item, PHONE)) ||
2420 (db_fget(e.item, WORKPHONE)) ||
2421 (db_fget(e.item, FAX)) ||
2422 (db_fget(e.item, MOBILEPHONE))) {
2424 for(j = PHONE; j <= MOBILEPHONE; j++)
2425 if(db_fget(e.item, j)) {
2426 get_field_info(field_id(j),
2428 fprintf(out, "%s: %s\n", str,
2429 db_fget(e.item, j));
2433 if(db_fget(e.item, URL))
2434 fprintf(out, "\n%s\n", db_fget(e.item, URL));
2435 if(db_fget(e.item, NOTES))
2436 fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2441 fprintf(out, "-----------------------------------------\n");
2447 * end of printable export filter
2451 * elm alias export filter
2455 elm_alias_export(FILE *out, struct db_enumerator e)
2457 char email[MAX_EMAIL_LEN];
2460 db_enumerate_items(e) {
2461 alias = mutt_alias_genalias(e.item);
2462 get_first_email(email, e.item);
2463 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2471 * end of elm alias export filter
2476 * Spruce export filter
2480 spruce_export_database (FILE *out, struct db_enumerator e)
2482 char email[MAX_EMAIL_LEN];
2484 fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2486 db_enumerate_items(e) {
2487 get_first_email(email, e.item);
2488 if(strcmp(email, "")) {
2489 fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2491 db_name_get(e.item),
2493 safe_str(db_fget(e.item, NOTES))
2498 fprintf (out, "# End of address book file.\n");
2504 * end of Spruce export filter
2508 * wanderlust addressbook export filter
2512 wl_export_database(FILE *out, struct db_enumerator e)
2514 char email[MAX_EMAIL_LEN];
2516 fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2517 db_enumerate_items(e) {
2518 get_first_email(email, e.item);
2521 "%s\t\"%s\"\t\"%s\"\n",
2523 safe_str(db_fget(e.item, NICK)),
2524 safe_str(db_name_get(e.item))
2529 fprintf (out, "\n# End of address book file.\n");
2535 * end of wanderlust addressbook export filter
2539 * BSD calendar export filter
2543 bsdcal_export_database(FILE *out, struct db_enumerator e)
2545 db_enumerate_items(e) {
2546 int year, month = 0, day = 0;
2547 char *anniversary = db_fget(e.item, ANNIVERSARY);
2550 if(!parse_date_string(anniversary, &day, &month, &year))
2554 _("%02d/%02d\tAnniversary of %s\n"),
2557 safe_str(db_name_get(e.item))
2566 * end of BSD calendar export filter
2570 * custom export filter
2574 find_field_enum(char *s) {
2576 while(standard_fields[++i].key)
2577 if(!strcmp(standard_fields[i].key, s))
2582 /* Convert a string with named placeholders to
2583 a *printf() compatible string.
2584 Stores the abook field values into ft. */
2586 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2588 if(! fmt_string || ! ft) {
2589 fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2593 char tmp[1] = { 0 };
2594 char *p, *start, *field_name = NULL;
2601 if(! *start) goto cannotparse;
2602 p = strchr(start, '}');
2603 if(! p) goto cannotparse;
2604 strcat(fmt_string, "%s");
2605 field_name = strndup(start, (size_t)(p-start));
2606 *ft = find_field_enum(field_name);
2608 fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2616 else if(*p == '\\') {
2618 if(! *p) tmp[0] = '\\'; // last char is a '\' ?
2619 else if(*p == 'n') *tmp = '\n';
2620 else if(*p == 't') *tmp = '\t';
2621 else if(*p == 'r') *tmp = '\r';
2622 else if(*p == 'v') *tmp = '\v';
2623 else if(*p == 'b') *tmp = '\b';
2624 else if(*p == 'a') *tmp = '\a';
2626 strncat(fmt_string, tmp, 1);
2630 // if no '\' following: quick mode using strchr/strncat
2631 else if(! strchr(start, '\\')) {
2632 p = strchr(start, '{');
2633 if(p) { // copy until the next placeholder
2634 strncat(fmt_string, start, (size_t)(p-start));
2637 else { // copy till the end
2638 strncat( fmt_string,
2640 FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2645 // otherwise character by character
2647 strncat(fmt_string, p, 1);
2656 fprintf(stderr, _("%s: invalid format, index %ld\n"), __FUNCTION__, (start - s));
2662 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2665 // stores the format string generated from --outformatstr {custom_format}
2666 // (when "custom" output format is requested)
2667 // overrides default value of custom_format set by from abook.c
2668 extern char custom_format[FORMAT_STRING_LEN];
2669 char parsed_custom_format[FORMAT_STRING_LEN];
2670 enum field_types *custom_format_fields = 0;
2672 /* wrapper for custom_export_item:
2673 1) avoid messing with extern pointer
2675 3) follow the prototype needed for an abook_output_item_filter entry */
2677 custom_print_item(FILE *out, int item)
2680 if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2685 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2689 // if the first character is '!':
2690 // we first check that all fields exist before continuing
2692 enum field_types *ftp = ft;
2693 while(*ft != ITEM_FIELDS) {
2694 if(! db_fget(item, *ft) )
2703 if(!strncmp(fmt, "%s", 2)) {
2704 fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2707 } else if (*ft == ITEM_FIELDS) {
2708 fprintf(out, "%s", fmt);
2711 p = strchr(fmt, '%');
2713 q = strndup(fmt, (size_t)(p-fmt));
2714 fprintf(out, "%s", q);
2719 fprintf(out, "%s", fmt);
2729 custom_export_database(FILE *out, struct db_enumerator e)
2731 enum field_types *ft =
2732 (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types));
2734 parse_custom_format(custom_format, (char*)&parsed_custom_format, ft);
2735 db_enumerate_items(e) {
2736 if(custom_export_item(out, e.item, (char*)&parsed_custom_format, ft) == 0)
2743 * end of custom export filter