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];
1002 fprintf(out, "version: 1\n");
1004 db_enumerate_items(e) {
1007 get_first_email(email, e.item);
1010 tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email);
1011 /* TODO: this may not be enough for a trully "Distinguished" name
1012 needed by LDAP. Appending a random uuid could do the trick */
1014 tmp = strdup_printf("cn=%s",db_name_get(e.item));
1016 ldif_fput_type_and_value(out, "dn", tmp);
1019 for(j = 0; j < ITEM_FIELDS; j++) {
1021 if(*email) // don't dump an empty email field
1022 ldif_fput_type_and_value(out,
1023 ldif_field_names[j],
1026 else if(db_fget(e.item,j)) {
1027 ldif_fput_type_and_value(out,
1028 ldif_field_names[j],
1029 db_fget(e.item, j));
1033 fprintf(out, "objectclass: top\n"
1034 "objectclass: person\n\n");
1041 * end of ldif export filter
1045 * html export filter
1048 static void html_export_write_head(FILE *out);
1049 static void html_export_write_tail(FILE *out);
1051 extern struct index_elem *index_elements;
1054 html_print_emails(FILE *out, struct list_field *f)
1056 abook_list *l = csv_to_abook_list(f->data);
1058 for(; l; l = l->next) {
1059 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
1064 abook_list_free(&l);
1068 html_export_database(FILE *out, struct db_enumerator e)
1070 struct list_field f;
1071 struct index_elem *cur;
1078 html_export_write_head(out);
1080 db_enumerate_items(e) {
1081 fprintf(out, "<tr>");
1082 for(cur = index_elements; cur; cur = cur->next) {
1083 if(cur->type != INDEX_FIELD)
1086 get_list_field(e.item, cur, &f);
1088 if(f.type == FIELD_EMAILS) {
1089 fprintf(out, "<td>");
1090 html_print_emails(out, &f);
1091 fprintf(out, "</td>");
1094 fprintf(out, "<td>%s</td>", safe_str(f.data));
1097 fprintf(out, "</tr>\n");
1100 html_export_write_tail(out);
1106 html_export_write_head(FILE *out)
1108 char *realname = get_real_name(), *str;
1109 struct index_elem *cur;
1111 fprintf(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
1112 fprintf(out, "<html>\n<head>\n <title>%s's addressbook</title>",
1114 fprintf(out, "\n</head>\n<body>\n");
1115 fprintf(out, "\n<h2>%s's addressbook</h2>\n", realname );
1116 fprintf(out, "<br><br>\n\n");
1118 fprintf(out, "<table border=\"1\" align=\"center\">\n<tr>");
1119 for(cur = index_elements; cur; cur = cur->next) {
1120 if(cur->type != INDEX_FIELD)
1123 get_field_info(cur->d.field.id, NULL, &str, NULL);
1124 fprintf(out, "<th>%s</th>", str);
1126 fprintf(out, "</tr>\n\n");
1132 html_export_write_tail(FILE *out)
1134 fprintf(out, "\n</table>\n");
1135 fprintf(out, "\n</body>\n</html>\n");
1139 * end of html export filter
1144 * pine addressbook import filter
1147 #define PINE_BUF_SIZE 2048
1150 pine_fixbuf(char *buf)
1154 for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1155 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1159 pine_convert_emails(char *s)
1164 if(s == NULL || *s != '(')
1167 for(i = 0; s[i]; i++)
1170 if( ( tmp = strchr(s,')')) )
1173 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1174 if(i > MAX_LIST_ITEMS - 1) {
1182 pine_parse_buf(char *buf)
1187 char tmp[PINE_BUF_SIZE];
1189 int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1191 item = item_create();
1193 for(i=0, last=0; !last ; i++) {
1194 if( !(end = strchr(start, '\t')) )
1197 len = last ? strlen(start) : (int) (end-start);
1198 len = min(len, PINE_BUF_SIZE - 1);
1200 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1201 && pine_conv_table[i] >= 0) {
1202 strncpy(tmp, start, len);
1205 item_fput(item, pine_conv_table[i],
1211 pine_convert_emails(item_fget(item, EMAIL));
1212 add_item2database(item);
1217 #define LINESIZE 1024
1220 pine_parse_file(FILE *in)
1222 char line[LINESIZE];
1227 fgets(line, LINESIZE, in);
1231 buf = xrealloc(buf, i*LINESIZE);
1234 fgets(line, LINESIZE, in);
1236 if(*ptr != ' ' || feof(in))
1250 pine_parse_buf(buf);
1259 * end of pine addressbook import filter
1264 * pine addressbook export filter
1266 * filter doesn't wrap the lines as it should but Pine seems to handle
1267 * created files without problems - JH
1271 pine_export_database(FILE *out, struct db_enumerator e)
1275 db_enumerate_items(e) {
1276 emails = db_email_get(e.item);
1277 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1278 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1279 safe_str(db_fget(e.item, NICK)),
1280 safe_str(db_name_get(e.item)),
1282 safe_str(db_fget(e.item, NOTES))
1291 * end of pine addressbook export filter
1300 * these files should be parsed according to a certain
1301 * lay out, or the default if layout is not given, at
1302 * the moment only default is done...
1305 #define CSV_COMMENT_CHAR '#'
1306 #define CSV_DUPLICATE_SEPARATOR " "
1307 #define CSV_TABLE_SIZE(t) (sizeof (t) / sizeof *(t))
1309 static int csv_conv_table[] = {
1317 static int allcsv_conv_table[] = {
1336 static int palmcsv_conv_table[] = {
1337 NAME, /* Last name */
1338 NAME, /* First name */
1355 csv_convert_emails(char *s)
1363 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1364 if(i > MAX_LIST_ITEMS - 1) {
1372 csv_remove_quotes(char *s)
1374 char *copy, *trimmed;
1377 copy = trimmed = xstrdup(s);
1380 len = strlen(trimmed);
1381 if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1386 trimmed[len - 1] = 0;
1388 trimmed = xstrdup(trimmed);
1398 csv_field_to_item(int *table_base, size_t table_size, int field)
1400 if(field < table_size)
1401 return field_id(table_base[field]);
1407 csv_store_item(list_item item, int i, char *s)
1409 char *newstr = NULL;
1414 if( !(newstr = csv_remove_quotes(s)) )
1418 if (item[i] != NULL) {
1419 char *oldstr = item[i];
1421 item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1434 csv_is_valid_quote_end(char *p)
1442 else if(!ISSPACE(*p))
1450 csv_is_valid_quote_start(char *p)
1455 else if(!ISSPACE(*p))
1463 csv_parse_line(char *line, int *table_base, size_t table_size)
1467 bool in_quote = FALSE;
1470 item = item_create();
1472 for(p = start = line, field = 0; *p; p++) {
1474 if(csv_is_valid_quote_end(p))
1477 if ( (((p - start) / sizeof (char)) < 2 ) &&
1478 csv_is_valid_quote_start(p) )
1482 if(*p == ',' && !in_quote) {
1484 csv_store_item(item,
1485 csv_field_to_item(table_base,table_size,field),
1494 csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1497 csv_convert_emails(item_fget(item, EMAIL));
1498 add_item2database(item);
1503 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1508 line = getaline(in);
1510 if(line && *line && *line != CSV_COMMENT_CHAR)
1511 csv_parse_line(line, conv_table, table_size);
1520 csv_parse_file(FILE *in)
1522 return csv_parse_file_common(in, csv_conv_table,
1523 CSV_TABLE_SIZE(csv_conv_table));
1527 allcsv_parse_file(FILE *in)
1529 return csv_parse_file_common(in, allcsv_conv_table,
1530 CSV_TABLE_SIZE(allcsv_conv_table));
1534 palmcsv_parse_file(FILE *in)
1536 return csv_parse_file_common(in, palmcsv_conv_table,
1537 CSV_TABLE_SIZE(palmcsv_conv_table));
1541 * end of csv import filter
1545 * vCard import filter
1548 static char *vcard_fields[] = {
1549 "FN", /* FORMATTED NAME */
1550 "EMAIL", /* EMAIL */
1551 "ADR", /* ADDRESS */
1552 "ADR", /* ADDRESS2 - not used */
1556 "ADR", /* COUNTRY */
1558 "TEL", /* WORKPHONE */
1560 "TEL", /* MOBILEPHONE */
1561 "NICKNAME", /* NICK */
1564 "N", /* NAME: special case/mapping in vcard_parse_line() */
1565 NULL /* not implemented: ANNIVERSARY, ITEM_FIELDS */
1569 * mappings between vCard ADR field and abook's ADDRESS
1570 * see rfc2426 section 3.2.1
1572 static int vcard_address_fields[] = {
1573 -1, /* vCard(post office box) - not used */
1574 -1, /* vCard(the extended address) - not used */
1575 2, /* vCard(the street address) - ADDRESS */
1576 4, /* vCard(the locality) - CITY */
1577 5, /* vCard(the region) - STATE */
1578 6, /* vCard(the postal code) - ZIP */
1579 7 /* vCard(the country name) - COUNTRY */
1584 VCARD_KEY_ATTRIBUTE,
1589 vcard_get_line_element(char *line, int element)
1592 char *line_copy = 0;
1598 line_copy = xstrdup(line);
1600 /* change newline characters, if present, to end of string */
1601 for(i=0; line_copy[i]; i++) {
1602 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1603 line_copy[i] = '\0';
1608 /* separate key from value */
1609 for(i=0; line_copy[i]; i++) {
1610 if(line_copy[i] == ':') {
1611 line_copy[i] = '\0';
1613 value = &line_copy[i+1];
1618 /* separate key from key attributes */
1619 /* works for vCard 2 as well (automagically) */
1621 for(i=0; key[i]; i++) {
1624 key_attr = &key[i+1];
1633 result = xstrdup(key);
1635 case VCARD_KEY_ATTRIBUTE:
1637 result = xstrdup(key_attr);
1641 result = xstrdup(value);
1650 vcard_parse_email(list_item item, char *line)
1654 email = vcard_get_line_element(line, VCARD_VALUE);
1657 item[1] = strconcat(item[1], ",", email, 0);
1666 vcard_parse_address(list_item item, char *line)
1671 char *address_field;
1673 value = vcard_get_line_element(line, VCARD_VALUE);
1677 address_field = value;
1678 for(i=k=0; value[i]; i++) {
1679 if(value[i] == ';') {
1681 if(vcard_address_fields[k] >= 0) {
1682 item[vcard_address_fields[k]] = xstrdup(address_field);
1684 address_field = &value[i+1];
1686 if((k+1)==(sizeof(vcard_address_fields)/sizeof(*vcard_address_fields)))
1690 item[vcard_address_fields[k]] = xstrdup(address_field);
1695 vcard_parse_name(list_item item, char *line)
1697 // store the "N" field into "NAME" *if* no "FN:"
1698 // value has already been stored here
1702 item[0] = vcard_get_line_element(line, VCARD_VALUE);
1703 // "N:" can be multivalued => replace ';' separators by ' '
1704 while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1706 // http://www.daniweb.com/software-development/c/code/216919
1707 char *original = item[0], *p = original;
1710 if (*original != ' ' || trimmed) {
1711 trimmed = 1; *p++ = *original;
1713 } while(*original++);
1717 vcard_parse_phone(list_item item, char *line)
1719 char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1720 char *value = vcard_get_line_element(line, VCARD_VALUE);
1722 /* set the standard number */
1723 if (!type) item_fput(item, PHONE, value);
1726 * see rfc2426 section 3.3.1
1727 * Note: we probably support both vCard 2 and 3
1730 if (strcasestr(type, "home") != NULL)
1731 item_fput(item, PHONE, xstrdup(value));
1732 else if (strcasestr(type, "work") != NULL)
1733 item_fput(item, WORKPHONE, xstrdup(value));
1734 else if (strcasestr(type, "fax") != NULL)
1735 item_fput(item, FAX, xstrdup(value));
1736 else if (strcasestr(type, "cell") != NULL)
1737 item_fput(item, MOBILEPHONE, xstrdup(value));
1745 vcard_parse_line(list_item item, char *line)
1750 for(i=0; vcard_fields[i]; i++) {
1751 key = vcard_fields[i];
1753 if(0 == strncmp(key, line, strlen(key))) {
1754 if(0 == strcmp(key, "EMAIL"))
1755 vcard_parse_email(item, line);
1757 vcard_parse_address(item, line);
1758 else if(0 == strcmp(key, "TEL"))
1759 vcard_parse_phone(item, line);
1760 else if(0 == strcmp(key, "N"))
1761 vcard_parse_name(item, line);
1763 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1770 vcard_parse_item(FILE *in)
1773 list_item item = item_create();
1776 line = getaline(in);
1778 if(line && !strncmp("END:VCARD", line, 9)) {
1783 vcard_parse_line(item, line);
1788 add_item2database(item);
1793 vcard_parse_file(FILE *in)
1798 line = getaline(in);
1800 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1802 vcard_parse_item(in);
1813 * end of vCard import filter
1817 * csv addressbook export filters
1820 #define CSV_LAST (-1)
1821 #define CSV_UNDEFINED (-2)
1822 #define CSV_SPECIAL(X) (-3 - (X))
1823 #define CSV_IS_SPECIAL(X) ((X) <= -3)
1826 csv_export_common(FILE *out, struct db_enumerator e,
1827 int fields[], void (*special_func)(FILE *, int, int))
1831 db_enumerate_items(e) {
1832 for(i = 0; fields[i] != CSV_LAST; i++) {
1833 if(fields[i] == CSV_UNDEFINED)
1834 fprintf(out, "\"\"");
1835 else if(CSV_IS_SPECIAL(fields[i])) {
1837 (*special_func)(out, e.item, fields[i]);
1840 strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1841 strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1843 safe_str(database[e.item][field_idx(fields[i])])
1845 fprintf(out, "\"%s\"",
1846 safe_str(db_fget(e.item,fields[i])));
1848 if(fields[i + 1] != CSV_LAST)
1858 csv_export_database(FILE *out, struct db_enumerator e)
1860 int csv_export_fields[] = {
1869 csv_export_common(out, e, csv_export_fields, NULL);
1875 allcsv_export_database(FILE *out, struct db_enumerator e)
1878 * TODO: Should get these atomatically from abook_fileds
1881 int allcsv_export_fields[] = {
1903 fprintf(out, "\"NAME\",");
1904 fprintf(out, "\"EMAIL\",");
1905 fprintf(out, "\"ADDRESS\",");
1906 fprintf(out, "\"ADDRESS2\",");
1907 fprintf(out, "\"CITY\",");
1908 fprintf(out, "\"STATE\",");
1909 fprintf(out, "\"ZIP\",");
1910 fprintf(out, "\"COUNTRY\",");
1911 fprintf(out, "\"PHONE\",");
1912 fprintf(out, "\"WORKPHONE\",");
1913 fprintf(out, "\"FAX\",");
1914 fprintf(out, "\"MOBILEPHONE\",");
1915 fprintf(out, "\"NICK\",");
1916 fprintf(out, "\"URL\",");
1917 fprintf(out, "\"NOTES\",");
1918 fprintf(out, "\"ANNIVERSARY\",");
1919 fprintf(out, "\"GROUPS\"\n");
1921 csv_export_common(out, e, allcsv_export_fields, NULL);
1930 #define PALM_CSV_NAME CSV_SPECIAL(0)
1931 #define PALM_CSV_END CSV_SPECIAL(1)
1932 #define PALM_CSV_CAT CSV_SPECIAL(2)
1935 palm_split_and_write_name(FILE *out, char *name)
1941 if ( (p = strchr(name, ' ')) ) {
1945 fprintf(out, "\"%s\",\"" , p + 1);
1946 fwrite((void *)name, p - name, sizeof(char), out);
1949 fprintf(out, "\"%s\"", safe_str(name));
1954 palm_csv_handle_specials(FILE *out, int item, int field)
1958 palm_split_and_write_name(out, db_name_get(item));
1961 fprintf(out, "\"abook\"");
1964 fprintf(out, "\"0\"");
1972 palm_export_database(FILE *out, struct db_enumerator e)
1974 int palm_export_fields[] = {
1975 PALM_CSV_NAME, /* LASTNAME, FIRSTNAME */
1976 CSV_UNDEFINED, /* TITLE */
1977 CSV_UNDEFINED, /* COMPANY */
1978 WORKPHONE, /* WORK PHONE */
1979 PHONE, /* HOME PHONE */
1981 MOBILEPHONE, /* OTHER */
1983 ADDRESS, /* ADDRESS */
1987 COUNTRY, /* COUNTRY */
1988 NICK, /* DEFINED 1 */
1989 URL, /* DEFINED 2 */
1990 CSV_UNDEFINED, /* DEFINED 3 */
1991 CSV_UNDEFINED, /* DEFINED 4 */
1993 PALM_CSV_END, /* "0" */
1994 PALM_CSV_CAT, /* CATEGORY */
1998 csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
2004 * end of csv export filters
2008 * vCard 2 addressbook export filter
2012 vcard_export_database(FILE *out, struct db_enumerator e)
2014 db_enumerate_items(e)
2015 vcard_export_item(out, e.item);
2020 vcard_export_item(FILE *out, int item)
2024 abook_list *emails, *em;
2025 fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
2026 safe_str(db_name_get(item)));
2028 name = get_surname(db_name_get(item));
2029 for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
2030 if((db_name_get(item))[j] == ' ')
2033 fprintf(out, "N:%s;%.*s\r\n",
2036 safe_str(db_name_get(item))
2041 if(db_fget(item, ADDRESS))
2042 fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n",
2043 safe_str(db_fget(item, ADDRESS)),
2044 safe_str(db_fget(item, ADDRESS2)),
2045 safe_str(db_fget(item, CITY)),
2046 safe_str(db_fget(item, STATE)),
2047 safe_str(db_fget(item, ZIP)),
2048 safe_str(db_fget(item, COUNTRY))
2051 if(db_fget(item, PHONE))
2052 fprintf(out, "TEL;HOME:%s\r\n",
2053 db_fget(item, PHONE));
2054 if(db_fget(item, WORKPHONE))
2055 fprintf(out, "TEL;WORK:%s\r\n",
2056 db_fget(item, WORKPHONE));
2057 if(db_fget(item, FAX))
2058 fprintf(out, "TEL;FAX:%s\r\n",
2059 db_fget(item, FAX));
2060 if(db_fget(item, MOBILEPHONE))
2061 fprintf(out, "TEL;CELL:%s\r\n",
2062 db_fget(item, MOBILEPHONE));
2064 tmp = db_email_get(item);
2066 emails = csv_to_abook_list(tmp);
2068 for(em = emails; em; em = em->next)
2069 fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data);
2071 abook_list_free(&emails);
2075 if(db_fget(item, NOTES))
2076 fprintf(out, "NOTE:%s\r\n",
2077 db_fget(item, NOTES));
2078 if(db_fget(item, URL))
2079 fprintf(out, "URL:%s\r\n",
2080 db_fget(item, URL));
2082 fprintf(out, "END:VCARD\r\n\r\n");
2087 * end of vCard export filter
2092 * mutt alias export filter
2096 mutt_alias_genalias(int i)
2100 if(db_fget(i, NICK))
2101 return xstrdup(db_fget(i, NICK));
2103 tmp = xstrdup(db_name_get(i));
2105 if( ( pos = strchr(tmp, ' ') ) )
2114 * This function is a variant of abook_list_to_csv
2117 mutt_alias_gengroups(int i)
2119 char *groups, *res = NULL;
2120 char groupstr[7] = "-group ";
2121 abook_list *list, *tmp;
2123 groups = db_fget(i, GROUPS);
2128 list = csv_to_abook_list(groups);
2129 for(tmp = list; tmp; tmp = tmp->next) {
2131 res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2132 res = strcpy(res, groupstr);
2134 res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2136 strcat(res, groupstr);
2138 strcat(res, tmp->data);
2140 abook_list_free(&list);
2147 mutt_alias_export(FILE *out, struct db_enumerator e)
2149 char email[MAX_EMAIL_LEN];
2151 char *groups = NULL;
2152 int email_addresses;
2155 db_enumerate_items(e) {
2156 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2157 groups = (field_id(GROUPS) != -1) ? mutt_alias_gengroups(e.item) : NULL;
2158 get_first_email(email, e.item);
2160 /* do not output contacts without email address */
2161 /* cause this does not make sense in mutt aliases */
2164 /* output first email address */
2165 fprintf(out,"alias ");
2167 fprintf(out, "%s ", groups);
2169 fprintf(out, "%s ", alias);
2170 fprintf(out, "%s <%s>\n",
2171 db_name_get(e.item),
2174 /* number of email addresses */
2175 email_addresses = 1;
2176 ptr = db_email_get(e.item);
2177 while (*ptr != '\0') {
2184 /* output other email addresses */
2185 while (email_addresses-- > 1) {
2186 roll_emails(e.item, ROTATE_RIGHT);
2187 get_first_email(email, e.item);
2188 fprintf(out,"alias ");
2190 fprintf(out, "%s ", groups);
2192 fprintf(out, "%s__%s ", alias, email);
2194 fprintf(out, "%s__%s ", db_name_get(e.item), email);
2195 fprintf(out, "%s <%s>\n",
2196 db_name_get(e.item),
2199 roll_emails(e.item, ROTATE_RIGHT);
2208 void muttq_print_item(FILE *file, int item)
2210 abook_list *emails, *e;
2211 char *tmp = db_email_get(item);
2213 emails = csv_to_abook_list(tmp);
2216 for(e = emails; e; e = e->next) {
2217 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2218 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2220 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2223 abook_list_free(&emails);
2227 mutt_query_export_database(FILE *out, struct db_enumerator e)
2229 fprintf(out, "All items\n");
2230 db_enumerate_items(e)
2231 muttq_print_item(out, e.item);
2236 * end of mutt alias export filter
2241 * printable export filter
2246 text_write_address_us(FILE *out, int i) {
2247 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2249 if(db_fget(i, ADDRESS2))
2250 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2252 if(db_fget(i, CITY))
2253 fprintf(out, "\n%s", db_fget(i, CITY));
2255 if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2258 if(db_fget(i, STATE)) {
2259 fprintf(out, "%s", db_fget(i, STATE));
2265 fprintf(out, "%s", db_fget(i, ZIP));
2268 if(db_fget(i, COUNTRY))
2269 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2274 text_write_address_uk(FILE *out, int i) {
2277 for(j = ADDRESS; j <= COUNTRY; j++)
2279 fprintf(out, "\n%s", db_fget(i, j));
2283 text_write_address_eu(FILE *out, int i) {
2284 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2286 if(db_fget(i, ADDRESS2))
2287 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2289 if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2292 if(db_fget(i, ZIP)) {
2293 fprintf(out, "%s", db_fget(i, ZIP));
2294 if(db_fget(i, CITY))
2298 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2301 if(db_fget(i, STATE))
2302 fprintf(out, "\n%s", db_fget(i, STATE));
2304 if(db_fget(i, COUNTRY))
2305 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2309 text_export_database(FILE * out, struct db_enumerator e)
2311 abook_list *emails, *em;
2313 char *realname = get_real_name(), *str = NULL, *tmp;
2314 char *style = opt_get_str(STR_ADDRESS_STYLE);
2317 "-----------------------------------------\n%s's address book\n"
2318 "-----------------------------------------\n\n\n",
2322 db_enumerate_items(e) {
2324 "-----------------------------------------\n\n");
2325 fprintf(out, "%s", db_name_get(e.item));
2326 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2327 fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2330 tmp = db_email_get(e.item);
2332 emails = csv_to_abook_list(tmp);
2335 for(em = emails; em; em = em->next)
2336 fprintf(out, "%s\n", em->data);
2338 abook_list_free(&emails);
2342 if(db_fget(e.item, ADDRESS)) {
2343 if(!safe_strcmp(style, "us")) /* US like */
2344 text_write_address_us(out, e.item);
2345 else if(!safe_strcmp(style, "uk")) /* UK like */
2346 text_write_address_uk(out, e.item);
2348 text_write_address_eu(out, e.item);
2353 if((db_fget(e.item, PHONE)) ||
2354 (db_fget(e.item, WORKPHONE)) ||
2355 (db_fget(e.item, FAX)) ||
2356 (db_fget(e.item, MOBILEPHONE))) {
2358 for(j = PHONE; j <= MOBILEPHONE; j++)
2359 if(db_fget(e.item, j)) {
2360 get_field_info(field_id(j),
2362 fprintf(out, "%s: %s\n", str,
2363 db_fget(e.item, j));
2367 if(db_fget(e.item, URL))
2368 fprintf(out, "\n%s\n", db_fget(e.item, URL));
2369 if(db_fget(e.item, NOTES))
2370 fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2375 fprintf(out, "-----------------------------------------\n");
2381 * end of printable export filter
2385 * elm alias export filter
2389 elm_alias_export(FILE *out, struct db_enumerator e)
2391 char email[MAX_EMAIL_LEN];
2394 db_enumerate_items(e) {
2395 alias = mutt_alias_genalias(e.item);
2396 get_first_email(email, e.item);
2397 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2405 * end of elm alias export filter
2410 * Spruce export filter
2414 spruce_export_database (FILE *out, struct db_enumerator e)
2416 char email[MAX_EMAIL_LEN];
2418 fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2420 db_enumerate_items(e) {
2421 get_first_email(email, e.item);
2422 if(strcmp(email, "")) {
2423 fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2425 db_name_get(e.item),
2427 safe_str(db_fget(e.item, NOTES))
2432 fprintf (out, "# End of address book file.\n");
2438 * end of Spruce export filter
2442 * wanderlust addressbook export filter
2446 wl_export_database(FILE *out, struct db_enumerator e)
2448 char email[MAX_EMAIL_LEN];
2450 fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2451 db_enumerate_items(e) {
2452 get_first_email(email, e.item);
2455 "%s\t\"%s\"\t\"%s\"\n",
2457 safe_str(db_fget(e.item, NICK)),
2458 safe_str(db_name_get(e.item))
2463 fprintf (out, "\n# End of address book file.\n");
2469 * end of wanderlust addressbook export filter
2473 * BSD calendar export filter
2477 bsdcal_export_database(FILE *out, struct db_enumerator e)
2479 db_enumerate_items(e) {
2480 int year, month = 0, day = 0;
2481 char *anniversary = db_fget(e.item, ANNIVERSARY);
2484 if(!parse_date_string(anniversary, &day, &month, &year))
2488 _("%02d/%02d\tAnniversary of %s\n"),
2491 safe_str(db_name_get(e.item))
2499 // see enum field_types @database.h
2500 static char *conv_table[] = {
2521 find_field_enum(char *s) {
2523 while (conv_table[i]) {
2524 if(!safe_strcmp(conv_table[i], s))
2532 /* Convert a string with named placeholders to
2533 a *printf() compatible string.
2534 Stores the abook field values into ft. */
2536 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2538 if(! fmt_string || ! ft) {
2539 fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2543 char *p, *start, *field_name = NULL;
2549 p = strchr(start, '}');
2551 fprintf(stderr, _("parse_custom_format: invalid format\n"));
2554 strcat(fmt_string, "%s");
2555 field_name = strndup(start, (size_t)(p-start));
2556 *ft = find_field_enum(field_name);
2558 fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2566 p = strchr(start, '{');
2568 strncat(fmt_string, start, (size_t)(p-start));
2572 strncat( fmt_string,
2574 FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2583 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2586 // used to store the format string from --outformatstr when "custom" format is used
2587 // default value overriden in export_file()
2588 extern char *parsed_custom_format;
2589 extern enum field_types *custom_format_fields;
2591 /* wrapper for custom_export_item:
2592 1) avoid messing with extern pointer
2594 3) follow the prototype needed for an abook_output_item_filter entry */
2596 custom_print_item(FILE *out, int item)
2599 if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2604 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2608 // if the first character is '!':
2609 // we first check that all fields exist before continuing
2611 enum field_types *ftp = ft;
2613 if(! db_fget(item, *ft) )
2622 if(!strncmp(fmt, "%s", 2)) {
2623 fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2626 } else if (*ft == 66) {
2627 fprintf(out, "%s", fmt);
2630 p = strchr(fmt, '%');
2632 q = strndup(fmt, (size_t)(p-fmt));
2633 fprintf(out, "%s", q);
2638 fprintf(out, "%s", fmt);
2647 // used to store the format string from --outformatstr when "custom" format is used
2648 // default value overriden from abook.c
2649 extern char custom_format[FORMAT_STRING_LEN];
2652 custom_export_database(FILE *out, struct db_enumerator e)
2654 char *format_string =
2655 (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2657 enum field_types *ft =
2658 (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2660 parse_custom_format(custom_format, format_string, ft);
2662 db_enumerate_items(e) {
2663 if(custom_export_item(out, e.item, format_string, ft) == 0)
2670 * end of BSD calendar export filter