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;
40 * function declarations
44 * import filter prototypes
47 static int ldif_parse_file(FILE *handle);
48 static int mutt_parse_file(FILE *in);
49 static int pine_parse_file(FILE *in);
50 static int csv_parse_file(FILE *in);
51 static int allcsv_parse_file(FILE *in);
52 static int palmcsv_parse_file(FILE *in);
53 static int vcard_parse_file(FILE *in);
56 * export filter prototypes
59 static int ldif_export_database(FILE *out, struct db_enumerator e);
60 static int html_export_database(FILE *out, struct db_enumerator e);
61 static int pine_export_database(FILE *out, struct db_enumerator e);
62 static int csv_export_database(FILE *out, struct db_enumerator e);
63 static int allcsv_export_database(FILE *out, struct db_enumerator e);
64 static int palm_export_database(FILE *out, struct db_enumerator e);
65 static int vcard_export_database(FILE *out, struct db_enumerator e);
66 static int mutt_alias_export(FILE *out, struct db_enumerator e);
67 static int mutt_query_export_database(FILE *out, struct db_enumerator e);
68 static int elm_alias_export(FILE *out, struct db_enumerator e);
69 static int text_export_database(FILE *out, struct db_enumerator e);
70 static int spruce_export_database(FILE *out, struct db_enumerator e);
71 static int wl_export_database(FILE *out, struct db_enumerator e);
72 static int bsdcal_export_database(FILE *out, struct db_enumerator e);
73 static int custom_export_database(FILE *out, struct db_enumerator e);
76 * export filter item prototypes
79 void vcard_export_item(FILE *out, int item);
80 void muttq_print_item(FILE *file, int item);
81 void custom_print_item(FILE *out, int item);
84 * end of function declarations
87 struct abook_input_filter i_filters[] = {
88 { "abook", N_("abook native format"), parse_database },
89 { "ldif", N_("ldif / Netscape addressbook"), ldif_parse_file },
90 { "mutt", N_("mutt alias"), mutt_parse_file },
91 { "pine", N_("pine addressbook"), pine_parse_file },
92 { "csv", N_("comma separated values"), csv_parse_file },
93 { "allcsv", N_("comma separated values (all fields)"), allcsv_parse_file },
94 { "palmcsv", N_("Palm comma separated values"), palmcsv_parse_file },
95 { "vcard", N_("vCard file"), vcard_parse_file },
99 struct abook_output_filter e_filters[] = {
100 { "abook", N_("abook native format"), write_database },
101 { "ldif", N_("ldif / Netscape addressbook (.4ld)"), ldif_export_database },
102 { "vcard", N_("vCard 2 file"), vcard_export_database },
103 { "mutt", N_("mutt alias"), mutt_alias_export },
104 { "muttq", N_("mutt query format (internal use)"), mutt_query_export_database },
105 { "html", N_("html document"), html_export_database },
106 { "pine", N_("pine addressbook"), pine_export_database },
107 { "csv", N_("comma separated values"), csv_export_database },
108 { "allcsv", N_("comma separated values (all fields)"), allcsv_export_database },
109 { "palmcsv", N_("Palm comma separated values"), palm_export_database},
110 { "elm", N_("elm alias"), elm_alias_export },
111 { "text", N_("plain text"), text_export_database },
112 { "wl", N_("Wanderlust address book"), wl_export_database },
113 { "spruce", N_("Spruce address book"), spruce_export_database },
114 { "bsdcal", N_("BSD calendar"), bsdcal_export_database },
115 { "custom", N_("Custom format"), custom_export_database },
119 struct abook_output_item_filter u_filters[] = {
120 { "vcard", N_("vCard 2 file"), vcard_export_item },
121 { "muttq", N_("mutt alias"), muttq_print_item },
122 { "custom", N_("Custom format"), custom_print_item },
135 puts(_("input formats:"));
136 for(i=0; *i_filters[i].filtname ; i++)
137 printf("\t%s\t%s\n", i_filters[i].filtname,
138 gettext(i_filters[i].desc));
142 puts(_("output formats:"));
143 for(i=0; *e_filters[i].filtname ; i++)
144 printf("\t%s\t%s\n", e_filters[i].filtname,
145 gettext(e_filters[i].desc));
149 puts(_("query-compatible output formats:"));
150 for(i=0; *u_filters[i].filtname ; i++)
151 printf("\t%s\t%s\n", u_filters[i].filtname,
152 gettext(u_filters[i].desc));
158 number_of_output_filters()
162 for(i=0; *e_filters[i].filtname ; i++)
169 number_of_input_filters()
173 for(i=0; *i_filters[i].filtname ; i++)
182 char *username = getenv("USER");
183 struct passwd *pwent;
187 pwent = getpwnam(username);
189 if((tmp = xstrdup(pwent->pw_gecos)) == NULL)
190 return xstrdup(username);
192 rtn = sscanf(pwent->pw_gecos, "%[^,]", tmp);
193 if (rtn == EOF || rtn == 0) {
195 return xstrdup(username);
204 static int i_read_file(char *filename, int (*func) (FILE *in));
213 refresh_statusline();
214 headerline(_("import database"));
216 mvaddstr(3, 1, _("please select a filter"));
219 for(i=0; *i_filters[i].filtname ; i++)
220 mvprintw(5 + i, 6, "%c -\t%s\t%s\n", 'a' + i,
221 i_filters[i].filtname,
222 gettext(i_filters[i].desc));
224 mvprintw(6 + i, 6, _("x -\tcancel"));
232 int tmp = db_n_items();
236 filter = getch() - 'a';
237 if(filter == 'x' - 'a' ||
238 filter >= number_of_input_filters() || filter < 0) {
243 mvaddstr(5+filter, 2, "->");
245 filename = ask_filename(_("Filename: "));
251 if(i_read_file(filename, i_filters[filter].func ))
252 statusline_msg(_("Error occured while opening the file"));
253 else if(tmp == db_n_items())
254 statusline_msg(_("File does not seem to be a valid addressbook"));
265 i_read_file(char *filename, int (*func) (FILE *in))
270 if( (in = abook_fopen( filename, "r" )) == NULL )
281 import_file(char filtname[FILTNAME_LEN], char *filename)
284 int tmp = db_n_items();
288 if(! strncasecmp(i_filters[i].filtname, filtname,
291 if(! *i_filters[i].filtname) {
301 // this is a special case for
302 // libvformat whose API expects a filename
303 if(!strcmp(filtname, "vcard")) {
304 if(!strcmp(filename, "-"))
305 ret = vcard_parse_file_libvformat("/dev/stdin");
307 ret = vcard_parse_file_libvformat(filename);
312 if(!strcmp(filename, "-")) {
314 if((fstat(fileno(stdin), &s)) == -1 || S_ISDIR(s.st_mode))
317 ret = (*i_filters[i].func) (stdin);
319 ret = i_read_file(filename, i_filters[i].func);
321 if(tmp == db_n_items())
331 static int e_write_file(char *filename,
332 int (*func) (FILE *in, struct db_enumerator e), int mode);
342 refresh_statusline();
343 headerline(_("export database"));
345 mvaddstr(3, 1, _("please select a filter"));
348 for(i = 0; *e_filters[i].filtname ; i++)
349 mvprintw(5 + i, 6, "%c -\t%s\t%s\n", 'a' + i,
350 e_filters[i].filtname,
351 gettext(e_filters[i].desc));
353 mvprintw(6 + i, 6, _("x -\tcancel"));
360 int enum_mode = ENUM_ALL;
365 filter = getch() - 'a';
366 if(filter == 'x' - 'a' ||
367 filter >= number_of_output_filters() || filter < 0) {
372 mvaddstr(5 + filter, 2, "->");
374 if(selected_items()) {
375 switch(statusline_askchoice(
376 _("Export <a>ll, export <s>elected, or <c>ancel?"),
377 S_("keybindings:all/selected/cancel|asc"), 3)) {
381 enum_mode = ENUM_SELECTED;
391 filename = ask_filename(_("Filename: "));
397 if( e_write_file(filename, e_filters[filter].func, enum_mode))
398 statusline_msg(_("Error occured while exporting"));
406 struct abook_output_item_filter select_output_item_filter(char filtname[FILTNAME_LEN]) {
409 if(!strncasecmp(u_filters[i].filtname, filtname, FILTNAME_LEN))
411 if(!*u_filters[i].filtname) {
420 e_write_item(FILE *out, int item, void (*func) (FILE *in, int item))
426 e_write_file(char *filename, int (*func) (FILE *in, struct db_enumerator e),
431 struct db_enumerator enumerator = init_db_enumerator(mode);
433 if((out = fopen(filename, "a")) == NULL)
439 ret = (*func) (out, enumerator);
447 fexport(char filtname[FILTNAME_LEN], FILE *handle, int enum_mode)
450 struct db_enumerator e = init_db_enumerator(enum_mode);
453 if(!strncasecmp(e_filters[i].filtname, filtname,
456 if(!*e_filters[i].filtname) {
462 return (e_filters[i].func) (handle, e);
468 export_file(char filtname[FILTNAME_LEN], char *filename)
470 const int mode = ENUM_ALL;
473 struct db_enumerator e = init_db_enumerator(mode);
476 if(!strncasecmp(e_filters[i].filtname, filtname,
479 if(!*e_filters[i].filtname) {
488 if(!strcmp(filename, "-"))
489 ret = (e_filters[i].func) (stdout, e);
491 ret = e_write_file(filename, e_filters[i].func, mode);
497 * end of common functions
506 static void ldif_fix_string(char *str);
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_fix_string(value);
794 ldif_convert(item, type, value);
797 } while ( !feof(handle) );
799 // force registration (= ldif_add_item()) of the last LDIF entry
800 ldif_convert(item, "dn", "");
806 ldif_fix_string(char *str)
810 for(i = 0, j = 0; j < (int)strlen(str); i++, j++)
811 str[i] = ( str[j] == (char)0xc3 ?
812 (char) str[++j] + (char) 0x40 :
823 * mutt alias import filter
829 mutt_read_line(FILE *in, char **groups, char **alias, char **rest)
833 abook_list *glist = NULL;
835 if( !(line = ptr = getaline(in)) )
836 return 1; /* error / EOF */
840 if(strncmp("alias", ptr, 5)) {
848 /* If the group option is used, save the groups */
852 for(n_groups = 0; 0 == strncmp("-group", ptr, 6); n_groups++) {
858 abook_list_append(&glist,xstrndup(start, end - start));
862 if(n_groups && groups)
863 *groups = abook_list_to_csv(glist);
865 abook_list_free(&glist);
873 *alias = xstrndup(start, end - start);
876 *rest = xstrdup(ptr);
883 mutt_fix_quoting(char *p)
903 mutt_parse_email(list_item item)
905 char *line = item_fget(item, NAME);
913 mutt_fix_quoting(line);
914 tmp = strconcat("From: ", line, NULL);
915 getname(tmp, &name, &email);
919 item_fput(item, NAME, name);
924 item_fput(item, EMAIL, email);
929 * this is completely broken
932 while( (start = strchr(start, ',')) && i++ < MAX_EMAILS - 1) {
933 tmp = strconcat("From: ", ++start, NULL);
934 getname(tmp, &name, &email);
939 tmp = strconcat(item[EMAIL], ",", email, NULL);
951 mutt_parse_file(FILE *in)
953 list_item item = item_create();
956 memset(item, 0, fields_count * sizeof(char *));
958 if(!mutt_read_line(in,
959 (field_id(GROUPS) != -1) ? &item[field_id(GROUPS)] : NULL,
960 (field_id(NICK) != -1) ? &item[field_id(NICK)] : NULL,
961 &item[field_id(NAME)]) )
962 mutt_parse_email(item);
969 add_item2database(item);
977 * end of mutt alias import filter
986 ldif_fput_type_and_value(FILE *out,char *type, char *value )
990 tmp = ldif_type_and_value(type, value, strlen(value));
998 ldif_export_database(FILE *out, struct db_enumerator e)
1000 char email[MAX_EMAILSTR_LEN];
1001 abook_list *emails, *em;
1003 fprintf(out, "version: 1\n");
1005 db_enumerate_items(e) {
1008 get_first_email(email, e.item);
1011 tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email);
1012 /* TODO: this may not be enough for a trully "Distinguished" name
1013 needed by LDAP. Appending a random uuid could do the trick */
1015 tmp = strdup_printf("cn=%s",db_name_get(e.item));
1017 ldif_fput_type_and_value(out, "dn", tmp);
1020 for(j = 0; j < ITEM_FIELDS; j++) {
1023 tmp = db_email_get(e.item);
1024 emails = csv_to_abook_list(tmp);
1026 for(em = emails; em; em = em->next)
1027 ldif_fput_type_and_value(out,
1028 ldif_field_names[EMAIL],
1032 else if(db_fget(e.item,j)) {
1033 ldif_fput_type_and_value(out,
1034 ldif_field_names[j],
1035 db_fget(e.item, j));
1039 fprintf(out, "objectclass: top\n"
1040 "objectclass: person\n\n");
1047 * end of ldif export filter
1051 * html export filter
1054 static void html_export_write_head(FILE *out);
1055 static void html_export_write_tail(FILE *out);
1057 extern struct index_elem *index_elements;
1060 html_print_emails(FILE *out, struct list_field *f)
1062 abook_list *l = csv_to_abook_list(f->data);
1064 for(; l; l = l->next) {
1065 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
1070 abook_list_free(&l);
1074 html_export_database(FILE *out, struct db_enumerator e)
1076 struct list_field f;
1077 struct index_elem *cur;
1084 html_export_write_head(out);
1086 db_enumerate_items(e) {
1087 fprintf(out, "<tr>");
1088 for(cur = index_elements; cur; cur = cur->next) {
1089 if(cur->type != INDEX_FIELD)
1092 get_list_field(e.item, cur, &f);
1094 if(f.type == FIELD_EMAILS) {
1095 fprintf(out, "<td>");
1096 html_print_emails(out, &f);
1097 fprintf(out, "</td>");
1100 fprintf(out, "<td>%s</td>", safe_str(f.data));
1103 fprintf(out, "</tr>\n");
1106 html_export_write_tail(out);
1112 html_export_write_head(FILE *out)
1114 char *realname = get_real_name(), *str;
1115 struct index_elem *cur;
1117 fprintf(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
1118 fprintf(out, "<html>\n<head>\n <title>%s's addressbook</title>",
1120 fprintf(out, "\n</head>\n<body>\n");
1121 fprintf(out, "\n<h2>%s's addressbook</h2>\n", realname );
1122 fprintf(out, "<br><br>\n\n");
1124 fprintf(out, "<table border=\"1\" align=\"center\">\n<tr>");
1125 for(cur = index_elements; cur; cur = cur->next) {
1126 if(cur->type != INDEX_FIELD)
1129 get_field_info(cur->d.field.id, NULL, &str, NULL);
1130 fprintf(out, "<th>%s</th>", str);
1132 fprintf(out, "</tr>\n\n");
1138 html_export_write_tail(FILE *out)
1140 fprintf(out, "\n</table>\n");
1141 fprintf(out, "\n</body>\n</html>\n");
1145 * end of html export filter
1150 * pine addressbook import filter
1153 #define PINE_BUF_SIZE 2048
1156 pine_fixbuf(char *buf)
1160 for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1161 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1165 pine_convert_emails(char *s)
1170 if(s == NULL || *s != '(')
1173 for(i = 0; s[i]; i++)
1176 if( ( tmp = strchr(s,')')) )
1179 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1180 if(i > MAX_LIST_ITEMS - 1) {
1188 pine_parse_buf(char *buf)
1193 char tmp[PINE_BUF_SIZE];
1195 int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1197 item = item_create();
1199 for(i=0, last=0; !last ; i++) {
1200 if( !(end = strchr(start, '\t')) )
1203 len = last ? strlen(start) : (int) (end-start);
1204 len = min(len, PINE_BUF_SIZE - 1);
1206 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1207 && pine_conv_table[i] >= 0) {
1208 strncpy(tmp, start, len);
1211 item_fput(item, pine_conv_table[i],
1217 pine_convert_emails(item_fget(item, EMAIL));
1218 add_item2database(item);
1223 #define LINESIZE 1024
1226 pine_parse_file(FILE *in)
1228 char line[LINESIZE];
1233 fgets(line, LINESIZE, in);
1237 buf = xrealloc(buf, i*LINESIZE);
1240 fgets(line, LINESIZE, in);
1242 if(*ptr != ' ' || feof(in))
1256 pine_parse_buf(buf);
1265 * end of pine addressbook import filter
1270 * pine addressbook export filter
1272 * filter doesn't wrap the lines as it should but Pine seems to handle
1273 * created files without problems - JH
1277 pine_export_database(FILE *out, struct db_enumerator e)
1281 db_enumerate_items(e) {
1282 emails = db_email_get(e.item);
1283 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1284 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1285 safe_str(db_fget(e.item, NICK)),
1286 safe_str(db_name_get(e.item)),
1288 safe_str(db_fget(e.item, NOTES))
1297 * end of pine addressbook export filter
1306 * these files should be parsed according to a certain
1307 * lay out, or the default if layout is not given, at
1308 * the moment only default is done...
1311 #define CSV_COMMENT_CHAR '#'
1312 #define CSV_DUPLICATE_SEPARATOR " "
1313 #define CSV_TABLE_SIZE(t) (sizeof (t) / sizeof *(t))
1315 static int csv_conv_table[] = {
1323 static int allcsv_conv_table[] = {
1342 static int palmcsv_conv_table[] = {
1343 NAME, /* Last name */
1344 NAME, /* First name */
1361 csv_convert_emails(char *s)
1369 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1370 if(i > MAX_LIST_ITEMS - 1) {
1378 csv_remove_quotes(char *s)
1380 char *copy, *trimmed;
1383 copy = trimmed = xstrdup(s);
1386 len = strlen(trimmed);
1387 if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1392 trimmed[len - 1] = 0;
1394 trimmed = xstrdup(trimmed);
1404 csv_field_to_item(int *table_base, size_t table_size, int field)
1406 if(field < table_size)
1407 return field_id(table_base[field]);
1413 csv_store_item(list_item item, int i, char *s)
1415 char *newstr = NULL;
1420 if( !(newstr = csv_remove_quotes(s)) )
1424 if (item[i] != NULL) {
1425 char *oldstr = item[i];
1427 item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1440 csv_is_valid_quote_end(char *p)
1448 else if(!ISSPACE(*p))
1456 csv_is_valid_quote_start(char *p)
1461 else if(!ISSPACE(*p))
1469 csv_parse_line(char *line, int *table_base, size_t table_size)
1473 bool in_quote = FALSE;
1476 item = item_create();
1478 for(p = start = line, field = 0; *p; p++) {
1480 if(csv_is_valid_quote_end(p))
1483 if ( (((p - start) / sizeof (char)) < 2 ) &&
1484 csv_is_valid_quote_start(p) )
1488 if(*p == ',' && !in_quote) {
1490 csv_store_item(item,
1491 csv_field_to_item(table_base,table_size,field),
1500 csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1503 csv_convert_emails(item_fget(item, EMAIL));
1504 add_item2database(item);
1509 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1514 line = getaline(in);
1516 if(line && *line && *line != CSV_COMMENT_CHAR)
1517 csv_parse_line(line, conv_table, table_size);
1526 csv_parse_file(FILE *in)
1528 return csv_parse_file_common(in, csv_conv_table,
1529 CSV_TABLE_SIZE(csv_conv_table));
1533 allcsv_parse_file(FILE *in)
1535 return csv_parse_file_common(in, allcsv_conv_table,
1536 CSV_TABLE_SIZE(allcsv_conv_table));
1540 palmcsv_parse_file(FILE *in)
1542 return csv_parse_file_common(in, palmcsv_conv_table,
1543 CSV_TABLE_SIZE(palmcsv_conv_table));
1547 * end of csv import filter
1551 * vCard import filter
1554 static char *vcard_fields[] = {
1555 "FN", /* FORMATTED NAME */
1556 "EMAIL", /* EMAIL */
1557 "ADR", /* ADDRESS */
1558 "ADR", /* ADDRESS2 - not used */
1562 "ADR", /* COUNTRY */
1564 "TEL", /* WORKPHONE */
1566 "TEL", /* MOBILEPHONE */
1567 "NICKNAME", /* NICK */
1570 "N", /* NAME: special case/mapping in vcard_parse_line() */
1571 NULL /* not implemented: ANNIVERSARY, ITEM_FIELDS */
1575 * mappings between vCard ADR field and abook's ADDRESS
1576 * see rfc2426 section 3.2.1
1578 static int vcard_address_fields[] = {
1579 -1, /* vCard(post office box) - not used */
1580 -1, /* vCard(the extended address) - not used */
1581 2, /* vCard(the street address) - ADDRESS */
1582 4, /* vCard(the locality) - CITY */
1583 5, /* vCard(the region) - STATE */
1584 6, /* vCard(the postal code) - ZIP */
1585 7 /* vCard(the country name) - COUNTRY */
1590 VCARD_KEY_ATTRIBUTE,
1595 vcard_get_line_element(char *line, int element)
1598 char *line_copy = 0;
1604 line_copy = xstrdup(line);
1606 /* change newline characters, if present, to end of string */
1607 for(i=0; line_copy[i]; i++) {
1608 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1609 line_copy[i] = '\0';
1614 /* separate key from value */
1615 for(i=0; line_copy[i]; i++) {
1616 if(line_copy[i] == ':') {
1617 line_copy[i] = '\0';
1619 value = &line_copy[i+1];
1624 /* separate key from key attributes */
1625 /* works for vCard 2 as well (automagically) */
1627 for(i=0; key[i]; i++) {
1630 key_attr = &key[i+1];
1639 result = xstrdup(key);
1641 case VCARD_KEY_ATTRIBUTE:
1643 result = xstrdup(key_attr);
1647 result = xstrdup(value);
1656 vcard_parse_email(list_item item, char *line)
1660 email = vcard_get_line_element(line, VCARD_VALUE);
1663 item[1] = strconcat(item[1], ",", email, 0);
1672 vcard_parse_address(list_item item, char *line)
1677 char *address_field;
1679 value = vcard_get_line_element(line, VCARD_VALUE);
1683 address_field = value;
1684 for(i=k=0; value[i]; i++) {
1685 if(value[i] == ';') {
1687 if(vcard_address_fields[k] >= 0) {
1688 item[vcard_address_fields[k]] = xstrdup(address_field);
1690 address_field = &value[i+1];
1692 if((k+1)==(sizeof(vcard_address_fields)/sizeof(*vcard_address_fields)))
1696 item[vcard_address_fields[k]] = xstrdup(address_field);
1701 vcard_parse_name(list_item item, char *line)
1703 // store the "N" field into "NAME" *if* no "FN:"
1704 // value has already been stored here
1708 item[0] = vcard_get_line_element(line, VCARD_VALUE);
1709 // "N:" can be multivalued => replace ';' separators by ' '
1710 while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1712 // http://www.daniweb.com/software-development/c/code/216919
1713 char *original = item[0], *p = original;
1716 if (*original != ' ' || trimmed) {
1717 trimmed = 1; *p++ = *original;
1719 } while(*original++);
1723 vcard_parse_phone(list_item item, char *line)
1725 char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1726 char *value = vcard_get_line_element(line, VCARD_VALUE);
1728 /* set the standard number */
1729 if (!type) item_fput(item, PHONE, value);
1732 * see rfc2426 section 3.3.1
1733 * Note: we probably support both vCard 2 and 3
1736 if (strcasestr(type, "home") != NULL)
1737 item_fput(item, PHONE, xstrdup(value));
1738 else if (strcasestr(type, "work") != NULL)
1739 item_fput(item, WORKPHONE, xstrdup(value));
1740 else if (strcasestr(type, "fax") != NULL)
1741 item_fput(item, FAX, xstrdup(value));
1742 else if (strcasestr(type, "cell") != NULL)
1743 item_fput(item, MOBILEPHONE, xstrdup(value));
1751 vcard_parse_line(list_item item, char *line)
1756 for(i=0; vcard_fields[i]; i++) {
1757 key = vcard_fields[i];
1759 if(0 == strncmp(key, line, strlen(key))) {
1760 if(0 == strcmp(key, "EMAIL"))
1761 vcard_parse_email(item, line);
1763 vcard_parse_address(item, line);
1764 else if(0 == strcmp(key, "TEL"))
1765 vcard_parse_phone(item, line);
1766 else if(0 == strcmp(key, "N"))
1767 vcard_parse_name(item, line);
1769 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1776 vcard_parse_item(FILE *in)
1779 list_item item = item_create();
1782 line = getaline(in);
1784 if(line && !strncmp("END:VCARD", line, 9)) {
1789 vcard_parse_line(item, line);
1794 add_item2database(item);
1799 vcard_parse_file(FILE *in)
1804 line = getaline(in);
1806 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1808 vcard_parse_item(in);
1819 * end of vCard import filter
1823 * csv addressbook export filters
1826 #define CSV_LAST (-1)
1827 #define CSV_UNDEFINED (-2)
1828 #define CSV_SPECIAL(X) (-3 - (X))
1829 #define CSV_IS_SPECIAL(X) ((X) <= -3)
1832 csv_export_common(FILE *out, struct db_enumerator e,
1833 int fields[], void (*special_func)(FILE *, int, int))
1837 db_enumerate_items(e) {
1838 for(i = 0; fields[i] != CSV_LAST; i++) {
1839 if(fields[i] == CSV_UNDEFINED)
1840 fprintf(out, "\"\"");
1841 else if(CSV_IS_SPECIAL(fields[i])) {
1843 (*special_func)(out, e.item, fields[i]);
1846 strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1847 strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1849 safe_str(database[e.item][field_idx(fields[i])])
1851 fprintf(out, "\"%s\"",
1852 safe_str(db_fget(e.item,fields[i])));
1854 if(fields[i + 1] != CSV_LAST)
1864 csv_export_database(FILE *out, struct db_enumerator e)
1866 int csv_export_fields[] = {
1875 csv_export_common(out, e, csv_export_fields, NULL);
1881 allcsv_export_database(FILE *out, struct db_enumerator e)
1884 * TODO: Should get these atomatically from abook_fileds
1887 int allcsv_export_fields[] = {
1909 fprintf(out, "\"NAME\",");
1910 fprintf(out, "\"EMAIL\",");
1911 fprintf(out, "\"ADDRESS\",");
1912 fprintf(out, "\"ADDRESS2\",");
1913 fprintf(out, "\"CITY\",");
1914 fprintf(out, "\"STATE\",");
1915 fprintf(out, "\"ZIP\",");
1916 fprintf(out, "\"COUNTRY\",");
1917 fprintf(out, "\"PHONE\",");
1918 fprintf(out, "\"WORKPHONE\",");
1919 fprintf(out, "\"FAX\",");
1920 fprintf(out, "\"MOBILEPHONE\",");
1921 fprintf(out, "\"NICK\",");
1922 fprintf(out, "\"URL\",");
1923 fprintf(out, "\"NOTES\",");
1924 fprintf(out, "\"ANNIVERSARY\",");
1925 fprintf(out, "\"GROUPS\"\n");
1927 csv_export_common(out, e, allcsv_export_fields, NULL);
1936 #define PALM_CSV_NAME CSV_SPECIAL(0)
1937 #define PALM_CSV_END CSV_SPECIAL(1)
1938 #define PALM_CSV_CAT CSV_SPECIAL(2)
1941 palm_split_and_write_name(FILE *out, char *name)
1947 if ( (p = strchr(name, ' ')) ) {
1951 fprintf(out, "\"%s\",\"" , p + 1);
1952 fwrite((void *)name, p - name, sizeof(char), out);
1955 fprintf(out, "\"%s\"", safe_str(name));
1960 palm_csv_handle_specials(FILE *out, int item, int field)
1964 palm_split_and_write_name(out, db_name_get(item));
1967 fprintf(out, "\"abook\"");
1970 fprintf(out, "\"0\"");
1978 palm_export_database(FILE *out, struct db_enumerator e)
1980 int palm_export_fields[] = {
1981 PALM_CSV_NAME, /* LASTNAME, FIRSTNAME */
1982 CSV_UNDEFINED, /* TITLE */
1983 CSV_UNDEFINED, /* COMPANY */
1984 WORKPHONE, /* WORK PHONE */
1985 PHONE, /* HOME PHONE */
1987 MOBILEPHONE, /* OTHER */
1989 ADDRESS, /* ADDRESS */
1993 COUNTRY, /* COUNTRY */
1994 NICK, /* DEFINED 1 */
1995 URL, /* DEFINED 2 */
1996 CSV_UNDEFINED, /* DEFINED 3 */
1997 CSV_UNDEFINED, /* DEFINED 4 */
1999 PALM_CSV_END, /* "0" */
2000 PALM_CSV_CAT, /* CATEGORY */
2004 csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
2010 * end of csv export filters
2014 * vCard 2 addressbook export filter
2018 vcard_export_database(FILE *out, struct db_enumerator e)
2020 db_enumerate_items(e)
2021 vcard_export_item(out, e.item);
2026 vcard_export_item(FILE *out, int item)
2030 abook_list *emails, *em;
2031 fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
2032 safe_str(db_name_get(item)));
2034 name = get_surname(db_name_get(item));
2035 for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
2036 if((db_name_get(item))[j] == ' ')
2039 fprintf(out, "N:%s;%.*s\r\n",
2042 safe_str(db_name_get(item))
2047 if(db_fget(item, ADDRESS))
2048 fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n",
2049 safe_str(db_fget(item, ADDRESS)),
2050 safe_str(db_fget(item, ADDRESS2)),
2051 safe_str(db_fget(item, CITY)),
2052 safe_str(db_fget(item, STATE)),
2053 safe_str(db_fget(item, ZIP)),
2054 safe_str(db_fget(item, COUNTRY))
2057 if(db_fget(item, PHONE))
2058 fprintf(out, "TEL;HOME:%s\r\n",
2059 db_fget(item, PHONE));
2060 if(db_fget(item, WORKPHONE))
2061 fprintf(out, "TEL;WORK:%s\r\n",
2062 db_fget(item, WORKPHONE));
2063 if(db_fget(item, FAX))
2064 fprintf(out, "TEL;FAX:%s\r\n",
2065 db_fget(item, FAX));
2066 if(db_fget(item, MOBILEPHONE))
2067 fprintf(out, "TEL;CELL:%s\r\n",
2068 db_fget(item, MOBILEPHONE));
2070 tmp = db_email_get(item);
2072 emails = csv_to_abook_list(tmp);
2074 for(em = emails; em; em = em->next)
2075 fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data);
2077 abook_list_free(&emails);
2081 if(db_fget(item, NOTES))
2082 fprintf(out, "NOTE:%s\r\n",
2083 db_fget(item, NOTES));
2084 if(db_fget(item, URL))
2085 fprintf(out, "URL:%s\r\n",
2086 db_fget(item, URL));
2088 fprintf(out, "END:VCARD\r\n\r\n");
2093 * end of vCard export filter
2098 * mutt alias export filter
2102 mutt_alias_genalias(int i)
2106 if(db_fget(i, NICK))
2107 return xstrdup(db_fget(i, NICK));
2109 tmp = xstrdup(db_name_get(i));
2111 if( ( pos = strchr(tmp, ' ') ) )
2120 * This function is a variant of abook_list_to_csv
2123 mutt_alias_gengroups(int i)
2125 char *groups, *res = NULL;
2126 char groupstr[7] = "-group ";
2127 abook_list *list, *tmp;
2129 groups = db_fget(i, GROUPS);
2134 list = csv_to_abook_list(groups);
2135 for(tmp = list; tmp; tmp = tmp->next) {
2137 res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2138 res = strcpy(res, groupstr);
2140 res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2142 strcat(res, groupstr);
2144 strcat(res, tmp->data);
2146 abook_list_free(&list);
2153 mutt_alias_export(FILE *out, struct db_enumerator e)
2155 char email[MAX_EMAIL_LEN];
2157 char *groups = NULL;
2158 int email_addresses;
2161 db_enumerate_items(e) {
2162 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2163 groups = (field_id(GROUPS) != -1) ? mutt_alias_gengroups(e.item) : NULL;
2164 get_first_email(email, e.item);
2166 /* do not output contacts without email address */
2167 /* cause this does not make sense in mutt aliases */
2170 /* output first email address */
2171 fprintf(out,"alias ");
2173 fprintf(out, "%s ", groups);
2175 fprintf(out, "%s ", alias);
2176 fprintf(out, "%s <%s>\n",
2177 db_name_get(e.item),
2180 /* number of email addresses */
2181 email_addresses = 1;
2182 ptr = db_email_get(e.item);
2183 while (*ptr != '\0') {
2190 /* output other email addresses */
2191 while (email_addresses-- > 1) {
2192 roll_emails(e.item, ROTATE_RIGHT);
2193 get_first_email(email, e.item);
2194 fprintf(out,"alias ");
2196 fprintf(out, "%s ", groups);
2198 fprintf(out, "%s__%s ", alias, email);
2200 fprintf(out, "%s__%s ", db_name_get(e.item), email);
2201 fprintf(out, "%s <%s>\n",
2202 db_name_get(e.item),
2205 roll_emails(e.item, ROTATE_RIGHT);
2214 void muttq_print_item(FILE *file, int item)
2216 abook_list *emails, *e;
2217 char *tmp = db_email_get(item);
2219 emails = csv_to_abook_list(tmp);
2222 for(e = emails; e; e = e->next) {
2223 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2224 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2226 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2229 abook_list_free(&emails);
2233 mutt_query_export_database(FILE *out, struct db_enumerator e)
2235 fprintf(out, "All items\n");
2236 db_enumerate_items(e)
2237 muttq_print_item(out, e.item);
2242 * end of mutt alias export filter
2247 * printable export filter
2252 text_write_address_us(FILE *out, int i) {
2253 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2255 if(db_fget(i, ADDRESS2))
2256 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2258 if(db_fget(i, CITY))
2259 fprintf(out, "\n%s", db_fget(i, CITY));
2261 if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2264 if(db_fget(i, STATE)) {
2265 fprintf(out, "%s", db_fget(i, STATE));
2271 fprintf(out, "%s", db_fget(i, ZIP));
2274 if(db_fget(i, COUNTRY))
2275 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2280 text_write_address_uk(FILE *out, int i) {
2283 for(j = ADDRESS; j <= COUNTRY; j++)
2285 fprintf(out, "\n%s", db_fget(i, j));
2289 text_write_address_eu(FILE *out, int i) {
2290 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2292 if(db_fget(i, ADDRESS2))
2293 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2295 if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2298 if(db_fget(i, ZIP)) {
2299 fprintf(out, "%s", db_fget(i, ZIP));
2300 if(db_fget(i, CITY))
2304 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2307 if(db_fget(i, STATE))
2308 fprintf(out, "\n%s", db_fget(i, STATE));
2310 if(db_fget(i, COUNTRY))
2311 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2315 text_export_database(FILE * out, struct db_enumerator e)
2317 abook_list *emails, *em;
2319 char *realname = get_real_name(), *str = NULL, *tmp;
2320 char *style = opt_get_str(STR_ADDRESS_STYLE);
2323 "-----------------------------------------\n%s's address book\n"
2324 "-----------------------------------------\n\n\n",
2328 db_enumerate_items(e) {
2330 "-----------------------------------------\n\n");
2331 fprintf(out, "%s", db_name_get(e.item));
2332 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2333 fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2336 tmp = db_email_get(e.item);
2338 emails = csv_to_abook_list(tmp);
2341 for(em = emails; em; em = em->next)
2342 fprintf(out, "%s\n", em->data);
2344 abook_list_free(&emails);
2348 if(db_fget(e.item, ADDRESS)) {
2349 if(!safe_strcmp(style, "us")) /* US like */
2350 text_write_address_us(out, e.item);
2351 else if(!safe_strcmp(style, "uk")) /* UK like */
2352 text_write_address_uk(out, e.item);
2354 text_write_address_eu(out, e.item);
2359 if((db_fget(e.item, PHONE)) ||
2360 (db_fget(e.item, WORKPHONE)) ||
2361 (db_fget(e.item, FAX)) ||
2362 (db_fget(e.item, MOBILEPHONE))) {
2364 for(j = PHONE; j <= MOBILEPHONE; j++)
2365 if(db_fget(e.item, j)) {
2366 get_field_info(field_id(j),
2368 fprintf(out, "%s: %s\n", str,
2369 db_fget(e.item, j));
2373 if(db_fget(e.item, URL))
2374 fprintf(out, "\n%s\n", db_fget(e.item, URL));
2375 if(db_fget(e.item, NOTES))
2376 fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2381 fprintf(out, "-----------------------------------------\n");
2387 * end of printable export filter
2391 * elm alias export filter
2395 elm_alias_export(FILE *out, struct db_enumerator e)
2397 char email[MAX_EMAIL_LEN];
2400 db_enumerate_items(e) {
2401 alias = mutt_alias_genalias(e.item);
2402 get_first_email(email, e.item);
2403 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2411 * end of elm alias export filter
2416 * Spruce export filter
2420 spruce_export_database (FILE *out, struct db_enumerator e)
2422 char email[MAX_EMAIL_LEN];
2424 fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2426 db_enumerate_items(e) {
2427 get_first_email(email, e.item);
2428 if(strcmp(email, "")) {
2429 fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2431 db_name_get(e.item),
2433 safe_str(db_fget(e.item, NOTES))
2438 fprintf (out, "# End of address book file.\n");
2444 * end of Spruce export filter
2448 * wanderlust addressbook export filter
2452 wl_export_database(FILE *out, struct db_enumerator e)
2454 char email[MAX_EMAIL_LEN];
2456 fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2457 db_enumerate_items(e) {
2458 get_first_email(email, e.item);
2461 "%s\t\"%s\"\t\"%s\"\n",
2463 safe_str(db_fget(e.item, NICK)),
2464 safe_str(db_name_get(e.item))
2469 fprintf (out, "\n# End of address book file.\n");
2475 * end of wanderlust addressbook export filter
2479 * BSD calendar export filter
2483 bsdcal_export_database(FILE *out, struct db_enumerator e)
2485 db_enumerate_items(e) {
2486 int year, month = 0, day = 0;
2487 char *anniversary = db_fget(e.item, ANNIVERSARY);
2490 if(!parse_date_string(anniversary, &day, &month, &year))
2494 _("%02d/%02d\tAnniversary of %s\n"),
2497 safe_str(db_name_get(e.item))
2505 // see enum field_types @database.h
2506 static char *conv_table[] = {
2527 find_field_enum(char *s) {
2529 while (conv_table[i]) {
2530 if(!safe_strcmp(conv_table[i], s))
2538 /* Convert a string with named placeholders to
2539 a *printf() compatible string.
2540 Stores the abook field values into ft. */
2542 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2544 if(! fmt_string || ! ft) {
2545 fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2549 char *p, *start, *field_name = NULL;
2555 p = strchr(start, '}');
2557 fprintf(stderr, _("parse_custom_format: invalid format\n"));
2560 strcat(fmt_string, "%s");
2561 field_name = strndup(start, (size_t)(p-start));
2562 *ft = find_field_enum(field_name);
2564 fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2572 p = strchr(start, '{');
2574 strncat(fmt_string, start, (size_t)(p-start));
2578 strncat( fmt_string,
2580 FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2589 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2592 // used to store the format string from --outformatstr when "custom" format is used
2593 // default value overriden in export_file()
2594 extern char *parsed_custom_format;
2595 extern enum field_types *custom_format_fields;
2597 /* wrapper for custom_export_item:
2598 1) avoid messing with extern pointer
2600 3) follow the prototype needed for an abook_output_item_filter entry */
2602 custom_print_item(FILE *out, int item)
2605 if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2610 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2614 // if the first character is '!':
2615 // we first check that all fields exist before continuing
2617 enum field_types *ftp = ft;
2619 if(! db_fget(item, *ft) )
2628 if(!strncmp(fmt, "%s", 2)) {
2629 fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2632 } else if (*ft == 66) {
2633 fprintf(out, "%s", fmt);
2636 p = strchr(fmt, '%');
2638 q = strndup(fmt, (size_t)(p-fmt));
2639 fprintf(out, "%s", q);
2644 fprintf(out, "%s", fmt);
2653 // used to store the format string from --outformatstr when "custom" format is used
2654 // default value overriden from abook.c
2655 extern char custom_format[FORMAT_STRING_LEN];
2658 custom_export_database(FILE *out, struct db_enumerator e)
2660 char *format_string =
2661 (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2663 enum field_types *ft =
2664 (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2666 parse_custom_format(custom_format, format_string, ft);
2668 db_enumerate_items(e) {
2669 if(custom_export_item(out, e.item, format_string, ft) == 0)
2676 * end of BSD calendar export filter