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>");
1073 for(cur = index_elements; cur; cur = cur->next) {
1074 if(cur->type != INDEX_FIELD)
1077 get_list_field(e.item, cur, &f);
1079 if(f.type == FIELD_EMAILS) {
1080 fprintf(out, "<td>");
1081 html_print_emails(out, &f);
1082 fprintf(out, "</td>");
1085 fprintf(out, "<td>%s</td>", safe_str(f.data));
1088 fprintf(out, "</tr>\n");
1091 html_export_write_tail(out);
1097 html_export_write_head(FILE *out)
1099 char *realname = get_real_name(), *str;
1100 struct index_elem *cur;
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>",
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");
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)
1114 get_field_info(cur->d.field.id, NULL, &str, NULL);
1115 fprintf(out, "<th>%s</th>", str);
1117 fprintf(out, "</tr>\n\n");
1123 html_export_write_tail(FILE *out)
1125 fprintf(out, "\n</table>\n");
1126 fprintf(out, "\n</body>\n</html>\n");
1130 * end of html export filter
1135 * pine addressbook import filter
1138 #define PINE_BUF_SIZE 2048
1141 pine_fixbuf(char *buf)
1145 for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1146 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1150 pine_convert_emails(char *s)
1155 if(s == NULL || *s != '(')
1158 for(i = 0; s[i]; i++)
1161 if( ( tmp = strchr(s,')')) )
1164 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1165 if(i > MAX_LIST_ITEMS - 1) {
1173 pine_parse_buf(char *buf)
1178 char tmp[PINE_BUF_SIZE];
1180 int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1182 item = item_create();
1184 for(i=0, last=0; !last ; i++) {
1185 if( !(end = strchr(start, '\t')) )
1188 len = last ? strlen(start) : (int) (end-start);
1189 len = min(len, PINE_BUF_SIZE - 1);
1191 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1192 && pine_conv_table[i] >= 0) {
1193 strncpy(tmp, start, len);
1196 item_fput(item, pine_conv_table[i],
1202 pine_convert_emails(item_fget(item, EMAIL));
1203 add_item2database(item);
1208 #define LINESIZE 1024
1211 pine_parse_file(FILE *in)
1213 char line[LINESIZE];
1218 fgets(line, LINESIZE, in);
1222 buf = xrealloc(buf, i*LINESIZE);
1225 fgets(line, LINESIZE, in);
1227 if(*ptr != ' ' || feof(in))
1241 pine_parse_buf(buf);
1250 * end of pine addressbook import filter
1255 * pine addressbook export filter
1257 * filter doesn't wrap the lines as it should but Pine seems to handle
1258 * created files without problems - JH
1262 pine_export_database(FILE *out, struct db_enumerator e)
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)),
1273 safe_str(db_fget(e.item, NOTES))
1282 * end of pine addressbook export filter
1290 /* This is used by both allcsv_export_database() and csv_export_common()
1291 to distinguish between standard and defined fields.
1292 To avoid confusions this should stay > ITEM_FIELDS */
1293 #define CUSTOM_FIELD_START_INDEX (ITEM_FIELDS + 10)
1296 * these files should be parsed according to a certain
1297 * lay out, or the default if layout is not given, at
1298 * the moment only default is done...
1301 #define CSV_COMMENT_CHAR '#'
1302 #define CSV_DUPLICATE_SEPARATOR " "
1303 #define CSV_TABLE_SIZE(t) (sizeof (t) / sizeof *(t))
1305 static int csv_conv_table[] = {
1313 static int allcsv_conv_table[] = {
1332 static int palmcsv_conv_table[] = {
1333 NAME, /* Last name */
1334 NAME, /* First name */
1351 csv_convert_emails(char *s)
1359 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1360 if(i > MAX_LIST_ITEMS - 1) {
1368 csv_remove_quotes(char *s)
1370 char *copy, *trimmed;
1373 copy = trimmed = xstrdup(s);
1376 len = strlen(trimmed);
1377 if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1382 trimmed[len - 1] = 0;
1384 trimmed = xstrdup(trimmed);
1394 csv_field_to_item(int *table_base, size_t table_size, int field)
1396 if(field < table_size)
1397 return field_id(table_base[field]);
1403 csv_store_item(list_item item, int i, char *s)
1405 char *newstr = NULL;
1410 if( !(newstr = csv_remove_quotes(s)) )
1414 if (item[i] != NULL) {
1415 char *oldstr = item[i];
1417 item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1430 csv_is_valid_quote_end(char *p)
1438 else if(!ISSPACE(*p))
1446 csv_is_valid_quote_start(char *p)
1451 else if(!ISSPACE(*p))
1459 csv_parse_line(char *line, int *table_base, size_t table_size)
1463 bool in_quote = FALSE;
1466 item = item_create();
1468 for(p = start = line, field = 0; *p; p++) {
1470 if(csv_is_valid_quote_end(p))
1473 if ( (((p - start) / sizeof (char)) < 2 ) &&
1474 csv_is_valid_quote_start(p) )
1478 if(*p == ',' && !in_quote) {
1480 csv_store_item(item,
1481 csv_field_to_item(table_base,table_size,field),
1490 csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1493 csv_convert_emails(item_fget(item, EMAIL));
1494 add_item2database(item);
1499 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1504 line = getaline(in);
1506 if(line && *line && *line != CSV_COMMENT_CHAR)
1507 csv_parse_line(line, conv_table, table_size);
1516 csv_parse_file(FILE *in)
1518 return csv_parse_file_common(in, csv_conv_table,
1519 CSV_TABLE_SIZE(csv_conv_table));
1523 allcsv_parse_file(FILE *in)
1525 return csv_parse_file_common(in, allcsv_conv_table,
1526 CSV_TABLE_SIZE(allcsv_conv_table));
1530 palmcsv_parse_file(FILE *in)
1532 return csv_parse_file_common(in, palmcsv_conv_table,
1533 CSV_TABLE_SIZE(palmcsv_conv_table));
1537 * end of csv import filter
1541 * vCard import filter
1544 static char *vcard_fields[] = {
1545 "FN", /* FORMATTED NAME */
1546 "EMAIL", /* EMAIL */
1547 "ADR", /* ADDRESS */
1548 "ADR", /* ADDRESS2 */
1552 "ADR", /* COUNTRY */
1554 "TEL", /* WORKPHONE */
1556 "TEL", /* MOBILEPHONE */
1557 "NICKNAME", /* NICK */
1560 "BDAY", /* ANNIVERSARY */
1561 "N", /* NAME: special case/mapping in vcard_parse_line() */
1562 NULL /* ITEM_FIELDS */
1567 VCARD_KEY_ATTRIBUTE,
1572 vcard_get_line_element(char *line, int element)
1575 char *line_copy = 0;
1581 line_copy = xstrdup(line);
1583 /* change newline characters, if present, to end of string */
1584 for(i=0; line_copy[i]; i++) {
1585 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1586 line_copy[i] = '\0';
1591 /* separate key from value */
1592 for(i=0; line_copy[i]; i++) {
1593 if(line_copy[i] == ':') {
1594 line_copy[i] = '\0';
1596 value = &line_copy[i+1];
1601 /* separate key from key attributes */
1602 /* works for vCard 2 as well (automagically) */
1604 for(i=0; key[i]; i++) {
1607 key_attr = &key[i+1];
1616 result = xstrdup(key);
1618 case VCARD_KEY_ATTRIBUTE:
1620 result = xstrdup(key_attr);
1624 result = xstrdup(value);
1633 vcard_parse_email(list_item item, char *line)
1637 email = vcard_get_line_element(line, VCARD_VALUE);
1640 item[1] = strconcat(item[1], ",", email, 0);
1650 * mappings between vCard ADR field and abook's ADDRESS
1651 * see rfc2426 section 3.2.1
1654 vcard_parse_address(list_item item, char *line)
1658 value = vcard_get_line_element(line, VCARD_VALUE);
1662 // vCard(post office box) - not used
1663 strsep(&value, ";");
1666 // vCard(the extended address)
1667 item_fput(item, ADDRESS2, xstrdup(strsep(&value, ";")));
1670 // vCard(the street address)
1671 item_fput(item, ADDRESS, xstrdup(strsep(&value, ";")));
1674 // vCard(the locality)
1675 item_fput(item, CITY, xstrdup(strsep(&value, ";")));
1678 // vCard(the region)
1679 item_fput(item, STATE, xstrdup(strsep(&value, ";")));
1682 // vCard(the postal code)
1683 item_fput(item, ZIP, xstrdup(strsep(&value, ";")));
1686 // vCard(the country name)
1687 item_fput(item, COUNTRY, xstrdup(strsep(&value, ";")));
1689 // support of optional trailing ";" to the ADR field
1690 if(value && *value) xfree(value);
1694 vcard_parse_name(list_item item, char *line)
1696 // store the "N" field into "NAME" *if* no "FN:"
1697 // value has already been stored here
1701 item[0] = vcard_get_line_element(line, VCARD_VALUE);
1702 // "N:" can be multivalued => replace ';' separators by ' '
1703 while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1705 // http://www.daniweb.com/software-development/c/code/216919
1706 char *original = item[0], *p = original;
1709 if (*original != ' ' || trimmed) {
1710 trimmed = 1; *p++ = *original;
1712 } while(*original++);
1716 vcard_parse_phone(list_item item, char *line)
1718 char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1719 char *value = vcard_get_line_element(line, VCARD_VALUE);
1721 /* set the standard number */
1722 if (!type) item_fput(item, PHONE, value);
1725 * see rfc2426 section 3.3.1
1726 * Note: we probably support both vCard 2 and 3
1729 if (strcasestr(type, "home") != NULL)
1730 item_fput(item, PHONE, xstrdup(value));
1731 else if (strcasestr(type, "work") != NULL)
1732 item_fput(item, WORKPHONE, xstrdup(value));
1733 else if (strcasestr(type, "fax") != NULL)
1734 item_fput(item, FAX, xstrdup(value));
1735 else if (strcasestr(type, "cell") != NULL)
1736 item_fput(item, MOBILEPHONE, xstrdup(value));
1744 vcard_parse_line(list_item item, char *line)
1749 for(i=0; vcard_fields[i]; i++) {
1750 key = vcard_fields[i];
1752 if(0 == strncmp(key, line, strlen(key))) {
1753 if(0 == strcmp(key, "EMAIL"))
1754 vcard_parse_email(item, line);
1756 vcard_parse_address(item, line);
1757 else if(0 == strcmp(key, "TEL"))
1758 vcard_parse_phone(item, line);
1759 else if(0 == strcmp(key, "N"))
1760 vcard_parse_name(item, line);
1762 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1769 vcard_parse_item(FILE *in)
1772 list_item item = item_create();
1775 line = getaline(in);
1777 if(line && !strncmp("END:VCARD", line, 9)) {
1782 vcard_parse_line(item, line);
1787 add_item2database(item);
1792 vcard_parse_file(FILE *in)
1797 line = getaline(in);
1799 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1801 vcard_parse_item(in);
1812 * end of vCard import filter
1816 * csv addressbook export filters
1819 #define CSV_LAST (-1)
1820 #define CSV_UNDEFINED (-2)
1821 #define CSV_SPECIAL(X) (-3 - (X))
1822 #define CSV_IS_SPECIAL(X) ((X) <= -3)
1825 csv_export_common(FILE *out, struct db_enumerator e,
1826 int fields[], void (*special_func)(FILE *, int, int))
1830 db_enumerate_items(e) {
1831 for(i = 0; fields[i] != CSV_LAST; i++) {
1832 if(fields[i] == CSV_UNDEFINED)
1833 fprintf(out, "\"\"");
1834 else if(CSV_IS_SPECIAL(fields[i])) {
1836 (*special_func)(out, e.item, fields[i]);
1838 else if(fields[i] >= CUSTOM_FIELD_START_INDEX) {
1839 fprintf(out, "\"%s\"",
1840 safe_str(db_fget_byid(e.item, fields[i] - CUSTOM_FIELD_START_INDEX)));
1844 strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1845 strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1847 safe_str(database[e.item][field_idx(fields[i])])
1849 fprintf(out, "\"%s\"",
1850 safe_str(db_fget(e.item,fields[i])));
1852 if(fields[i + 1] != CSV_LAST)
1862 csv_export_database(FILE *out, struct db_enumerator e)
1864 int csv_export_fields[] = {
1873 csv_export_common(out, e, csv_export_fields, NULL);
1879 allcsv_export_database(FILE *out, struct db_enumerator e)
1882 * TODO: Should get these atomatically from abook_fileds
1885 int allcsv_export_fields[ITEM_FIELDS + 6] = { // only the 5 custom fields are allowed so far
1897 MOBILEPHONE, // spelt "mobile" in standard_fields
1908 while(allcsv_export_fields[i+1] != CSV_LAST) {
1909 fprintf(out, "\"%s\",", standard_fields[i++].key);
1911 fprintf(out, "\"%s\"", standard_fields[i].key);
1914 Custom fields handling:
1915 This loop appends custom fields' id at the end of allcsv_export_fields and shift
1916 the CSV_LAST sentinel value each time one is found.
1917 CUSTOM_FIELD_START_INDEX is added to these index values so csv_export_common()
1918 can later recognize them and call db_fget_byid() instead of the traditional db_fget()
1920 It only search for defined the [legacy?] "custom" fields.
1923 // pointer to the end of the field list
1924 int append_field = ITEM_FIELDS;
1925 // custom field's trailing number (between 1 and 5)
1927 // full custom field name, eg "custom4"
1928 char custom_field_key[8];
1929 // index used by custom_field_key
1931 // name of the defined field <field_no> as chosen by the user
1932 char *custom_field_name;
1934 for (j = 1; j <= 5; j++) {
1935 snprintf(custom_field_key, 8, "custom%d", j++);
1936 if(find_declared_field(custom_field_key)) {
1937 find_field_number(custom_field_key, &field_no);
1938 get_field_info(field_no, NULL, &custom_field_name, NULL);
1939 // append the field to the list
1940 allcsv_export_fields[append_field] = field_no + CUSTOM_FIELD_START_INDEX;
1941 allcsv_export_fields[++append_field] = CSV_LAST;
1942 // print column name
1943 fprintf(out, ",\"%s\"", custom_field_name);
1946 free(custom_field_name);
1949 csv_export_common(out, e, allcsv_export_fields, NULL);
1958 #define PALM_CSV_NAME CSV_SPECIAL(0)
1959 #define PALM_CSV_END CSV_SPECIAL(1)
1960 #define PALM_CSV_CAT CSV_SPECIAL(2)
1963 palm_split_and_write_name(FILE *out, char *name)
1969 if ( (p = strchr(name, ' ')) ) {
1973 fprintf(out, "\"%s\",\"" , p + 1);
1974 fwrite((void *)name, p - name, sizeof(char), out);
1977 fprintf(out, "\"%s\"", safe_str(name));
1982 palm_csv_handle_specials(FILE *out, int item, int field)
1986 palm_split_and_write_name(out, db_name_get(item));
1989 fprintf(out, "\"abook\"");
1992 fprintf(out, "\"0\"");
2000 palm_export_database(FILE *out, struct db_enumerator e)
2002 int palm_export_fields[] = {
2003 PALM_CSV_NAME, /* LASTNAME, FIRSTNAME */
2004 CSV_UNDEFINED, /* TITLE */
2005 CSV_UNDEFINED, /* COMPANY */
2006 WORKPHONE, /* WORK PHONE */
2007 PHONE, /* HOME PHONE */
2009 MOBILEPHONE, /* OTHER */
2011 ADDRESS, /* ADDRESS */
2015 COUNTRY, /* COUNTRY */
2016 NICK, /* DEFINED 1 */
2017 URL, /* DEFINED 2 */
2018 CSV_UNDEFINED, /* DEFINED 3 */
2019 CSV_UNDEFINED, /* DEFINED 4 */
2021 PALM_CSV_END, /* "0" */
2022 PALM_CSV_CAT, /* CATEGORY */
2026 csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
2032 * end of csv export filters
2036 * vCard 2 addressbook export filter
2040 vcard_export_database(FILE *out, struct db_enumerator e)
2042 db_enumerate_items(e)
2043 vcard_export_item(out, e.item);
2048 vcard_export_item(FILE *out, int item)
2052 abook_list *emails, *em;
2053 fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
2054 safe_str(db_name_get(item)));
2056 name = get_surname(db_name_get(item));
2057 for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
2058 if((db_name_get(item))[j] == ' ')
2061 fprintf(out, "N:%s;%.*s\r\n",
2064 safe_str(db_name_get(item))
2069 if(db_fget(item, NICK))
2070 fprintf(out, "NICKNAME:%s\r\n",
2071 safe_str(db_fget(item, NICK)));
2072 if(db_fget(item, ANNIVERSARY))
2073 fprintf(out, "BDAY:%s\r\n",
2074 safe_str(db_fget(item, ANNIVERSARY)));
2076 // see rfc6350 section 6.3.1
2077 if(db_fget(item, ADDRESS)) {
2078 fprintf(out, "ADR:;%s;%s;%s;%s;%s;%s\r\n",
2079 // pobox (unsupported)
2080 safe_str(db_fget(item, ADDRESS2)), // ext (n°, ...)
2081 safe_str(db_fget(item, ADDRESS)), // street
2082 safe_str(db_fget(item, CITY)), // locality
2083 safe_str(db_fget(item, STATE)), // region
2084 safe_str(db_fget(item, ZIP)), // code (postal)
2085 safe_str(db_fget(item, COUNTRY)) // country
2089 if(db_fget(item, PHONE))
2090 fprintf(out, "TEL;HOME:%s\r\n",
2091 db_fget(item, PHONE));
2092 if(db_fget(item, WORKPHONE))
2093 fprintf(out, "TEL;WORK:%s\r\n",
2094 db_fget(item, WORKPHONE));
2095 if(db_fget(item, FAX))
2096 fprintf(out, "TEL;FAX:%s\r\n",
2097 db_fget(item, FAX));
2098 if(db_fget(item, MOBILEPHONE))
2099 fprintf(out, "TEL;CELL:%s\r\n",
2100 db_fget(item, MOBILEPHONE));
2102 tmp = db_email_get(item);
2104 emails = csv_to_abook_list(tmp);
2105 fprintf(out, "EMAIL;PREF;INTERNET:%s\r\n", emails->data);
2107 for(em = emails->next; em; em = em->next, email_no++ )
2108 fprintf(out, "EMAIL;%d;INTERNET:%s\r\n", email_no, em->data);
2110 abook_list_free(&emails);
2114 if(db_fget(item, NOTES))
2115 fprintf(out, "NOTE:%s\r\n",
2116 db_fget(item, NOTES));
2117 if(db_fget(item, URL))
2118 fprintf(out, "URL:%s\r\n",
2119 db_fget(item, URL));
2121 fprintf(out, "END:VCARD\r\n\r\n");
2126 * end of vCard export filter
2131 * mutt alias export filter
2135 mutt_alias_genalias(int i)
2139 if(db_fget(i, NICK))
2140 return xstrdup(db_fget(i, NICK));
2142 tmp = xstrdup(db_name_get(i));
2144 if( ( pos = strchr(tmp, ' ') ) )
2153 * This function is a variant of abook_list_to_csv
2156 mutt_alias_gengroups(int i)
2158 char *groups, *res = NULL;
2159 char groupstr[7] = "-group ";
2160 abook_list *list, *tmp;
2162 groups = db_fget(i, GROUPS);
2167 list = csv_to_abook_list(groups);
2168 for(tmp = list; tmp; tmp = tmp->next) {
2170 res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2171 res = strcpy(res, groupstr);
2173 res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2175 strcat(res, groupstr);
2177 strcat(res, tmp->data);
2179 abook_list_free(&list);
2186 mutt_alias_export(FILE *out, struct db_enumerator e)
2188 char email[MAX_EMAIL_LEN];
2190 char *groups = NULL;
2191 int email_addresses;
2194 db_enumerate_items(e) {
2195 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2196 groups = (field_id(GROUPS) != -1) ? mutt_alias_gengroups(e.item) : NULL;
2197 get_first_email(email, e.item);
2199 /* do not output contacts without email address */
2200 /* cause this does not make sense in mutt aliases */
2203 /* output first email address */
2204 fprintf(out,"alias ");
2206 fprintf(out, "%s ", groups);
2208 fprintf(out, "%s ", alias);
2209 fprintf(out, "%s <%s>\n",
2210 db_name_get(e.item),
2213 /* number of email addresses */
2214 email_addresses = 1;
2215 ptr = db_email_get(e.item);
2216 while (*ptr != '\0') {
2223 /* output other email addresses */
2224 while (email_addresses-- > 1) {
2225 roll_emails(e.item, ROTATE_RIGHT);
2226 get_first_email(email, e.item);
2227 fprintf(out,"alias ");
2229 fprintf(out, "%s ", groups);
2231 fprintf(out, "%s__%s ", alias, email);
2233 fprintf(out, "%s__%s ", db_name_get(e.item), email);
2234 fprintf(out, "%s <%s>\n",
2235 db_name_get(e.item),
2238 roll_emails(e.item, ROTATE_RIGHT);
2247 void muttq_print_item(FILE *file, int item)
2249 abook_list *emails, *e;
2250 char *tmp = db_email_get(item);
2252 emails = csv_to_abook_list(tmp);
2255 for(e = emails; e; e = e->next) {
2256 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2257 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2259 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2262 abook_list_free(&emails);
2266 mutt_query_export_database(FILE *out, struct db_enumerator e)
2268 fprintf(out, "All items\n");
2269 db_enumerate_items(e)
2270 muttq_print_item(out, e.item);
2275 * end of mutt alias export filter
2280 * printable export filter
2285 text_write_address_us(FILE *out, int i) {
2286 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2288 if(db_fget(i, ADDRESS2))
2289 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2291 if(db_fget(i, CITY))
2292 fprintf(out, "\n%s", db_fget(i, CITY));
2294 if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2297 if(db_fget(i, STATE)) {
2298 fprintf(out, "%s", db_fget(i, STATE));
2304 fprintf(out, "%s", db_fget(i, ZIP));
2307 if(db_fget(i, COUNTRY))
2308 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2313 text_write_address_uk(FILE *out, int i) {
2316 for(j = ADDRESS; j <= COUNTRY; j++)
2318 fprintf(out, "\n%s", db_fget(i, j));
2322 text_write_address_eu(FILE *out, int i) {
2323 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2325 if(db_fget(i, ADDRESS2))
2326 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2328 if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2331 if(db_fget(i, ZIP)) {
2332 fprintf(out, "%s", db_fget(i, ZIP));
2333 if(db_fget(i, CITY))
2337 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2340 if(db_fget(i, STATE))
2341 fprintf(out, "\n%s", db_fget(i, STATE));
2343 if(db_fget(i, COUNTRY))
2344 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2348 text_export_database(FILE * out, struct db_enumerator e)
2350 abook_list *emails, *em;
2352 char *realname = get_real_name(), *str = NULL, *tmp;
2353 char *style = opt_get_str(STR_ADDRESS_STYLE);
2356 "-----------------------------------------\n%s's address book\n"
2357 "-----------------------------------------\n\n\n",
2361 db_enumerate_items(e) {
2363 "-----------------------------------------\n\n");
2364 fprintf(out, "%s", db_name_get(e.item));
2365 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2366 fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2369 tmp = db_email_get(e.item);
2371 emails = csv_to_abook_list(tmp);
2374 for(em = emails; em; em = em->next)
2375 fprintf(out, "%s\n", em->data);
2377 abook_list_free(&emails);
2381 if(db_fget(e.item, ADDRESS)) {
2382 if(!safe_strcmp(style, "us")) /* US like */
2383 text_write_address_us(out, e.item);
2384 else if(!safe_strcmp(style, "uk")) /* UK like */
2385 text_write_address_uk(out, e.item);
2387 text_write_address_eu(out, e.item);
2392 if((db_fget(e.item, PHONE)) ||
2393 (db_fget(e.item, WORKPHONE)) ||
2394 (db_fget(e.item, FAX)) ||
2395 (db_fget(e.item, MOBILEPHONE))) {
2397 for(j = PHONE; j <= MOBILEPHONE; j++)
2398 if(db_fget(e.item, j)) {
2399 get_field_info(field_id(j),
2401 fprintf(out, "%s: %s\n", str,
2402 db_fget(e.item, j));
2406 if(db_fget(e.item, URL))
2407 fprintf(out, "\n%s\n", db_fget(e.item, URL));
2408 if(db_fget(e.item, NOTES))
2409 fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2414 fprintf(out, "-----------------------------------------\n");
2420 * end of printable export filter
2424 * elm alias export filter
2428 elm_alias_export(FILE *out, struct db_enumerator e)
2430 char email[MAX_EMAIL_LEN];
2433 db_enumerate_items(e) {
2434 alias = mutt_alias_genalias(e.item);
2435 get_first_email(email, e.item);
2436 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2444 * end of elm alias export filter
2449 * Spruce export filter
2453 spruce_export_database (FILE *out, struct db_enumerator e)
2455 char email[MAX_EMAIL_LEN];
2457 fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2459 db_enumerate_items(e) {
2460 get_first_email(email, e.item);
2461 if(strcmp(email, "")) {
2462 fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2464 db_name_get(e.item),
2466 safe_str(db_fget(e.item, NOTES))
2471 fprintf (out, "# End of address book file.\n");
2477 * end of Spruce export filter
2481 * wanderlust addressbook export filter
2485 wl_export_database(FILE *out, struct db_enumerator e)
2487 char email[MAX_EMAIL_LEN];
2489 fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2490 db_enumerate_items(e) {
2491 get_first_email(email, e.item);
2494 "%s\t\"%s\"\t\"%s\"\n",
2496 safe_str(db_fget(e.item, NICK)),
2497 safe_str(db_name_get(e.item))
2502 fprintf (out, "\n# End of address book file.\n");
2508 * end of wanderlust addressbook export filter
2512 * BSD calendar export filter
2516 bsdcal_export_database(FILE *out, struct db_enumerator e)
2518 db_enumerate_items(e) {
2519 int year, month = 0, day = 0;
2520 char *anniversary = db_fget(e.item, ANNIVERSARY);
2523 if(!parse_date_string(anniversary, &day, &month, &year))
2527 _("%02d/%02d\tAnniversary of %s\n"),
2530 safe_str(db_name_get(e.item))
2539 find_field_enum(char *s) {
2541 while(standard_fields[++i].key)
2542 if(!strcmp(standard_fields[i].key, s))
2547 /* Convert a string with named placeholders to
2548 a *printf() compatible string.
2549 Stores the abook field values into ft. */
2551 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2553 if(! fmt_string || ! ft) {
2554 fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2558 char tmp[1] = { 0 };
2559 char *p, *start, *field_name = NULL;
2566 if(! *start) goto cannotparse;
2567 p = strchr(start, '}');
2568 if(! p) goto cannotparse;
2569 strcat(fmt_string, "%s");
2570 field_name = strndup(start, (size_t)(p-start));
2571 *ft = find_field_enum(field_name);
2573 fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2581 else if(*p == '\\') {
2583 if(! *p) tmp[0] = '\\'; // last char is a '\' ?
2584 else if(*p == 'n') *tmp = '\n';
2585 else if(*p == 't') *tmp = '\t';
2586 else if(*p == 'r') *tmp = '\r';
2587 else if(*p == 'v') *tmp = '\v';
2588 else if(*p == 'b') *tmp = '\b';
2589 else if(*p == 'a') *tmp = '\a';
2591 strncat(fmt_string, tmp, 1);
2595 // if no '\' following: quick mode using strchr/strncat
2596 else if(! strchr(start, '\\')) {
2597 p = strchr(start, '{');
2598 if(p) { // copy until the next placeholder
2599 strncat(fmt_string, start, (size_t)(p-start));
2602 else { // copy till the end
2603 strncat( fmt_string,
2605 FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2610 // otherwise character by character
2612 strncat(fmt_string, p, 1);
2621 fprintf(stderr, _("%s: invalid format, index %ld\n"), __FUNCTION__, (start - s));
2623 while(*ft) free(ft--);
2628 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2631 // used to store the format string from --outformatstr when "custom" format is used
2632 // default value overriden in export_file()
2633 extern char *parsed_custom_format;
2634 extern enum field_types *custom_format_fields;
2636 /* wrapper for custom_export_item:
2637 1) avoid messing with extern pointer
2639 3) follow the prototype needed for an abook_output_item_filter entry */
2641 custom_print_item(FILE *out, int item)
2644 if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2649 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2653 // if the first character is '!':
2654 // we first check that all fields exist before continuing
2656 enum field_types *ftp = ft;
2657 while(*ft != ITEM_FIELDS) {
2658 if(! db_fget(item, *ft) )
2667 if(!strncmp(fmt, "%s", 2)) {
2668 fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2671 } else if (*ft == ITEM_FIELDS) {
2672 fprintf(out, "%s", fmt);
2675 p = strchr(fmt, '%');
2677 q = strndup(fmt, (size_t)(p-fmt));
2678 fprintf(out, "%s", q);
2683 fprintf(out, "%s", fmt);
2692 // used to store the format string from --outformatstr when "custom" format is used
2693 // default value overriden from abook.c
2694 extern char custom_format[FORMAT_STRING_LEN];
2697 custom_export_database(FILE *out, struct db_enumerator e)
2699 char *format_string =
2700 (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2702 enum field_types *ft =
2703 (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2705 parse_custom_format(custom_format, format_string, ft);
2707 db_enumerate_items(e) {
2708 if(custom_export_item(out, e.item, format_string, ft) == 0)
2715 * end of BSD calendar export filter