X-Git-Url: https://git.deb.at/w?p=pkg%2Fabook.git;a=blobdiff_plain;f=filter.c;h=e928492e596ffeddc69d0c8c380a73d8978e97b9;hp=d2bc6efc0f35fbe801b683270877cfdd089aed8d;hb=7c501d6668c722e4b0200c4303c70fe840a708a9;hpb=498e95b4f04e4377a63865bde70361f4be570276 diff --git a/filter.c b/filter.c index d2bc6ef..e928492 100644 --- a/filter.c +++ b/filter.c @@ -7,6 +7,8 @@ * Copyright (C) Jaakko Heinonen */ +#define _GNU_SOURCE + #include #include #include @@ -27,9 +29,12 @@ #include "xmalloc.h" #include -extern int items; -extern list_item *database; -extern struct abook_field abook_fields[]; +#ifdef HAVE_VFORMAT +#include "vcard.h" +#endif + +extern abook_field_list *fields_list; +extern int fields_count; /* * function declarations @@ -45,6 +50,7 @@ static int pine_parse_file(FILE *in); static int csv_parse_file(FILE *in); static int allcsv_parse_file(FILE *in); static int palmcsv_parse_file(FILE *in); +static int vcard_parse_file(FILE *in); /* * export filter prototypes @@ -56,11 +62,23 @@ static int pine_export_database(FILE *out, struct db_enumerator e); static int csv_export_database(FILE *out, struct db_enumerator e); static int allcsv_export_database(FILE *out, struct db_enumerator e); static int palm_export_database(FILE *out, struct db_enumerator e); -static int gcrd_export_database(FILE *out, struct db_enumerator e); +static int vcard_export_database(FILE *out, struct db_enumerator e); static int mutt_alias_export(FILE *out, struct db_enumerator e); +static int mutt_query_export_database(FILE *out, struct db_enumerator e); static int elm_alias_export(FILE *out, struct db_enumerator e); static int text_export_database(FILE *out, struct db_enumerator e); static int spruce_export_database(FILE *out, struct db_enumerator e); +static int wl_export_database(FILE *out, struct db_enumerator e); +static int bsdcal_export_database(FILE *out, struct db_enumerator e); +static int custom_export_database(FILE *out, struct db_enumerator e); + +/* + * export filter item prototypes + */ + +void vcard_export_item(FILE *out, int item); +void muttq_print_item(FILE *file, int item); +void custom_print_item(FILE *out, int item); /* * end of function declarations @@ -74,25 +92,37 @@ struct abook_input_filter i_filters[] = { { "csv", N_("comma separated values"), csv_parse_file }, { "allcsv", N_("comma separated values (all fields)"), allcsv_parse_file }, { "palmcsv", N_("Palm comma separated values"), palmcsv_parse_file }, + { "vcard", N_("vCard file"), vcard_parse_file }, { "\0", NULL, NULL } }; struct abook_output_filter e_filters[] = { { "abook", N_("abook native format"), write_database }, { "ldif", N_("ldif / Netscape addressbook (.4ld)"), ldif_export_database }, + { "vcard", N_("vCard 2 file"), vcard_export_database }, { "mutt", N_("mutt alias"), mutt_alias_export }, + { "muttq", N_("mutt query format (internal use)"), mutt_query_export_database }, { "html", N_("html document"), html_export_database }, { "pine", N_("pine addressbook"), pine_export_database }, - { "gcrd", N_("GnomeCard (VCard) addressbook"), gcrd_export_database }, { "csv", N_("comma separated values"), csv_export_database }, { "allcsv", N_("comma separated values (all fields)"), allcsv_export_database }, { "palmcsv", N_("Palm comma separated values"), palm_export_database}, { "elm", N_("elm alias"), elm_alias_export }, { "text", N_("plain text"), text_export_database }, + { "wl", N_("Wanderlust address book"), wl_export_database }, { "spruce", N_("Spruce address book"), spruce_export_database }, + { "bsdcal", N_("BSD calendar"), bsdcal_export_database }, + { "custom", N_("Custom format"), custom_export_database }, { "\0", NULL, NULL } }; +struct abook_output_item_filter u_filters[] = { + { "vcard", N_("vCard 2 file"), vcard_export_item }, + { "muttq", N_("mutt alias"), muttq_print_item }, + { "custom", N_("Custom format"), custom_print_item }, + { "\0", NULL } +}; + /* * common functions */ @@ -102,19 +132,26 @@ print_filters() { int i; - puts(_("input:")); + puts(_("input formats:")); for(i=0; *i_filters[i].filtname ; i++) printf("\t%s\t%s\n", i_filters[i].filtname, gettext(i_filters[i].desc)); putchar('\n'); - puts(_("output:")); + puts(_("output formats:")); for(i=0; *e_filters[i].filtname ; i++) printf("\t%s\t%s\n", e_filters[i].filtname, gettext(e_filters[i].desc)); putchar('\n'); + + puts(_("query-compatible output formats:")); + for(i=0; *u_filters[i].filtname ; i++) + printf("\t%s\t%s\n", u_filters[i].filtname, + gettext(u_filters[i].desc)); + + putchar('\n'); } static int @@ -192,7 +229,7 @@ import_database() { int filter; char *filename; - int tmp = items; + int tmp = db_n_items(); import_screen(); @@ -213,7 +250,7 @@ import_database() if(i_read_file(filename, i_filters[filter].func )) statusline_msg(_("Error occured while opening the file")); - else if(tmp == items) + else if(tmp == db_n_items()) statusline_msg(_("File does not seem to be a valid addressbook")); refresh_screen(); @@ -244,7 +281,7 @@ int import_file(char filtname[FILTNAME_LEN], char *filename) { int i; - int tmp = items; + int tmp = db_n_items(); int ret = 0; for(i=0;; i++) { @@ -260,6 +297,18 @@ import_file(char filtname[FILTNAME_LEN], char *filename) if(i < 0) return -1; +#ifdef HAVE_VFORMAT + // this is a special case for + // libvformat whose API expects a filename + if(!strcmp(filtname, "vcard")) { + if(!strcmp(filename, "-")) + ret = vcard_parse_file_libvformat("/dev/stdin"); + else + ret = vcard_parse_file_libvformat(filename); + } + else +#endif + if(!strcmp(filename, "-")) { struct stat s; if((fstat(fileno(stdin), &s)) == -1 || S_ISDIR(s.st_mode)) @@ -269,7 +318,7 @@ import_file(char filtname[FILTNAME_LEN], char *filename) } else ret = i_read_file(filename, i_filters[i].func); - if(tmp == items) + if(tmp == db_n_items()) ret = 1; return ret; @@ -279,7 +328,7 @@ import_file(char filtname[FILTNAME_LEN], char *filename) * export */ -static int e_write_file(char *filename, +static int e_write_file(char *filename, int (*func) (FILE *in, struct db_enumerator e), int mode); static void @@ -320,10 +369,12 @@ export_database() return 1; } - mvaddstr(5+filter, 2, "->"); + mvaddstr(5 + filter, 2, "->"); if(selected_items()) { - switch(statusline_askchoice(_("Export ll, export elected, or ancel?"), S_("keybindings:all/selected/cancel|asc"), 3)) { + switch(statusline_askchoice( + _("Export ll, export elected, or ancel?"), + S_("keybindings:all/selected/cancel|asc"), 3)) { case 1: break; case 2: @@ -352,6 +403,25 @@ export_database() return 0; } +struct abook_output_item_filter select_output_item_filter(char filtname[FILTNAME_LEN]) { + int i; + for(i=0;; i++) { + if(!strncasecmp(u_filters[i].filtname, filtname, FILTNAME_LEN)) + break; + if(!*u_filters[i].filtname) { + i = -1; + break; + } + } + return u_filters[i]; +} + +void +e_write_item(FILE *out, int item, void (*func) (FILE *in, int item)) +{ + (*func) (out, item); +} + static int e_write_file(char *filename, int (*func) (FILE *in, struct db_enumerator e), int mode) @@ -477,35 +547,42 @@ static int ldif_conv_table[LDIF_ITEM_FIELDS] = { -1, /* "objectclass" */ /* this must be the last entry */ }; - +/* + Handles multi-line strings. + If a string starts with a space, it's the continuation + of the previous line. Thus we need to always read ahead. + But for this to work with stdin, we need to stores the next + line for later use in case it's not a continuation of the + first line. + */ static char * -ldif_read_line(FILE *in) +ldif_read_line(FILE *in, char **next_line) { char *buf = NULL; char *ptr, *tmp; - long pos; - int i; - - for(i = 1;;i++) { - char *line; + char *line; + + // buf filled with the first line + if(!*next_line) + buf = getaline(in); + else { + buf = xstrdup(*next_line); + xfree(*next_line); + } - pos = ftell(in); + while(!feof(in)) { + // if no line already read-ahead. line = getaline(in); + if(!line) break; - if(feof(in) || !line) - break; - - if(i == 1) { - buf = line; - continue; - } - + // this is not a continuation of what is already in buf + // store it for the next round if(*line != ' ') { - fseek(in, pos, SEEK_SET); /* fixme ! */ - free(line); + *next_line = line; break; } + // starts with ' ': this is the continuation of buf ptr = line; while( *ptr == ' ') ptr++; @@ -527,10 +604,10 @@ ldif_read_line(FILE *in) static void ldif_add_item(ldif_item li) { - list_item abook_item; + list_item item; int i; - memset(abook_item, 0, sizeof(abook_item)); + item = item_create(); if(!li[LDIF_ITEM_FIELDS -1]) goto bail_out; @@ -538,14 +615,15 @@ ldif_add_item(ldif_item li) for(i=0; i < LDIF_ITEM_FIELDS; i++) { if(ldif_conv_table[i] >= 0 && li[i] && *li[i]) - abook_item[ldif_conv_table[i]] = xstrdup(li[i]); + item_fput(item,ldif_conv_table[i],xstrdup(li[i])); } - add_item2database(abook_item); + add_item2database(item); bail_out: for(i=0; i < LDIF_ITEM_FIELDS; i++) xfree(li[i]); + item_free(&item); } @@ -559,14 +637,10 @@ ldif_convert(ldif_item item, char *type, char *value) return; } - for(i=0; i < LDIF_ITEM_FIELDS; i++) { - if(!safe_strcmp(ldif_field_names[i], type) && *value) { - if(i == LDIF_ITEM_FIELDS - 1) /* this is a dirty hack */ - if(safe_strcmp("person", value)) - break; - if(item[i]) - xfree(item[i]); - item[i] = xstrdup(value); + for(i=0; i < LDIF_ITEM_FIELDS - 1; i++) { + if(!strcasecmp(ldif_field_names[i], type) && *value) { + item_fput(item, i, xstrdup(value)); + break; } } } @@ -575,6 +649,7 @@ static int ldif_parse_file(FILE *handle) { char *line = NULL; + char *next_line = NULL; char *type, *value; int vlen; ldif_item item; @@ -582,8 +657,10 @@ ldif_parse_file(FILE *handle) memset(item, 0, sizeof(item)); do { - if( !(line = ldif_read_line(handle)) ) - continue; + line = ldif_read_line(handle, &next_line); + + // EOF or empty lines: continue; + if(!line || *line == '\0') continue; if(-1 == (str_parse_line(line, &type, &value, &vlen))) { xfree(line); @@ -626,16 +703,16 @@ ldif_fix_string(char *str) #include "getname.h" static int -mutt_read_line(FILE *in, char **alias, char **rest) +mutt_read_line(FILE *in, char **groups, char **alias, char **rest) { - char *line, *ptr, *tmp; - size_t alias_len; + char *line, *ptr; + char *start, *end; + abook_list *glist = NULL; if( !(line = ptr = getaline(in)) ) return 1; /* error / EOF */ - while( ISSPACE(*ptr) ) - ptr++; + SKIPWS(ptr); if(strncmp("alias", ptr, 5)) { free(line); @@ -643,28 +720,39 @@ mutt_read_line(FILE *in, char **alias, char **rest) } ptr += 5; + SKIPWS(ptr); + + /* If the group option is used, save the groups */ + *groups = NULL; + start = ptr; + int n_groups; + for(n_groups = 0; 0 == strncmp("-group", ptr, 6); n_groups++) { + ptr += 6; + SKIPWS(ptr); + start = ptr; + SKIPNONWS(ptr); + end = ptr; + abook_list_append(&glist,xstrndup(start, end - start)); + SKIPWS(ptr); + } - while( ISSPACE(*ptr) ) - ptr++; - - tmp = ptr; - - while( ! ISSPACE(*ptr) ) - ptr++; - - alias_len = (size_t)(ptr - tmp); - - *alias = xmalloc_inc(alias_len, 1); + if(n_groups && groups) + *groups = abook_list_to_csv(glist); - strncpy(*alias, tmp, alias_len); - *(*alias + alias_len) = 0; + abook_list_free(&glist); - while(ISSPACE(*ptr)) - ptr++; + /* alias */ + start = ptr; + SKIPNONWS(ptr); + end = ptr; + SKIPWS(ptr); + if(alias) + *alias = xstrndup(start, end - start); + /* rest (email) */ *rest = xstrdup(ptr); - free(line); + xfree(line); return 0; } @@ -691,7 +779,7 @@ mutt_fix_quoting(char *p) static void mutt_parse_email(list_item item) { - char *line = item[NAME]; + char *line = item_fget(item, NAME); char *tmp; char *name, *email; #if 0 @@ -705,11 +793,12 @@ mutt_parse_email(list_item item) free(tmp); if(name) - item[NAME] = name; + item_fput(item, NAME, name); else return; + if(email) - item[EMAIL] = email; + item_fput(item, EMAIL, email); else return; @@ -738,22 +827,25 @@ mutt_parse_email(list_item item) static int mutt_parse_file(FILE *in) { - list_item item; + list_item item = item_create(); for(;;) { - memset(item, 0, sizeof(item)); + memset(item, 0, fields_count * sizeof(char *)); - if(!mutt_read_line(in, &item[NICK], - &item[NAME]) ) + if(!mutt_read_line(in, + (field_id(GROUPS) != -1) ? &item[field_id(GROUPS)] : NULL, + (field_id(NICK) != -1) ? &item[field_id(NICK)] : NULL, + &item[field_id(NAME)]) ) mutt_parse_email(item); if(feof(in)) { - free_list_item(item); + item_empty(item); break; } add_item2database(item); } + item_free(&item); return 0; } @@ -791,7 +883,8 @@ ldif_export_database(FILE *out, struct db_enumerator e) int j; get_first_email(email, e.item); - tmp = mkstr("cn=%s,mail=%s", database[e.item][NAME], email); + tmp = strdup_printf("cn=%s,mail=%s",db_name_get(e.item),email); + ldif_fput_type_and_value(out, "dn", tmp); free(tmp); @@ -800,10 +893,11 @@ ldif_export_database(FILE *out, struct db_enumerator e) if(ldif_conv_table[j] == EMAIL) ldif_fput_type_and_value(out, ldif_field_names[j], email); - else if(database[e.item][ldif_conv_table[j]]) + else if(db_fget(e.item,ldif_conv_table[j])) ldif_fput_type_and_value(out, ldif_field_names[j], - database[e.item][ldif_conv_table[j]]); + db_fget(e.item, + ldif_conv_table[j])); } } @@ -822,38 +916,56 @@ ldif_export_database(FILE *out, struct db_enumerator e) * html export filter */ -static void html_export_write_head(FILE *out, int extra_column); +static void html_export_write_head(FILE *out); static void html_export_write_tail(FILE *out); -extern int extra_column; +extern struct index_elem *index_elements; + +static void +html_print_emails(FILE *out, struct list_field *f) +{ + abook_list *l = csv_to_abook_list(f->data); + + for(; l; l = l->next) { + fprintf(out, "%s", l->data, l->data); + if(l->next) + fprintf(out, ", "); + } + + abook_list_free(&l); +} static int html_export_database(FILE *out, struct db_enumerator e) { - char tmp[MAX_EMAILSTR_LEN]; + struct list_field f; + struct index_elem *cur; - if(items < 1) + if(list_is_empty()) return 2; - extra_column = (extra_column > 2 && extra_column < ITEM_FIELDS) ? - extra_column : PHONE; + init_index(); - html_export_write_head(out, extra_column); + html_export_write_head(out); db_enumerate_items(e) { - get_first_email(tmp, e.item); - if (*tmp) - fprintf(out, "\n%s\n", - tmp, - database[e.item][NAME] ); - else - fprintf(out, "\n%s\n", - database[e.item][NAME] ); - - fprintf(out, "%s\n%s\n", - database[e.item][EMAIL], - safe_str(database[e.item][extra_column]) ); - fprintf(out, "\n\n"); + fprintf(out, ""); + for(cur = index_elements; cur; cur = cur->next) { + if(cur->type != INDEX_FIELD) + continue; + + get_list_field(e.item, cur, &f); + + if(f.type == FIELD_EMAILS) { + fprintf(out, ""); + html_print_emails(out, &f); + fprintf(out, ""); + continue; + } else { + fprintf(out, "%s", safe_str(f.data)); + } + } + fprintf(out, "\n"); } html_export_write_tail(out); @@ -861,21 +973,28 @@ html_export_database(FILE *out, struct db_enumerator e) return 0; } - static void -html_export_write_head(FILE *out, int extra_column) +html_export_write_head(FILE *out) { - char *realname = get_real_name(); + char *realname = get_real_name(), *str; + struct index_elem *cur; fprintf(out, "\n"); - fprintf(out, "\n\n %s's addressbook", realname ); + fprintf(out, "\n\n %s's addressbook", + realname ); fprintf(out, "\n\n\n"); fprintf(out, "\n

%s's addressbook

\n", realname ); fprintf(out, "

\n\n"); - fprintf(out, "\n"); - fprintf(out, "\n\n\n", - abook_fields[extra_column].name); + fprintf(out, "
NameE-mail address(es)%s
\n"); + for(cur = index_elements; cur; cur = cur->next) { + if(cur->type != INDEX_FIELD) + continue; + + get_field_info(cur->d.field.id, NULL, &str, NULL); + fprintf(out, "", str); + } + fprintf(out, "\n\n"); free(realname); } @@ -903,7 +1022,7 @@ pine_fixbuf(char *buf) { int i,j; - for(i=0,j=0; j < (int)strlen(buf); i++, j++) + for(i = 0,j = 0; j < (int)strlen(buf); i++, j++) buf[i] = buf[j] == '\n' ? buf[++j] : buf[j]; } @@ -916,15 +1035,15 @@ pine_convert_emails(char *s) if(s == NULL || *s != '(') return; - for(i=0; s[i]; i++ ) - s[i] = s[i+1]; + for(i = 0; s[i]; i++) + s[i] = s[i + 1]; if( ( tmp = strchr(s,')')) ) - *tmp=0; + *tmp = '\0'; for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1) - if(i > MAX_EMAILS - 1) { - *tmp = 0; + if(i > MAX_LIST_ITEMS - 1) { + *tmp = '\0'; break; } @@ -940,7 +1059,7 @@ pine_parse_buf(char *buf) int i, len, last; int pine_conv_table[]= {NICK, NAME, EMAIL, -1, NOTES}; - memset(&item, 0, sizeof(item)); + item = item_create(); for(i=0, last=0; !last ; i++) { if( !(end = strchr(start, '\t')) ) @@ -954,13 +1073,15 @@ pine_parse_buf(char *buf) strncpy(tmp, start, len); tmp[len] = 0; if(*tmp) - item[pine_conv_table[i]] = xstrdup(tmp); + item_fput(item, pine_conv_table[i], + xstrdup(tmp)); } start = end + 1; } - pine_convert_emails(item[EMAIL]); + pine_convert_emails(item_fget(item, EMAIL)); add_item2database(item); + item_free(&item); } @@ -1020,14 +1141,18 @@ pine_parse_file(FILE *in) static int pine_export_database(FILE *out, struct db_enumerator e) { + char *emails; + db_enumerate_items(e) { - fprintf(out, have_multiple_emails(e.item) ? + emails = db_email_get(e.item); + fprintf(out, strchr(emails, ',') /* multiple addresses? */ ? "%s\t%s\t(%s)\t\t%s\n" : "%s\t%s\t%s\t\t%s\n", - safe_str(database[e.item][NICK]), - safe_str(database[e.item][NAME]), - safe_str(database[e.item][EMAIL]), - safe_str(database[e.item][NOTES]) + safe_str(db_fget(e.item, NICK)), + safe_str(db_name_get(e.item)), + emails, + safe_str(db_fget(e.item, NOTES)) ); + free(emails); } return 0; @@ -1076,11 +1201,7 @@ static int allcsv_conv_table[] = { NICK, URL, NOTES, - CUSTOM1, - CUSTOM2, - CUSTOM3, - CUSTOM4, - CUSTOM5, + ANNIVERSARY }; static int palmcsv_conv_table[] = { @@ -1098,11 +1219,7 @@ static int palmcsv_conv_table[] = { STATE, ZIP, COUNTRY, - CUSTOM1, - CUSTOM2, - CUSTOM3, - CUSTOM4, - CUSTOM5, + ANNIVERSARY, }; static void @@ -1115,7 +1232,7 @@ csv_convert_emails(char *s) return; for(i = 1; ( tmp = strchr(s, ',') ) != NULL ; i++, s = tmp + 1) - if(i > MAX_EMAILS - 1) { + if(i > MAX_LIST_ITEMS - 1) { *tmp = 0; break; } @@ -1152,7 +1269,7 @@ static int csv_field_to_item(int *table_base, size_t table_size, int field) { if(field < table_size) - return table_base[field]; + return field_id(table_base[field]); return -1; } @@ -1221,7 +1338,7 @@ csv_parse_line(char *line, int *table_base, size_t table_size) bool in_quote = FALSE; list_item item; - memset(item, 0, sizeof(item)); + item = item_create(); for(p = start = line, field = 0; *p; p++) { if(in_quote) { @@ -1235,10 +1352,8 @@ csv_parse_line(char *line, int *table_base, size_t table_size) if(*p == ',' && !in_quote) { *p = 0; - csv_field_to_item(table_base, table_size, field); csv_store_item(item, - csv_field_to_item(table_base, - table_size, field), + csv_field_to_item(table_base,table_size,field), start); field++; start = p + 1; @@ -1250,8 +1365,9 @@ csv_parse_line(char *line, int *table_base, size_t table_size) csv_store_item(item, csv_field_to_item(table_base, table_size, field), start); - csv_convert_emails(item[EMAIL]); + csv_convert_emails(item_fget(item, EMAIL)); add_item2database(item); + item_free(&item); } static int @@ -1296,6 +1412,278 @@ palmcsv_parse_file(FILE *in) * end of csv import filter */ +/* + * vCard import filter + */ + +static char *vcard_fields[] = { + "FN", /* FORMATTED NAME */ + "EMAIL", /* EMAIL */ + "ADR", /* ADDRESS */ + "ADR", /* ADDRESS2 - not used */ + "ADR", /* CITY */ + "ADR", /* STATE */ + "ADR", /* ZIP */ + "ADR", /* COUNTRY */ + "TEL", /* PHONE */ + "TEL", /* WORKPHONE */ + "TEL", /* FAX */ + "TEL", /* MOBILEPHONE */ + "NICKNAME", /* NICK */ + "URL", /* URL */ + "NOTE", /* NOTES */ + "N", /* NAME: special case/mapping in vcard_parse_line() */ + NULL /* not implemented: ANNIVERSARY, ITEM_FIELDS */ +}; + +/* + * mappings between vCard ADR field and abook's ADDRESS + * see rfc2426 section 3.2.1 + */ +static int vcard_address_fields[] = { + -1, /* vCard(post office box) - not used */ + -1, /* vCard(the extended address) - not used */ + 2, /* vCard(the street address) - ADDRESS */ + 4, /* vCard(the locality) - CITY */ + 5, /* vCard(the region) - STATE */ + 6, /* vCard(the postal code) - ZIP */ + 7 /* vCard(the country name) - COUNTRY */ +}; + +enum { + VCARD_KEY = 0, + VCARD_KEY_ATTRIBUTE, + VCARD_VALUE, +}; + +static char * +vcard_get_line_element(char *line, int element) +{ + int i; + char *line_copy = 0; + char *result = 0; + char *key = 0; + char *key_attr = 0; + char *value = 0; + + line_copy = xstrdup(line); + + /* change newline characters, if present, to end of string */ + for(i=0; line_copy[i]; i++) { + if(line_copy[i] == '\r' || line_copy[i] == '\n') { + line_copy[i] = '\0'; + break; + } + } + + /* separate key from value */ + for(i=0; line_copy[i]; i++) { + if(line_copy[i] == ':') { + line_copy[i] = '\0'; + key = line_copy; + value = &line_copy[i+1]; + break; + } + } + + /* separate key from key attributes */ + /* works for vCard 2 as well (automagically) */ + if (key) { + for(i=0; key[i]; i++) { + if(key[i] == ';') { + key[i] = '\0'; + key_attr = &key[i+1]; + break; + } + } + } + + switch(element) { + case VCARD_KEY: + if(key) + result = xstrdup(key); + break; + case VCARD_KEY_ATTRIBUTE: + if(key_attr) + result = xstrdup(key_attr); + break; + case VCARD_VALUE: + if(value) + result = xstrdup(value); + break; + } + + xfree(line_copy); + return result; +} + +static void +vcard_parse_email(list_item item, char *line) +{ + char *email; + + email = vcard_get_line_element(line, VCARD_VALUE); + + if(item[1]) { + item[1] = strconcat(item[1], ",", email, 0); + xfree(email); + } + else { + item[1] = email; + } +} + +static void +vcard_parse_address(list_item item, char *line) +{ + int i; + int k; + char *value; + char *address_field; + + value = vcard_get_line_element(line, VCARD_VALUE); + if(!value) + return; + + address_field = value; + for(i=k=0; value[i]; i++) { + if(value[i] == ';') { + value[i] = '\0'; + if(vcard_address_fields[k] >= 0) { + item[vcard_address_fields[k]] = xstrdup(address_field); + } + address_field = &value[i+1]; + k++; + if((k+1)==(sizeof(vcard_address_fields)/sizeof(*vcard_address_fields))) + break; + } + } + item[vcard_address_fields[k]] = xstrdup(address_field); + xfree(value); +} + +static void +vcard_parse_name(list_item item, char *line) +{ + // store the "N" field into "NAME" *if* no "FN:" + // value has already been stored here + if(item[0]) return; + + int i = -1; + item[0] = vcard_get_line_element(line, VCARD_VALUE); + // "N:" can be multivalued => replace ';' separators by ' ' + while(item[0][++i]) if(item[0][i] == ';') item[0][i] = ' '; + + // http://www.daniweb.com/software-development/c/code/216919 + char *original = item[0], *p = original; + int trimmed = 0; + do { + if (*original != ' ' || trimmed) { + trimmed = 1; *p++ = *original; + } + } while(*original++); +} + +static void +vcard_parse_phone(list_item item, char *line) +{ + char *type = vcard_get_line_element(line, VCARD_KEY_ATTRIBUTE); + char *value = vcard_get_line_element(line, VCARD_VALUE); + + /* set the standard number */ + if (!type) item_fput(item, PHONE, value); + + /* + * see rfc2426 section 3.3.1 + * Note: we probably support both vCard 2 and 3 + */ + else { + if (strcasestr(type, "home") != NULL) + item_fput(item, PHONE, xstrdup(value)); + else if (strcasestr(type, "work") != NULL) + item_fput(item, WORKPHONE, xstrdup(value)); + else if (strcasestr(type, "fax") != NULL) + item_fput(item, FAX, xstrdup(value)); + else if (strcasestr(type, "cell") != NULL) + item_fput(item, MOBILEPHONE, xstrdup(value)); + + xfree(type); + xfree(value); + } +} + +static void +vcard_parse_line(list_item item, char *line) +{ + int i; + char *key; + + for(i=0; vcard_fields[i]; i++) { + key = vcard_fields[i]; + + if(0 == strncmp(key, line, strlen(key))) { + if(0 == strcmp(key, "EMAIL")) + vcard_parse_email(item, line); + else if(i == 2) + vcard_parse_address(item, line); + else if(0 == strcmp(key, "TEL")) + vcard_parse_phone(item, line); + else if(0 == strcmp(key, "N")) + vcard_parse_name(item, line); + else + item[i] = vcard_get_line_element(line, VCARD_VALUE); + return; + } + } +} + +static void +vcard_parse_item(FILE *in) +{ + char *line = NULL; + list_item item = item_create(); + + while(!feof(in)) { + line = getaline(in); + + if(line && !strncmp("END:VCARD", line, 9)) { + xfree(line); + break; + } + else if(line) { + vcard_parse_line(item, line); + xfree(line); + } + } + + add_item2database(item); + item_free(&item); +} + +static int +vcard_parse_file(FILE *in) +{ + char *line = NULL; + + while(!feof(in)) { + line = getaline(in); + + if(line && !strncmp("BEGIN:VCARD", line, 11)) { + xfree(line); + vcard_parse_item(in); + } + else if(line) { + xfree(line); + } + } + + return 0; +} + +/* + * end of vCard import filter + */ + /* * csv addressbook export filters */ @@ -1320,13 +1708,13 @@ csv_export_common(FILE *out, struct db_enumerator e, (*special_func)(out, e.item, fields[i]); } else /*fprintf(out,( - strchr(safe_str(database[e.item][fields[i]]), ',') || - strchr(safe_str(database[e.item][fields[i]]), '\"')) ? + strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') || + strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ? "\"%s\"" : "%s", - safe_str(database[e.item][fields[i]]) + safe_str(database[e.item][field_idx(fields[i])]) );*/ fprintf(out, "\"%s\"", - safe_str(database[e.item][fields[i]])); + safe_str(db_fget(e.item,fields[i]))); if(fields[i + 1] != CSV_LAST) fputc(',', out); @@ -1377,11 +1765,8 @@ allcsv_export_database(FILE *out, struct db_enumerator e) NICK, URL, NOTES, - CUSTOM1, - CUSTOM2, - CUSTOM3, - CUSTOM4, - CUSTOM5, + ANNIVERSARY, + GROUPS, CSV_LAST }; @@ -1401,11 +1786,8 @@ allcsv_export_database(FILE *out, struct db_enumerator e) fprintf(out, "\"NICK\","); fprintf(out, "\"URL\","); fprintf(out, "\"NOTES\","); - fprintf(out, "\"CUSTOM1\","); - fprintf(out, "\"CUSTOM2\","); - fprintf(out, "\"CUSTOM3\","); - fprintf(out, "\"CUSTOM4\","); - fprintf(out, "\"CUSTOM5\"\n"); + fprintf(out, "\"ANNIVERSARY\","); + fprintf(out, "\"GROUPS\"\n"); csv_export_common(out, e, allcsv_export_fields, NULL); @@ -1444,7 +1826,7 @@ palm_csv_handle_specials(FILE *out, int item, int field) { switch(field) { case PALM_CSV_NAME: - palm_split_and_write_name(out, database[item][NAME]); + palm_split_and_write_name(out, db_name_get(item)); break; case PALM_CSV_CAT: fprintf(out, "\"abook\""); @@ -1494,75 +1876,86 @@ palm_export_database(FILE *out, struct db_enumerator e) */ /* - * GnomeCard (VCard) addressbook export filter + * vCard 2 addressbook export filter */ static int -gcrd_export_database(FILE *out, struct db_enumerator e) +vcard_export_database(FILE *out, struct db_enumerator e) { - char emails[MAX_EMAILS][MAX_EMAIL_LEN]; - int j; - char *name; - - db_enumerate_items(e) { - fprintf(out, "BEGIN:VCARD\nFN:%s\n", - safe_str(database[e.item][NAME])); - - name = get_surname(database[e.item][NAME]); - for( j = strlen(database[e.item][NAME]) - 1; j >= 0; j-- ) { - if(database[e.item][NAME][j] == ' ') - break; - } - fprintf(out, "N:%s;%.*s\n", - safe_str(name), - j, - safe_str(database[e.item][NAME]) - ); - - free(name); - - if ( database[e.item][ADDRESS] ) - fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\n", - safe_str(database[e.item][ADDRESS]), - safe_str(database[e.item][ADDRESS2]), - safe_str(database[e.item][CITY]), - safe_str(database[e.item][STATE]), - safe_str(database[e.item][ZIP]), - safe_str(database[e.item][COUNTRY]) - ); - - if (database[e.item][PHONE]) - fprintf(out, "TEL;HOME:%s\n", database[e.item][PHONE]); - if (database[e.item][WORKPHONE]) - fprintf(out, "TEL;WORK:%s\n", database[e.item][WORKPHONE]); - if (database[e.item][FAX]) - fprintf(out, "TEL;FAX:%s\n", database[e.item][FAX]); - if (database[e.item][MOBILEPHONE]) - fprintf(out, "TEL;CELL:%s\n", database[e.item][MOBILEPHONE]); - - if ( database[e.item][EMAIL] ) { - split_emailstr(e.item, emails); - for(j=0; j < MAX_EMAILS ; j++) { - if ( *emails[j] ) - fprintf(out, "EMAIL;INTERNET:%s\n", - emails[j]); - } - } + db_enumerate_items(e) + vcard_export_item(out, e.item); + return 0; +} - if ( database[e.item][NOTES] ) - fprintf(out, "NOTE:%s\n", database[e.item][NOTES]); - if (database[e.item][URL]) - fprintf(out, "URL:%s\n", database[e.item][URL]); +void +vcard_export_item(FILE *out, int item) +{ + int j; + char *name, *tmp; + abook_list *emails, *em; + fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n", + safe_str(db_name_get(item))); + + name = get_surname(db_name_get(item)); + for( j = strlen(db_name_get(item)) - 1; j >= 0; j-- ) { + if((db_name_get(item))[j] == ' ') + break; + } + fprintf(out, "N:%s;%.*s\r\n", + safe_str(name), + j, + safe_str(db_name_get(item)) + ); + + free(name); + + if(db_fget(item, ADDRESS)) + fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n", + safe_str(db_fget(item, ADDRESS)), + safe_str(db_fget(item, ADDRESS2)), + safe_str(db_fget(item, CITY)), + safe_str(db_fget(item, STATE)), + safe_str(db_fget(item, ZIP)), + safe_str(db_fget(item, COUNTRY)) + ); + + if(db_fget(item, PHONE)) + fprintf(out, "TEL;HOME:%s\r\n", + db_fget(item, PHONE)); + if(db_fget(item, WORKPHONE)) + fprintf(out, "TEL;WORK:%s\r\n", + db_fget(item, WORKPHONE)); + if(db_fget(item, FAX)) + fprintf(out, "TEL;FAX:%s\r\n", + db_fget(item, FAX)); + if(db_fget(item, MOBILEPHONE)) + fprintf(out, "TEL;CELL:%s\r\n", + db_fget(item, MOBILEPHONE)); + + tmp = db_email_get(item); + if(*tmp) { + emails = csv_to_abook_list(tmp); + + for(em = emails; em; em = em->next) + fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data); + + abook_list_free(&emails); + } + free(tmp); - fprintf(out, "END:VCARD\n\n"); + if(db_fget(item, NOTES)) + fprintf(out, "NOTE:%s\r\n", + db_fget(item, NOTES)); + if(db_fget(item, URL)) + fprintf(out, "URL:%s\r\n", + db_fget(item, URL)); - } + fprintf(out, "END:VCARD\r\n\r\n"); - return 0; } /* - * end of GnomeCard export filter + * end of vCard export filter */ @@ -1575,10 +1968,10 @@ mutt_alias_genalias(int i) { char *tmp, *pos; - if(database[i][NICK]) - return xstrdup(database[i][NICK]); + if(db_fget(i, NICK)) + return xstrdup(db_fget(i, NICK)); - tmp = xstrdup(database[i][NAME]); + tmp = xstrdup(db_name_get(i)); if( ( pos = strchr(tmp, ' ') ) ) *pos = 0; @@ -1588,26 +1981,128 @@ mutt_alias_genalias(int i) return tmp; } +/* + * This function is a variant of abook_list_to_csv + * */ +static char * +mutt_alias_gengroups(int i) +{ + char *groups, *res = NULL; + char groupstr[7] = "-group "; + abook_list *list, *tmp; + + groups = db_fget(i, GROUPS); + + if(!groups) + return NULL; + + list = csv_to_abook_list(groups); + for(tmp = list; tmp; tmp = tmp->next) { + if(tmp == list) { + res = xmalloc(strlen(groupstr)+strlen(tmp->data)+1); + res = strcpy(res, groupstr); + } else { + res = xrealloc(res, strlen(res)+1+strlen(groupstr)+strlen(tmp->data)+1); + strcat(res, " "); + strcat(res, groupstr); + } + strcat(res, tmp->data); + } + abook_list_free(&list); + xfree(groups); + + return res; +} + static int mutt_alias_export(FILE *out, struct db_enumerator e) { char email[MAX_EMAIL_LEN]; char *alias = NULL; + char *groups = NULL; + int email_addresses; + char *ptr; db_enumerate_items(e) { - alias = mutt_alias_genalias(e.item); - + alias = (field_id(NICK) != -1) ? mutt_alias_genalias(e.item) : NULL; + groups = (field_id(GROUPS) != -1) ? mutt_alias_gengroups(e.item) : NULL; get_first_email(email, e.item); - fprintf(out, *email ? "alias %s %s <%s>\n": "alias %s %s%s\n", - alias, - database[e.item][NAME], - email); - xfree(alias); + + /* do not output contacts without email address */ + /* cause this does not make sense in mutt aliases */ + if (*email) { + + /* output first email address */ + fprintf(out,"alias "); + if(groups) + fprintf(out, "%s ", groups); + if(alias) + fprintf(out, "%s ", alias); + fprintf(out, "%s <%s>\n", + db_name_get(e.item), + email); + + /* number of email addresses */ + email_addresses = 1; + ptr = db_email_get(e.item); + while (*ptr != '\0') { + if (*ptr == ',') { + email_addresses++; + } + ptr++; + } + + /* output other email addresses */ + while (email_addresses-- > 1) { + roll_emails(e.item, ROTATE_RIGHT); + get_first_email(email, e.item); + fprintf(out,"alias "); + if( groups ) + fprintf(out, "%s ", groups); + if(alias) + fprintf(out, "%s__%s ", alias, email); + else + fprintf(out, "%s__%s ", db_name_get(e.item), email); + fprintf(out, "%s <%s>\n", + db_name_get(e.item), + email); + } + roll_emails(e.item, ROTATE_RIGHT); + xfree(alias); + xfree(groups); + } } return 0; } +void muttq_print_item(FILE *file, int item) +{ + abook_list *emails, *e; + char *tmp = db_email_get(item); + + emails = csv_to_abook_list(tmp); + free(tmp); + + for(e = emails; e; e = e->next) { + fprintf(file, "%s\t%s\t%s\n", e->data, db_name_get(item), + !db_fget(item, NOTES) ?" " :db_fget(item, NOTES) + ); + if(!opt_get_bool(BOOL_MUTT_RETURN_ALL_EMAILS)) + break; + } + abook_list_free(&emails); +} + +static int +mutt_query_export_database(FILE *out, struct db_enumerator e) +{ + fprintf(out, "All items\n"); + db_enumerate_items(e) + muttq_print_item(out, e.item); + return 0; +} + /* * end of mutt alias export filter */ @@ -1620,29 +2115,29 @@ mutt_alias_export(FILE *out, struct db_enumerator e) static void text_write_address_us(FILE *out, int i) { - fprintf(out, "\n%s", database[i][ADDRESS]); + fprintf(out, "\n%s", db_fget(i, ADDRESS)); - if (database[i][ADDRESS2]) - fprintf(out, "\n%s", database[i][ADDRESS2]); + if(db_fget(i, ADDRESS2)) + fprintf(out, "\n%s", db_fget(i, ADDRESS2)); - if (database[i][CITY]) - fprintf(out, "\n%s", database[i][CITY]); + if(db_fget(i, CITY)) + fprintf(out, "\n%s", db_fget(i, CITY)); - if (database[i][STATE] || database[i][ZIP]) { + if(db_fget(i, STATE) || db_fget(i, ZIP)) { fputc('\n', out); - if(database[i][STATE]) { - fprintf(out, "%s", database[i][STATE]); - if(database[i][ZIP]) + if(db_fget(i, STATE)) { + fprintf(out, "%s", db_fget(i, STATE)); + if(db_fget(i, ZIP)) fputc(' ', out); } - if(database[i][ZIP]) - fprintf(out, "%s", database[i][ZIP]); + if(db_fget(i, ZIP)) + fprintf(out, "%s", db_fget(i, ZIP)); } - if (database[i][COUNTRY]) - fprintf(out, "\n%s", database[i][COUNTRY]); + if(db_fget(i, COUNTRY)) + fprintf(out, "\n%s", db_fget(i, COUNTRY)); } @@ -1650,44 +2145,43 @@ static void text_write_address_uk(FILE *out, int i) { int j; - for (j = ADDRESS; j <= COUNTRY; j++) - if (database[i][j]) - fprintf(out, "\n%s", database[i][j]); + for(j = ADDRESS; j <= COUNTRY; j++) + if(db_fget(i, j)) + fprintf(out, "\n%s", db_fget(i, j)); } static void text_write_address_eu(FILE *out, int i) { - fprintf(out, "\n%s", database[i][ADDRESS]); + fprintf(out, "\n%s", db_fget(i, ADDRESS)); - if (database[i][ADDRESS2]) - fprintf(out, "\n%s", database[i][ADDRESS2]); + if(db_fget(i, ADDRESS2)) + fprintf(out, "\n%s", db_fget(i, ADDRESS2)); - if (database[i][ZIP] || database[i][CITY]) { + if(db_fget(i, ZIP) || db_fget(i, CITY)) { fputc('\n', out); - if(database[i][ZIP]) { - fprintf(out, "%s", database[i][ZIP]); - if(database[i][CITY]) + if(db_fget(i, ZIP)) { + fprintf(out, "%s", db_fget(i, ZIP)); + if(db_fget(i, CITY)) fputc(' ', out); } - if(database[i][CITY]) - fprintf(out, "%s", database[i][CITY]); + fprintf(out, "%s", safe_str(db_fget(i, CITY))); } - if (database[i][STATE]) - fprintf(out, "\n%s", database[i][STATE]); + if(db_fget(i, STATE)) + fprintf(out, "\n%s", db_fget(i, STATE)); - if (database[i][COUNTRY]) - fprintf(out, "\n%s", database[i][COUNTRY]); + if(db_fget(i, COUNTRY)) + fprintf(out, "\n%s", db_fget(i, COUNTRY)); } static int text_export_database(FILE * out, struct db_enumerator e) { - char emails[MAX_EMAILS][MAX_EMAIL_LEN]; + abook_list *emails, *em; int j; - char *realname = get_real_name(); + char *realname = get_real_name(), *str = NULL, *tmp; char *style = opt_get_str(STR_ADDRESS_STYLE); fprintf(out, @@ -1699,23 +2193,27 @@ text_export_database(FILE * out, struct db_enumerator e) db_enumerate_items(e) { fprintf(out, "-----------------------------------------\n\n"); - fprintf(out, "%s", database[e.item][NAME]); - if (database[e.item][NICK] && *database[e.item][NICK]) - fprintf(out, "\n(%s)", database[e.item][NICK]); + fprintf(out, "%s", db_name_get(e.item)); + if(db_fget(e.item, NICK) && *db_fget(e.item, NICK)) + fprintf(out, "\n(%s)", db_fget(e.item, NICK)); fprintf(out, "\n"); - if (*database[e.item][EMAIL]) { + tmp = db_email_get(e.item); + if(*tmp) { + emails = csv_to_abook_list(tmp); + fprintf(out, "\n"); - split_emailstr(e.item, emails); - for (j = 0; j < MAX_EMAILS; j++) - if (*emails[j]) - fprintf(out, "%s\n", emails[j]); + for(em = emails; em; em = em->next) + fprintf(out, "%s\n", em->data); + + abook_list_free(&emails); } + free(tmp); /* Print address */ - if (database[e.item][ADDRESS]) { - if (!safe_strcmp(style, "us")) /* US like */ + if(db_fget(e.item, ADDRESS)) { + if(!safe_strcmp(style, "us")) /* US like */ text_write_address_us(out, e.item); - else if (!safe_strcmp(style, "uk")) /* UK like */ + else if(!safe_strcmp(style, "uk")) /* UK like */ text_write_address_uk(out, e.item); else /* EU like */ text_write_address_eu(out, e.item); @@ -1723,22 +2221,24 @@ text_export_database(FILE * out, struct db_enumerator e) fprintf(out, "\n"); } - if ((database[e.item][PHONE]) || - (database[e.item][WORKPHONE]) || - (database[e.item][FAX]) || - (database[e.item][MOBILEPHONE])) { + if((db_fget(e.item, PHONE)) || + (db_fget(e.item, WORKPHONE)) || + (db_fget(e.item, FAX)) || + (db_fget(e.item, MOBILEPHONE))) { fprintf(out, "\n"); - for (j = PHONE; j <= MOBILEPHONE; j++) - if (database[e.item][j]) - fprintf(out, "%s: %s\n", - abook_fields[j].name, - database[e.item][j]); + for(j = PHONE; j <= MOBILEPHONE; j++) + if(db_fget(e.item, j)) { + get_field_info(field_id(j), + NULL, &str, NULL); + fprintf(out, "%s: %s\n", str, + db_fget(e.item, j)); + } } - if (database[e.item][URL]) - fprintf(out, "\n%s\n", database[e.item][URL]); - if (database[e.item][NOTES]) - fprintf(out, "\n%s\n", database[e.item][NOTES]); + if(db_fget(e.item, URL)) + fprintf(out, "\n%s\n", db_fget(e.item, URL)); + if(db_fget(e.item, NOTES)) + fprintf(out, "\n%s\n", db_fget(e.item, NOTES)); fprintf(out, "\n"); } @@ -1765,10 +2265,7 @@ elm_alias_export(FILE *out, struct db_enumerator e) db_enumerate_items(e) { alias = mutt_alias_genalias(e.item); get_first_email(email, e.item); - fprintf(out, "%s = %s = %s\n", - alias, - database[e.item][NAME], - email); + fprintf(out, "%s = %s = %s\n",alias,db_name_get(e.item),email); xfree(alias); } @@ -1789,16 +2286,16 @@ spruce_export_database (FILE *out, struct db_enumerator e) { char email[MAX_EMAIL_LEN]; - fprintf (out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n"); + fprintf(out, "# This is a generated file made by abook for the Spruce e-mail client.\n\n"); db_enumerate_items(e) { - if(strcmp (safe_str(database[e.item][EMAIL]), "")) { - get_first_email(email, e.item); + get_first_email(email, e.item); + if(strcmp(email, "")) { fprintf(out, "# Address %d\nName: %s\nEmail: %s\nMemo: %s\n\n", e.item, - database[e.item][NAME], + db_name_get(e.item), email, - safe_str(database[e.item][NOTES]) + safe_str(db_fget(e.item, NOTES)) ); } } @@ -1812,3 +2309,235 @@ spruce_export_database (FILE *out, struct db_enumerator e) * end of Spruce export filter */ +/* + * wanderlust addressbook export filter + */ + +static int +wl_export_database(FILE *out, struct db_enumerator e) +{ + char email[MAX_EMAIL_LEN]; + + fprintf(out, "# Wanderlust address book written by %s\n\n", PACKAGE); + db_enumerate_items(e) { + get_first_email(email, e.item); + if(*email) { + fprintf(out, + "%s\t\"%s\"\t\"%s\"\n", + email, + safe_str(db_fget(e.item, NICK)), + safe_str(db_name_get(e.item)) + ); + } + } + + fprintf (out, "\n# End of address book file.\n"); + + return 0; +} + +/* + * end of wanderlust addressbook export filter + */ + +/* + * BSD calendar export filter + */ + +static int +bsdcal_export_database(FILE *out, struct db_enumerator e) +{ + db_enumerate_items(e) { + int year, month = 0, day = 0; + char *anniversary = db_fget(e.item, ANNIVERSARY); + + if(anniversary) { + if(!parse_date_string(anniversary, &day, &month, &year)) + continue; + + fprintf(out, + _("%02d/%02d\tAnniversary of %s\n"), + month, + day, + safe_str(db_name_get(e.item)) + ); + } + } + + return 0; +} + +// see enum field_types @database.h +static char *conv_table[] = { + "name", + "email", + "address", + "address2", + "city", + "state", + "zip", + "country", + "phone", + "workphone", + "fax", + "mobile", + "nick", + "url", + "notes", + "anniversary", + 0 /* ITEM_FIELDS */ +}; + +static int +find_field_enum(char *s) { + int i = 0; + while (conv_table[i]) { + if(!safe_strcmp(conv_table[i], s)) + return i; + i++; + } + // failed + return -1; +} + +/* Convert a string with named placeholders to + a *printf() compatible string. + Stores the abook field values into ft. */ +void +parse_custom_format(char *s, char *fmt_string, enum field_types *ft) +{ + if(! fmt_string || ! ft) { + fprintf(stderr, _("parse_custom_format: fmt_string or ft not allocated\n")); + exit(EXIT_FAILURE); + } + + char *p, *start, *field_name = NULL; + p = start = s; + + while(*p) { + if(*p == '{') { + start = ++p; + p = strchr(start, '}'); + if(! *p) { + fprintf(stderr, _("parse_custom_format: invalid format\n")); + exit(EXIT_FAILURE); + } + strcat(fmt_string, "%s"); + field_name = strndup(start, (size_t)(p-start)); + *ft = find_field_enum(field_name); + if(*ft == -1) { + fprintf(stderr, _("parse_custom_format: invalid placeholder: {%s}\n"), field_name); + exit(EXIT_FAILURE); + } + + ft++; + p++; + start = p; + } else { + p = strchr(start, '{'); + if(p && *p) { + strncat(fmt_string, start, (size_t)(p-start)); + start = p; + } + else { + strncat( fmt_string, + start, + FORMAT_STRING_LEN - strlen(fmt_string) - 1 ); + break; + } + } + } + *ft = 66; +} + +static int +custom_export_item(FILE *out, int item, char *s, enum field_types *ft); + + +// used to store the format string from --outformatstr when "custom" format is used +// default value overriden in export_file() +extern char *parsed_custom_format; +extern enum field_types *custom_format_fields; + +/* wrapper for custom_export_item: + 1) avoid messing with extern pointer + 2) adds \n + 3) follow the prototype needed for an abook_output_item_filter entry */ +void +custom_print_item(FILE *out, int item) +{ + + if(custom_export_item(out, item, parsed_custom_format, custom_format_fields) == 0) + fprintf(out, "\n"); +} + +static int +custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft) +{ + char *p, *q = 0; + + // if the first character is '!': + // we first check that all fields exist before continuing + if(*fmt == '!') { + enum field_types *ftp = ft; + while(*ft != 66) { + if(! db_fget(item, *ft) ) + return 1; + ft++; + } + ft = ftp; + fmt++; + } + + while (*fmt) { + if(!strncmp(fmt, "%s", 2)) { + fprintf(out, "%s", safe_str(db_fget(item, *ft))); + ft++; + fmt+=2; + } else if (*ft == 66) { + fprintf(out, "%s", fmt); + return 0; + } else { + p = strchr(fmt, '%'); + if(*p) { + q = strndup(fmt, (size_t)(p-fmt)); + fprintf(out, "%s", q); + free(q); + fmt = p; + } + else { + fprintf(out, "%s", fmt); + return 0; + } + } + } + + return 0; +} + +// used to store the format string from --outformatstr when "custom" format is used +// default value overriden from abook.c +extern char custom_format[FORMAT_STRING_LEN]; + +static int +custom_export_database(FILE *out, struct db_enumerator e) +{ + char *format_string = + (char *)malloc(FORMAT_STRING_LEN * sizeof(char*)); + + enum field_types *ft = + (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *)); + + parse_custom_format(custom_format, format_string, ft); + + db_enumerate_items(e) { + if(custom_export_item(out, e.item, format_string, ft) == 0) + fprintf(out, "\n"); + } + return 0; +} + +/* + * end of BSD calendar export filter + */ +
%s