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 /* During LDIF import we need more fields than the
507 ITEM_FIELDS of a *list_item. Eg: "objectclass"
508 to test valid records, ...
509 Here we extends the existing field_types enum
510 to define new fields indexes usable during processing.
511 Newly created LDIF attr names could be associated to
512 them using ldif_conv_table[]. */
514 LDIF_OBJECTCLASS = ITEM_FIELDS + 1
517 #define LDIF_ITEM_FIELDS LDIF_OBJECTCLASS
519 typedef char *ldif_item[LDIF_ITEM_FIELDS];
521 /* LDIF field's names *must* respect the ordering
522 defined by the field_types enum from database.h
523 This is only used to define (for export only)
524 abook standard field to LDIF attr name mappings */
525 static ldif_item ldif_field_names = {
528 "streetaddress", // ADDRESS
529 "streetaddress2", // ADDRESS2
533 "countryname", // COUNTRY
534 "homephone", // PHONE
535 "telephonenumber", // WORKPHONE
536 "facsimiletelephonenumber", // FAX
537 "cellphone", // MOBILEPHONE
538 "xmozillanickname", // NICK
540 "description", // NOTES
541 "anniversary", // ANNIVERSARY
545 /* Used to map LDIF attr names from input to
546 the abook restricted set of standard fields. */
550 } ldif_available_items;
552 static ldif_available_items ldif_conv_table[] = {
553 /* initial field names respect the field_types enum
554 from database.h but this is only for readability.
555 This ldif_item struct allow use to define multiple
556 LDIF field names ("attribute names") for one abook field */
560 {"streetaddress", ADDRESS},
561 {"streetaddress2", ADDRESS2},
565 {"countryname", COUNTRY},
566 {"homephone", PHONE},
567 {"telephonenumber", WORKPHONE}, // workphone, according to Mozilla
568 {"facsimiletelephonenumber", FAX},
569 {"cellphone", MOBILEPHONE},
570 {"mozillanickname", NICK},
572 {"description", NOTES},
573 {"anniversary", ANNIVERSARY},
574 {"ou", GROUPS}, // 16
576 // here comes a couple of aliases
577 {"mozillasecondemail", EMAIL},
581 {"xmozillaanyphone", WORKPHONE}, // ever used ?
582 {"workphone", WORKPHONE},
584 {"telexnumber", FAX},
585 {"mobilephone", MOBILEPHONE},
586 {"mobile", MOBILEPHONE},
587 {"xmozillanickname", NICK},
590 {"birthday", ANNIVERSARY},
591 {"category", GROUPS},
594 "sn": append to lastname ?
595 "surname": append to lastname ?
596 "givenname": prepend to firstname ? */
598 /* here starts dummy fields:
600 As long as additional indexes are created
601 (using the above ldif_field_types),
602 any other LDIF attr name can be added and
603 used during ldif entry processing.
604 But obviously fields > ITEM_FIELDS (database.h) won't be
605 copied into the final *list_item. */
607 /* - to avoid mistake, don't use the special ITEM_FIELDS value.
608 - see also: http://mxr.mozilla.org/comm-central/source/mailnews/addrbook/src/nsAbLDIFService.cpp */
610 // used to check valid LDIF records:
611 {"objectclass", LDIF_OBJECTCLASS}
613 const int LDIF_IMPORTABLE_ITEM_FIELDS = (int)sizeof(ldif_conv_table)/sizeof(*ldif_conv_table);
616 Handles multi-line strings.
617 If a string starts with a space, it's the continuation
618 of the previous line. Thus we need to always read ahead.
619 But for this to work with stdin, we need to stores the next
620 line for later use in case it's not a continuation of the
624 ldif_read_line(FILE *in, char **next_line)
630 // buf filled with the first line
634 buf = xstrdup(*next_line);
639 // if no line already read-ahead.
643 // this is not a continuation of what is already in buf
644 // store it for the next round
650 // starts with ' ': this is the continuation of buf
656 buf = strconcat(buf, ptr, NULL);
661 if(buf && *buf == '#' ) {
670 ldif_add_item(ldif_item li)
675 /* if there's no value for "objectclass"
676 it's probably a buggy record */
677 if(!li[LDIF_OBJECTCLASS])
680 /* just copy from our extended ldif_item to a regular
682 TODO: API could be changed so db_fput_byid() is usable */
683 item = item_create();
684 for(i=0; i < ITEM_FIELDS; i++) {
686 item[i] = xstrdup(li[i]);
689 add_item2database(item);
693 for(i=0; i < LDIF_ITEM_FIELDS; i++)
698 ldif_convert(ldif_item item, char *type, char *value)
700 /* this is the first (mandatory) attribute to expected
701 from a new valid LDIF record.
702 The previous record must be added to the database before
703 we can go further with the new one */
704 if(!strcmp(type, "dn")) {
711 for( i=0; i < LDIF_IMPORTABLE_ITEM_FIELDS; i++ ) {
713 if( *value && // there's a value for the attr provided
714 ldif_conv_table[i].key && // there exists an ldif attr name...
715 !strcasecmp(ldif_conv_table[i].key, type)) { // ...matching that provided at input
717 assert((i >= 0) && (i < LDIF_ITEM_FIELDS));
718 // which abook field this attribute's name maps to ?
719 index = ldif_conv_table[i].index;
720 assert((index >= 0) && (index < LDIF_ITEM_FIELDS));
722 /* TODO: here must be handled multi-valued cases
723 (first or latest win, append or prepend values, ...)
724 Currently: emails are appended, for other fields the
725 first attribute found wins.
726 Eg: the value of "mobile" will be taken into
727 account if such a line comes before "cellphone". */
729 /* Remember: LDIF_ITEM_FIELDS > ITEM_FIELDS,
730 lower (common) indexes of *ldif_item map well to *list_item.
731 We can use item_fput()... */
732 if(index < ITEM_FIELDS) {
733 // multiple email support, but two only will stay
734 // in the final *list_item
735 if(index == EMAIL && item_fget(item, EMAIL)) {
738 strconcat(item_fget(item, EMAIL), ",", value, 0));
741 /* Don't override already initialized fields:
742 This is the rule of the "first win" */
743 if(! item_fget(item, index))
744 item_fput(item, index, xstrdup(value));
748 /* ... but if the ldif field's name index is higher
749 than what standards abook fields struct can hold,
750 these extra indexes must be managed manually.
751 This is the case of LDIF_OBJECTCLASS ("objectclass" attr) */
753 item[index] = xstrdup(value);
756 // matching attr found and field filled:
757 // no further attr search is needed for `type`
764 ldif_parse_file(FILE *handle)
767 char *next_line = NULL;
771 /* This is our extended fields holder to put the values from
772 successfully parsed LDIF attributes.
773 ldif_item item is temporary. When the end of an entry is reached,
774 values are copied into a regular *list_item struct, see ldif_add_item() */
777 memset(item, 0, sizeof(item));
780 line = ldif_read_line(handle, &next_line);
782 // EOF or empty lines: continue;
783 if(!line || *line == '\0') continue;
785 if(-1 == (str_parse_line(line, &type, &value, &vlen))) {
787 continue; /* just skip the errors */
790 ldif_convert(item, type, value);
793 } while ( !feof(handle) );
795 // force registration (= ldif_add_item()) of the last LDIF entry
796 ldif_convert(item, "dn", "");
806 * mutt alias import filter
812 mutt_read_line(FILE *in, char **groups, char **alias, char **rest)
816 abook_list *glist = NULL;
818 if( !(line = ptr = getaline(in)) )
819 return 1; /* error / EOF */
823 if(strncmp("alias", ptr, 5)) {
831 /* If the group option is used, save the groups */
835 for(n_groups = 0; 0 == strncmp("-group", ptr, 6); n_groups++) {
841 abook_list_append(&glist,xstrndup(start, end - start));
845 if(n_groups && groups)
846 *groups = abook_list_to_csv(glist);
848 abook_list_free(&glist);
856 *alias = xstrndup(start, end - start);
859 *rest = xstrdup(ptr);
866 mutt_fix_quoting(char *p)
886 mutt_parse_email(list_item item)
888 char *line = item_fget(item, NAME);
896 mutt_fix_quoting(line);
897 tmp = strconcat("From: ", line, NULL);
898 getname(tmp, &name, &email);
902 item_fput(item, NAME, name);
907 item_fput(item, EMAIL, email);
912 * this is completely broken
915 while( (start = strchr(start, ',')) && i++ < MAX_EMAILS - 1) {
916 tmp = strconcat("From: ", ++start, NULL);
917 getname(tmp, &name, &email);
922 tmp = strconcat(item[EMAIL], ",", email, NULL);
934 mutt_parse_file(FILE *in)
936 list_item item = item_create();
939 memset(item, 0, fields_count * sizeof(char *));
941 if(!mutt_read_line(in,
942 (field_id(GROUPS) != -1) ? &item[field_id(GROUPS)] : NULL,
943 (field_id(NICK) != -1) ? &item[field_id(NICK)] : NULL,
944 &item[field_id(NAME)]) )
945 mutt_parse_email(item);
952 add_item2database(item);
960 * end of mutt alias import filter
969 ldif_fput_type_and_value(FILE *out,char *type, char *value )
973 tmp = ldif_type_and_value(type, value, strlen(value));
981 ldif_export_database(FILE *out, struct db_enumerator e)
983 char email[MAX_EMAILSTR_LEN];
984 abook_list *emails, *em;
986 fprintf(out, "version: 1\n");
988 db_enumerate_items(e) {
991 get_first_email(email, e.item);
994 tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email);
995 /* TODO: this may not be enough for a trully "Distinguished" name
996 needed by LDAP. Appending a random uuid could do the trick */
998 tmp = strdup_printf("cn=%s",db_name_get(e.item));
1000 ldif_fput_type_and_value(out, "dn", tmp);
1003 for(j = 0; j < ITEM_FIELDS; j++) {
1006 tmp = db_email_get(e.item);
1007 emails = csv_to_abook_list(tmp);
1009 for(em = emails; em; em = em->next)
1010 ldif_fput_type_and_value(out,
1011 ldif_field_names[EMAIL],
1015 else if(db_fget(e.item,j)) {
1016 ldif_fput_type_and_value(out,
1017 ldif_field_names[j],
1018 db_fget(e.item, j));
1022 fprintf(out, "objectclass: top\n"
1023 "objectclass: person\n\n");
1030 * end of ldif export filter
1034 * html export filter
1037 static void html_export_write_head(FILE *out);
1038 static void html_export_write_tail(FILE *out);
1040 extern struct index_elem *index_elements;
1043 html_print_emails(FILE *out, struct list_field *f)
1045 abook_list *l = csv_to_abook_list(f->data);
1047 for(; l; l = l->next) {
1048 fprintf(out, "<a href=\"mailto:%s\">%s</a>", l->data, l->data);
1053 abook_list_free(&l);
1057 html_export_database(FILE *out, struct db_enumerator e)
1059 struct list_field f;
1060 struct index_elem *cur;
1067 html_export_write_head(out);
1069 db_enumerate_items(e) {
1070 fprintf(out, "<tr>");
1071 for(cur = index_elements; cur; cur = cur->next) {
1072 if(cur->type != INDEX_FIELD)
1075 get_list_field(e.item, cur, &f);
1077 if(f.type == FIELD_EMAILS) {
1078 fprintf(out, "<td>");
1079 html_print_emails(out, &f);
1080 fprintf(out, "</td>");
1083 fprintf(out, "<td>%s</td>", safe_str(f.data));
1086 fprintf(out, "</tr>\n");
1089 html_export_write_tail(out);
1095 html_export_write_head(FILE *out)
1097 char *realname = get_real_name(), *str;
1098 struct index_elem *cur;
1100 fprintf(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
1101 fprintf(out, "<html>\n<head>\n <title>%s's addressbook</title>",
1103 fprintf(out, "\n</head>\n<body>\n");
1104 fprintf(out, "\n<h2>%s's addressbook</h2>\n", realname );
1105 fprintf(out, "<br><br>\n\n");
1107 fprintf(out, "<table border=\"1\" align=\"center\">\n<tr>");
1108 for(cur = index_elements; cur; cur = cur->next) {
1109 if(cur->type != INDEX_FIELD)
1112 get_field_info(cur->d.field.id, NULL, &str, NULL);
1113 fprintf(out, "<th>%s</th>", str);
1115 fprintf(out, "</tr>\n\n");
1121 html_export_write_tail(FILE *out)
1123 fprintf(out, "\n</table>\n");
1124 fprintf(out, "\n</body>\n</html>\n");
1128 * end of html export filter
1133 * pine addressbook import filter
1136 #define PINE_BUF_SIZE 2048
1139 pine_fixbuf(char *buf)
1143 for(i = 0,j = 0; j < (int)strlen(buf); i++, j++)
1144 buf[i] = buf[j] == '\n' ? buf[++j] : buf[j];
1148 pine_convert_emails(char *s)
1153 if(s == NULL || *s != '(')
1156 for(i = 0; s[i]; i++)
1159 if( ( tmp = strchr(s,')')) )
1162 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1163 if(i > MAX_LIST_ITEMS - 1) {
1171 pine_parse_buf(char *buf)
1176 char tmp[PINE_BUF_SIZE];
1178 int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES};
1180 item = item_create();
1182 for(i=0, last=0; !last ; i++) {
1183 if( !(end = strchr(start, '\t')) )
1186 len = last ? strlen(start) : (int) (end-start);
1187 len = min(len, PINE_BUF_SIZE - 1);
1189 if(i < (int)(sizeof(pine_conv_table) / sizeof(*pine_conv_table))
1190 && pine_conv_table[i] >= 0) {
1191 strncpy(tmp, start, len);
1194 item_fput(item, pine_conv_table[i],
1200 pine_convert_emails(item_fget(item, EMAIL));
1201 add_item2database(item);
1206 #define LINESIZE 1024
1209 pine_parse_file(FILE *in)
1211 char line[LINESIZE];
1216 fgets(line, LINESIZE, in);
1220 buf = xrealloc(buf, i*LINESIZE);
1223 fgets(line, LINESIZE, in);
1225 if(*ptr != ' ' || feof(in))
1239 pine_parse_buf(buf);
1248 * end of pine addressbook import filter
1253 * pine addressbook export filter
1255 * filter doesn't wrap the lines as it should but Pine seems to handle
1256 * created files without problems - JH
1260 pine_export_database(FILE *out, struct db_enumerator e)
1264 db_enumerate_items(e) {
1265 emails = db_email_get(e.item);
1266 fprintf(out, strchr(emails, ',') /* multiple addresses? */ ?
1267 "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n",
1268 safe_str(db_fget(e.item, NICK)),
1269 safe_str(db_name_get(e.item)),
1271 safe_str(db_fget(e.item, NOTES))
1280 * end of pine addressbook export filter
1289 * these files should be parsed according to a certain
1290 * lay out, or the default if layout is not given, at
1291 * the moment only default is done...
1294 #define CSV_COMMENT_CHAR '#'
1295 #define CSV_DUPLICATE_SEPARATOR " "
1296 #define CSV_TABLE_SIZE(t) (sizeof (t) / sizeof *(t))
1298 static int csv_conv_table[] = {
1306 static int allcsv_conv_table[] = {
1325 static int palmcsv_conv_table[] = {
1326 NAME, /* Last name */
1327 NAME, /* First name */
1344 csv_convert_emails(char *s)
1352 for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1)
1353 if(i > MAX_LIST_ITEMS - 1) {
1361 csv_remove_quotes(char *s)
1363 char *copy, *trimmed;
1366 copy = trimmed = xstrdup(s);
1369 len = strlen(trimmed);
1370 if(trimmed[len - 1] == '\"' && *trimmed == '\"') {
1375 trimmed[len - 1] = 0;
1377 trimmed = xstrdup(trimmed);
1387 csv_field_to_item(int *table_base, size_t table_size, int field)
1389 if(field < table_size)
1390 return field_id(table_base[field]);
1396 csv_store_item(list_item item, int i, char *s)
1398 char *newstr = NULL;
1403 if( !(newstr = csv_remove_quotes(s)) )
1407 if (item[i] != NULL) {
1408 char *oldstr = item[i];
1410 item[i] = strconcat(newstr, CSV_DUPLICATE_SEPARATOR,
1423 csv_is_valid_quote_end(char *p)
1431 else if(!ISSPACE(*p))
1439 csv_is_valid_quote_start(char *p)
1444 else if(!ISSPACE(*p))
1452 csv_parse_line(char *line, int *table_base, size_t table_size)
1456 bool in_quote = FALSE;
1459 item = item_create();
1461 for(p = start = line, field = 0; *p; p++) {
1463 if(csv_is_valid_quote_end(p))
1466 if ( (((p - start) / sizeof (char)) < 2 ) &&
1467 csv_is_valid_quote_start(p) )
1471 if(*p == ',' && !in_quote) {
1473 csv_store_item(item,
1474 csv_field_to_item(table_base,table_size,field),
1483 csv_store_item(item, csv_field_to_item(table_base, table_size, field),
1486 csv_convert_emails(item_fget(item, EMAIL));
1487 add_item2database(item);
1492 csv_parse_file_common(FILE *in, int *conv_table, size_t table_size)
1497 line = getaline(in);
1499 if(line && *line && *line != CSV_COMMENT_CHAR)
1500 csv_parse_line(line, conv_table, table_size);
1509 csv_parse_file(FILE *in)
1511 return csv_parse_file_common(in, csv_conv_table,
1512 CSV_TABLE_SIZE(csv_conv_table));
1516 allcsv_parse_file(FILE *in)
1518 return csv_parse_file_common(in, allcsv_conv_table,
1519 CSV_TABLE_SIZE(allcsv_conv_table));
1523 palmcsv_parse_file(FILE *in)
1525 return csv_parse_file_common(in, palmcsv_conv_table,
1526 CSV_TABLE_SIZE(palmcsv_conv_table));
1530 * end of csv import filter
1534 * vCard import filter
1537 static char *vcard_fields[] = {
1538 "FN", /* FORMATTED NAME */
1539 "EMAIL", /* EMAIL */
1540 "ADR", /* ADDRESS */
1541 "ADR", /* ADDRESS2 */
1545 "ADR", /* COUNTRY */
1547 "TEL", /* WORKPHONE */
1549 "TEL", /* MOBILEPHONE */
1550 "NICKNAME", /* NICK */
1553 "N", /* NAME: special case/mapping in vcard_parse_line() */
1554 NULL /* not implemented: ANNIVERSARY, ITEM_FIELDS */
1559 VCARD_KEY_ATTRIBUTE,
1564 vcard_get_line_element(char *line, int element)
1567 char *line_copy = 0;
1573 line_copy = xstrdup(line);
1575 /* change newline characters, if present, to end of string */
1576 for(i=0; line_copy[i]; i++) {
1577 if(line_copy[i] == '\r' || line_copy[i] == '\n') {
1578 line_copy[i] = '\0';
1583 /* separate key from value */
1584 for(i=0; line_copy[i]; i++) {
1585 if(line_copy[i] == ':') {
1586 line_copy[i] = '\0';
1588 value = &line_copy[i+1];
1593 /* separate key from key attributes */
1594 /* works for vCard 2 as well (automagically) */
1596 for(i=0; key[i]; i++) {
1599 key_attr = &key[i+1];
1608 result = xstrdup(key);
1610 case VCARD_KEY_ATTRIBUTE:
1612 result = xstrdup(key_attr);
1616 result = xstrdup(value);
1625 vcard_parse_email(list_item item, char *line)
1629 email = vcard_get_line_element(line, VCARD_VALUE);
1632 item[1] = strconcat(item[1], ",", email, 0);
1642 * mappings between vCard ADR field and abook's ADDRESS
1643 * see rfc2426 section 3.2.1
1646 vcard_parse_address(list_item item, char *line)
1650 value = vcard_get_line_element(line, VCARD_VALUE);
1654 // vCard(post office box) - not used
1655 strsep(&value, ";");
1656 // vCard(the extended address)
1657 item_fput(item, ADDRESS2, xstrdup(strsep(&value, ";")));
1658 // vCard(the street address)
1659 item_fput(item, ADDRESS, xstrdup(strsep(&value, ";")));
1660 // vCard(the locality)
1661 item_fput(item, CITY, xstrdup(strsep(&value, ";")));
1662 // vCard(the region)
1663 item_fput(item, STATE, xstrdup(strsep(&value, ";")));
1664 // vCard(the postal code)
1665 item_fput(item, ZIP, xstrdup(strsep(&value, ";")));
1666 // vCard(the country name)
1667 item_fput(item, COUNTRY, xstrdup(strsep(&value, ";")));
1669 if(*value) xfree(value);
1673 vcard_parse_name(list_item item, char *line)
1675 // store the "N" field into "NAME" *if* no "FN:"
1676 // value has already been stored here
1680 item[0] = vcard_get_line_element(line, VCARD_VALUE);
1681 // "N:" can be multivalued => replace ';' separators by ' '
1682 while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' ';
1684 // http://www.daniweb.com/software-development/c/code/216919
1685 char *original = item[0], *p = original;
1688 if (*original != ' ' || trimmed) {
1689 trimmed = 1; *p++ = *original;
1691 } while(*original++);
1695 vcard_parse_phone(list_item item, char *line)
1697 char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE);
1698 char *value = vcard_get_line_element(line, VCARD_VALUE);
1700 /* set the standard number */
1701 if (!type) item_fput(item, PHONE, value);
1704 * see rfc2426 section 3.3.1
1705 * Note: we probably support both vCard 2 and 3
1708 if (strcasestr(type, "home") != NULL)
1709 item_fput(item, PHONE, xstrdup(value));
1710 else if (strcasestr(type, "work") != NULL)
1711 item_fput(item, WORKPHONE, xstrdup(value));
1712 else if (strcasestr(type, "fax") != NULL)
1713 item_fput(item, FAX, xstrdup(value));
1714 else if (strcasestr(type, "cell") != NULL)
1715 item_fput(item, MOBILEPHONE, xstrdup(value));
1723 vcard_parse_line(list_item item, char *line)
1728 for(i=0; vcard_fields[i]; i++) {
1729 key = vcard_fields[i];
1731 if(0 == strncmp(key, line, strlen(key))) {
1732 if(0 == strcmp(key, "EMAIL"))
1733 vcard_parse_email(item, line);
1735 vcard_parse_address(item, line);
1736 else if(0 == strcmp(key, "TEL"))
1737 vcard_parse_phone(item, line);
1738 else if(0 == strcmp(key, "N"))
1739 vcard_parse_name(item, line);
1741 item[i] = vcard_get_line_element(line, VCARD_VALUE);
1748 vcard_parse_item(FILE *in)
1751 list_item item = item_create();
1754 line = getaline(in);
1756 if(line && !strncmp("END:VCARD", line, 9)) {
1761 vcard_parse_line(item, line);
1766 add_item2database(item);
1771 vcard_parse_file(FILE *in)
1776 line = getaline(in);
1778 if(line && !strncmp("BEGIN:VCARD", line, 11)) {
1780 vcard_parse_item(in);
1791 * end of vCard import filter
1795 * csv addressbook export filters
1798 #define CSV_LAST (-1)
1799 #define CSV_UNDEFINED (-2)
1800 #define CSV_SPECIAL(X) (-3 - (X))
1801 #define CSV_IS_SPECIAL(X) ((X) <= -3)
1804 csv_export_common(FILE *out, struct db_enumerator e,
1805 int fields[], void (*special_func)(FILE *, int, int))
1809 db_enumerate_items(e) {
1810 for(i = 0; fields[i] != CSV_LAST; i++) {
1811 if(fields[i] == CSV_UNDEFINED)
1812 fprintf(out, "\"\"");
1813 else if(CSV_IS_SPECIAL(fields[i])) {
1815 (*special_func)(out, e.item, fields[i]);
1818 strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
1819 strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
1821 safe_str(database[e.item][field_idx(fields[i])])
1823 fprintf(out, "\"%s\"",
1824 safe_str(db_fget(e.item,fields[i])));
1826 if(fields[i + 1] != CSV_LAST)
1836 csv_export_database(FILE *out, struct db_enumerator e)
1838 int csv_export_fields[] = {
1847 csv_export_common(out, e, csv_export_fields, NULL);
1853 allcsv_export_database(FILE *out, struct db_enumerator e)
1856 * TODO: Should get these atomatically from abook_fileds
1859 int allcsv_export_fields[] = {
1881 fprintf(out, "\"NAME\",");
1882 fprintf(out, "\"EMAIL\",");
1883 fprintf(out, "\"ADDRESS\",");
1884 fprintf(out, "\"ADDRESS2\",");
1885 fprintf(out, "\"CITY\",");
1886 fprintf(out, "\"STATE\",");
1887 fprintf(out, "\"ZIP\",");
1888 fprintf(out, "\"COUNTRY\",");
1889 fprintf(out, "\"PHONE\",");
1890 fprintf(out, "\"WORKPHONE\",");
1891 fprintf(out, "\"FAX\",");
1892 fprintf(out, "\"MOBILEPHONE\",");
1893 fprintf(out, "\"NICK\",");
1894 fprintf(out, "\"URL\",");
1895 fprintf(out, "\"NOTES\",");
1896 fprintf(out, "\"ANNIVERSARY\",");
1897 fprintf(out, "\"GROUPS\"\n");
1899 csv_export_common(out, e, allcsv_export_fields, NULL);
1908 #define PALM_CSV_NAME CSV_SPECIAL(0)
1909 #define PALM_CSV_END CSV_SPECIAL(1)
1910 #define PALM_CSV_CAT CSV_SPECIAL(2)
1913 palm_split_and_write_name(FILE *out, char *name)
1919 if ( (p = strchr(name, ' ')) ) {
1923 fprintf(out, "\"%s\",\"" , p + 1);
1924 fwrite((void *)name, p - name, sizeof(char), out);
1927 fprintf(out, "\"%s\"", safe_str(name));
1932 palm_csv_handle_specials(FILE *out, int item, int field)
1936 palm_split_and_write_name(out, db_name_get(item));
1939 fprintf(out, "\"abook\"");
1942 fprintf(out, "\"0\"");
1950 palm_export_database(FILE *out, struct db_enumerator e)
1952 int palm_export_fields[] = {
1953 PALM_CSV_NAME, /* LASTNAME, FIRSTNAME */
1954 CSV_UNDEFINED, /* TITLE */
1955 CSV_UNDEFINED, /* COMPANY */
1956 WORKPHONE, /* WORK PHONE */
1957 PHONE, /* HOME PHONE */
1959 MOBILEPHONE, /* OTHER */
1961 ADDRESS, /* ADDRESS */
1965 COUNTRY, /* COUNTRY */
1966 NICK, /* DEFINED 1 */
1967 URL, /* DEFINED 2 */
1968 CSV_UNDEFINED, /* DEFINED 3 */
1969 CSV_UNDEFINED, /* DEFINED 4 */
1971 PALM_CSV_END, /* "0" */
1972 PALM_CSV_CAT, /* CATEGORY */
1976 csv_export_common(out, e, palm_export_fields, palm_csv_handle_specials);
1982 * end of csv export filters
1986 * vCard 2 addressbook export filter
1990 vcard_export_database(FILE *out, struct db_enumerator e)
1992 db_enumerate_items(e)
1993 vcard_export_item(out, e.item);
1998 vcard_export_item(FILE *out, int item)
2002 abook_list *emails, *em;
2003 fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
2004 safe_str(db_name_get(item)));
2006 name = get_surname(db_name_get(item));
2007 for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) {
2008 if((db_name_get(item))[j] == ' ')
2011 fprintf(out, "N:%s;%.*s\r\n",
2014 safe_str(db_name_get(item))
2019 // see rfc6350 section 6.3.1
2020 if(db_fget(item, ADDRESS)) {
2021 fprintf(out, "ADR:;%s;%s;%s;%s;%s;%s\r\n",
2022 // pobox (unsupported)
2023 safe_str(db_fget(item, ADDRESS2)), // ext (n°, ...)
2024 safe_str(db_fget(item, ADDRESS)), // street
2025 safe_str(db_fget(item, CITY)), // locality
2026 safe_str(db_fget(item, STATE)), // region
2027 safe_str(db_fget(item, ZIP)), // code (postal)
2028 safe_str(db_fget(item, COUNTRY)) // country
2032 if(db_fget(item, PHONE))
2033 fprintf(out, "TEL;HOME:%s\r\n",
2034 db_fget(item, PHONE));
2035 if(db_fget(item, WORKPHONE))
2036 fprintf(out, "TEL;WORK:%s\r\n",
2037 db_fget(item, WORKPHONE));
2038 if(db_fget(item, FAX))
2039 fprintf(out, "TEL;FAX:%s\r\n",
2040 db_fget(item, FAX));
2041 if(db_fget(item, MOBILEPHONE))
2042 fprintf(out, "TEL;CELL:%s\r\n",
2043 db_fget(item, MOBILEPHONE));
2045 tmp = db_email_get(item);
2047 emails = csv_to_abook_list(tmp);
2048 fprintf(out, "EMAIL;PREF;INTERNET:%s\r\n", emails->data);
2050 for(em = emails->next; em; em = em->next, email_no++ )
2051 fprintf(out, "EMAIL;%d;INTERNET:%s\r\n", email_no, em->data);
2053 abook_list_free(&emails);
2057 if(db_fget(item, NOTES))
2058 fprintf(out, "NOTE:%s\r\n",
2059 db_fget(item, NOTES));
2060 if(db_fget(item, URL))
2061 fprintf(out, "URL:%s\r\n",
2062 db_fget(item, URL));
2064 fprintf(out, "END:VCARD\r\n\r\n");
2069 * end of vCard export filter
2074 * mutt alias export filter
2078 mutt_alias_genalias(int i)
2082 if(db_fget(i, NICK))
2083 return xstrdup(db_fget(i, NICK));
2085 tmp = xstrdup(db_name_get(i));
2087 if( ( pos = strchr(tmp, ' ') ) )
2096 * This function is a variant of abook_list_to_csv
2099 mutt_alias_gengroups(int i)
2101 char *groups, *res = NULL;
2102 char groupstr[7] = "-group ";
2103 abook_list *list, *tmp;
2105 groups = db_fget(i, GROUPS);
2110 list = csv_to_abook_list(groups);
2111 for(tmp = list; tmp; tmp = tmp->next) {
2113 res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1);
2114 res = strcpy(res, groupstr);
2116 res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1);
2118 strcat(res, groupstr);
2120 strcat(res, tmp->data);
2122 abook_list_free(&list);
2129 mutt_alias_export(FILE *out, struct db_enumerator e)
2131 char email[MAX_EMAIL_LEN];
2133 char *groups = NULL;
2134 int email_addresses;
2137 db_enumerate_items(e) {
2138 alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL;
2139 groups = (field_id(GROUPS) != -1) ? mutt_alias_gengroups(e.item) : NULL;
2140 get_first_email(email, e.item);
2142 /* do not output contacts without email address */
2143 /* cause this does not make sense in mutt aliases */
2146 /* output first email address */
2147 fprintf(out,"alias ");
2149 fprintf(out, "%s ", groups);
2151 fprintf(out, "%s ", alias);
2152 fprintf(out, "%s <%s>\n",
2153 db_name_get(e.item),
2156 /* number of email addresses */
2157 email_addresses = 1;
2158 ptr = db_email_get(e.item);
2159 while (*ptr != '\0') {
2166 /* output other email addresses */
2167 while (email_addresses-- > 1) {
2168 roll_emails(e.item, ROTATE_RIGHT);
2169 get_first_email(email, e.item);
2170 fprintf(out,"alias ");
2172 fprintf(out, "%s ", groups);
2174 fprintf(out, "%s__%s ", alias, email);
2176 fprintf(out, "%s__%s ", db_name_get(e.item), email);
2177 fprintf(out, "%s <%s>\n",
2178 db_name_get(e.item),
2181 roll_emails(e.item, ROTATE_RIGHT);
2190 void muttq_print_item(FILE *file, int item)
2192 abook_list *emails, *e;
2193 char *tmp = db_email_get(item);
2195 emails = csv_to_abook_list(tmp);
2198 for(e = emails; e; e = e->next) {
2199 fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item),
2200 !db_fget(item, NOTES) ?" " :db_fget(item, NOTES)
2202 if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS))
2205 abook_list_free(&emails);
2209 mutt_query_export_database(FILE *out, struct db_enumerator e)
2211 fprintf(out, "All items\n");
2212 db_enumerate_items(e)
2213 muttq_print_item(out, e.item);
2218 * end of mutt alias export filter
2223 * printable export filter
2228 text_write_address_us(FILE *out, int i) {
2229 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2231 if(db_fget(i, ADDRESS2))
2232 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2234 if(db_fget(i, CITY))
2235 fprintf(out, "\n%s", db_fget(i, CITY));
2237 if(db_fget(i, STATE) || db_fget(i, ZIP)) {
2240 if(db_fget(i, STATE)) {
2241 fprintf(out, "%s", db_fget(i, STATE));
2247 fprintf(out, "%s", db_fget(i, ZIP));
2250 if(db_fget(i, COUNTRY))
2251 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2256 text_write_address_uk(FILE *out, int i) {
2259 for(j = ADDRESS; j <= COUNTRY; j++)
2261 fprintf(out, "\n%s", db_fget(i, j));
2265 text_write_address_eu(FILE *out, int i) {
2266 fprintf(out, "\n%s", db_fget(i, ADDRESS));
2268 if(db_fget(i, ADDRESS2))
2269 fprintf(out, "\n%s", db_fget(i, ADDRESS2));
2271 if(db_fget(i, ZIP) || db_fget(i, CITY)) {
2274 if(db_fget(i, ZIP)) {
2275 fprintf(out, "%s", db_fget(i, ZIP));
2276 if(db_fget(i, CITY))
2280 fprintf(out, "%s", safe_str(db_fget(i, CITY)));
2283 if(db_fget(i, STATE))
2284 fprintf(out, "\n%s", db_fget(i, STATE));
2286 if(db_fget(i, COUNTRY))
2287 fprintf(out, "\n%s", db_fget(i, COUNTRY));
2291 text_export_database(FILE * out, struct db_enumerator e)
2293 abook_list *emails, *em;
2295 char *realname = get_real_name(), *str = NULL, *tmp;
2296 char *style = opt_get_str(STR_ADDRESS_STYLE);
2299 "-----------------------------------------\n%s's address book\n"
2300 "-----------------------------------------\n\n\n",
2304 db_enumerate_items(e) {
2306 "-----------------------------------------\n\n");
2307 fprintf(out, "%s", db_name_get(e.item));
2308 if(db_fget(e.item, NICK) && *db_fget(e.item, NICK))
2309 fprintf(out, "\n(%s)", db_fget(e.item, NICK));
2312 tmp = db_email_get(e.item);
2314 emails = csv_to_abook_list(tmp);
2317 for(em = emails; em; em = em->next)
2318 fprintf(out, "%s\n", em->data);
2320 abook_list_free(&emails);
2324 if(db_fget(e.item, ADDRESS)) {
2325 if(!safe_strcmp(style, "us")) /* US like */
2326 text_write_address_us(out, e.item);
2327 else if(!safe_strcmp(style, "uk")) /* UK like */
2328 text_write_address_uk(out, e.item);
2330 text_write_address_eu(out, e.item);
2335 if((db_fget(e.item, PHONE)) ||
2336 (db_fget(e.item, WORKPHONE)) ||
2337 (db_fget(e.item, FAX)) ||
2338 (db_fget(e.item, MOBILEPHONE))) {
2340 for(j = PHONE; j <= MOBILEPHONE; j++)
2341 if(db_fget(e.item, j)) {
2342 get_field_info(field_id(j),
2344 fprintf(out, "%s: %s\n", str,
2345 db_fget(e.item, j));
2349 if(db_fget(e.item, URL))
2350 fprintf(out, "\n%s\n", db_fget(e.item, URL));
2351 if(db_fget(e.item, NOTES))
2352 fprintf(out, "\n%s\n", db_fget(e.item, NOTES));
2357 fprintf(out, "-----------------------------------------\n");
2363 * end of printable export filter
2367 * elm alias export filter
2371 elm_alias_export(FILE *out, struct db_enumerator e)
2373 char email[MAX_EMAIL_LEN];
2376 db_enumerate_items(e) {
2377 alias = mutt_alias_genalias(e.item);
2378 get_first_email(email, e.item);
2379 fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email);
2387 * end of elm alias export filter
2392 * Spruce export filter
2396 spruce_export_database (FILE *out, struct db_enumerator e)
2398 char email[MAX_EMAIL_LEN];
2400 fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n");
2402 db_enumerate_items(e) {
2403 get_first_email(email, e.item);
2404 if(strcmp(email, "")) {
2405 fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n",
2407 db_name_get(e.item),
2409 safe_str(db_fget(e.item, NOTES))
2414 fprintf (out, "# End of address book file.\n");
2420 * end of Spruce export filter
2424 * wanderlust addressbook export filter
2428 wl_export_database(FILE *out, struct db_enumerator e)
2430 char email[MAX_EMAIL_LEN];
2432 fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE);
2433 db_enumerate_items(e) {
2434 get_first_email(email, e.item);
2437 "%s\t\"%s\"\t\"%s\"\n",
2439 safe_str(db_fget(e.item, NICK)),
2440 safe_str(db_name_get(e.item))
2445 fprintf (out, "\n# End of address book file.\n");
2451 * end of wanderlust addressbook export filter
2455 * BSD calendar export filter
2459 bsdcal_export_database(FILE *out, struct db_enumerator e)
2461 db_enumerate_items(e) {
2462 int year, month = 0, day = 0;
2463 char *anniversary = db_fget(e.item, ANNIVERSARY);
2466 if(!parse_date_string(anniversary, &day, &month, &year))
2470 _("%02d/%02d\tAnniversary of %s\n"),
2473 safe_str(db_name_get(e.item))
2481 // see also enum field_types @database.h
2482 extern abook_field standard_fields[];
2484 find_field_enum(char *s) {
2486 while(standard_fields[++i].key)
2487 if(!strcmp(standard_fields[i].key, s))
2492 /* Convert a string with named placeholders to
2493 a *printf() compatible string.
2494 Stores the abook field values into ft. */
2496 parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
2498 if(! fmt_string || ! ft) {
2499 fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n"));
2503 char tmp[1] = { 0 };
2504 char *p, *start, *field_name = NULL;
2511 if(! *start) goto cannotparse;
2512 p = strchr(start, '}');
2513 if(! p) goto cannotparse;
2514 strcat(fmt_string, "%s");
2515 field_name = strndup(start, (size_t)(p-start));
2516 *ft = find_field_enum(field_name);
2518 fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name);
2526 else if(*p == '\\') {
2528 if(! *p) tmp[0] = '\\'; // last char is a '\' ?
2529 else if(*p == 'n') *tmp = '\n';
2530 else if(*p == 't') *tmp = '\t';
2531 else if(*p == 'r') *tmp = '\r';
2532 else if(*p == 'v') *tmp = '\v';
2533 else if(*p == 'b') *tmp = '\b';
2534 else if(*p == 'a') *tmp = '\a';
2536 strncat(fmt_string, tmp, 1);
2540 // if no '\' following: quick mode using strchr/strncat
2541 else if(! strchr(start, '\\')) {
2542 p = strchr(start, '{');
2543 if(p) { // copy until the next placeholder
2544 strncat(fmt_string, start, (size_t)(p-start));
2547 else { // copy till the end
2548 strncat( fmt_string,
2550 FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
2555 // otherwise character by character
2557 strncat(fmt_string, p, 1);
2566 fprintf(stderr, _("%s: invalid format, index %ld\n"), __FUNCTION__, (start - s));
2568 while(*ft) free(ft--);
2573 custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
2576 // used to store the format string from --outformatstr when "custom" format is used
2577 // default value overriden in export_file()
2578 extern char *parsed_custom_format;
2579 extern enum field_types *custom_format_fields;
2581 /* wrapper for custom_export_item:
2582 1) avoid messing with extern pointer
2584 3) follow the prototype needed for an abook_output_item_filter entry */
2586 custom_print_item(FILE *out, int item)
2589 if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0)
2594 custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
2598 // if the first character is '!':
2599 // we first check that all fields exist before continuing
2601 enum field_types *ftp = ft;
2602 while(*ft != ITEM_FIELDS) {
2603 if(! db_fget(item, *ft) )
2612 if(!strncmp(fmt, "%s", 2)) {
2613 fprintf(out, "%s", safe_str(db_fget(item, *ft)));
2616 } else if (*ft == ITEM_FIELDS) {
2617 fprintf(out, "%s", fmt);
2620 p = strchr(fmt, '%');
2622 q = strndup(fmt, (size_t)(p-fmt));
2623 fprintf(out, "%s", q);
2628 fprintf(out, "%s", fmt);
2637 // used to store the format string from --outformatstr when "custom" format is used
2638 // default value overriden from abook.c
2639 extern char custom_format[FORMAT_STRING_LEN];
2642 custom_export_database(FILE *out, struct db_enumerator e)
2644 char *format_string =
2645 (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
2647 enum field_types *ft =
2648 (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
2650 parse_custom_format(custom_format, format_string, ft);
2652 db_enumerate_items(e) {
2653 if(custom_export_item(out, e.item, format_string, ft) == 0)
2660 * end of BSD calendar export filter