X-Git-Url: https://git.dlugolecki.net.pl/?a=blobdiff_plain;f=gedcom%2Fwrite.c;h=5bc9efa10fd33e841b25b6cae66e0b25a2440569;hb=89ff39aaedee4aa65dec40032686e8b2f8ca272a;hp=e74a571ed336bfaf24767416fc760443b786036c;hpb=4c78192cf17bde2f3c6bff7bb90757c21d1e3792;p=gedcom-parse.git diff --git a/gedcom/write.c b/gedcom/write.c index e74a571..5bc9efa 100644 --- a/gedcom/write.c +++ b/gedcom/write.c @@ -26,12 +26,14 @@ #include "encoding.h" #include "tag_data.h" #include "buffer.h" -#include "utf8.h" +#include "utf8tools.h" #include #include #include #include +#define MAXWRITELEN MAXGEDCLINELEN + const char* encoding = "ASCII"; int write_encoding_details = ONE_BYTE; /* SYS_NEWLINE is defined in config.h */ @@ -60,14 +62,22 @@ const char* terminator[] = { }; void cleanup_write_buffer(); +struct safe_buffer write_buffer = { NULL, 0, NULL, 0, cleanup_write_buffer }; -struct safe_buffer write_buffer = { NULL, 0, cleanup_write_buffer }; +void cleanup_convert_at_buffer(); +struct safe_buffer convert_at_buffer = { NULL, 0, NULL, 0, + cleanup_convert_at_buffer }; void cleanup_write_buffer() { cleanup_buffer(&write_buffer); } +void cleanup_convert_at_buffer() +{ + cleanup_buffer(&convert_at_buffer); +} + int write_simple(Gedcom_write_hndl hndl, int level, char* xref, char* tag, char* value) { @@ -87,18 +97,96 @@ int write_simple(Gedcom_write_hndl hndl, res += safe_buf_append(&write_buffer, " %s", value); res += safe_buf_append(&write_buffer, hndl->term); - converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer), - &conv_fails, &outlen); - - if (converted && (conv_fails == 0)) - write(hndl->filedesc, converted, outlen); + if (utf8_strlen(get_buf_string(&write_buffer)) > MAXGEDCLINELEN) { + gedcom_error(_("Line too long")); + } + else { + converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer), + &conv_fails, &outlen); + + if (converted && (conv_fails == 0)) { + line_no++; + write(hndl->filedesc, converted, outlen); + } + else { + hndl->total_conv_fails += conv_fails; + gedcom_error + (_("Error converting output string: %s (%d conversion failures)"), + strerror(errno), conv_fails); + } + } + } + return 0; +} + +int supports_continuation(int elt_or_rec, int which_continuation) +{ + return tag_data[elt_or_rec].options & which_continuation; +} + +int write_long(Gedcom_write_hndl hndl, int elt_or_rec, + int level, char* xref, char* tag, char* value) +{ + int prefix_len, value_len = 0, term_len; + char* nl_pos = NULL; + if (value) nl_pos = strchr(value, '\n'); + + prefix_len = utf8_strlen(tag) + 3; /* for e.g. "0 INDI " */ + if (level > 9) prefix_len++; + if (xref) prefix_len += utf8_strlen(xref) + 1; + if (value) value_len = utf8_strlen(value); + term_len = strlen(hndl->term); + + if (!nl_pos && prefix_len + value_len + term_len <= MAXWRITELEN) + write_simple(hndl, level, xref, tag, value); + else { + char* value_ptr = value; + int cont_supported = supports_continuation(elt_or_rec, OPT_CONT); + int cont_as_conc = supports_continuation(elt_or_rec, OPT_CONT_AS_CONC); + if (nl_pos && !cont_supported) { + gedcom_error (_("The tag %s doesn't support newlines\n"), tag); + return 1; + } else { - hndl->total_conv_fails += conv_fails; - gedcom_error - (_("Error converting output string: %s (%d conversion failures)"), - strerror(errno), conv_fails); + char value_part[MAXWRITELEN]; + int cont_prefix_len, write_level = level; + cont_prefix_len = utf8_strlen("CONT") + 3; + if (level + 1 > 9) cont_prefix_len++; + + while (value_ptr) { + char* cont_tag = "CONT"; + int line_len = (nl_pos && cont_supported + ? nl_pos - value_ptr : value_len); + + if (prefix_len + line_len + term_len > MAXWRITELEN) { + line_len = MAXWRITELEN - prefix_len - term_len; + if (!cont_as_conc) + cont_tag = "CONC"; + } + + memset(value_part, 0, sizeof(value_part)); + strncpy(value_part, value_ptr, line_len); + write_simple(hndl, write_level, xref, tag, value_part); + + if (line_len < value_len) { + value_ptr = value_ptr + line_len; + value_len = value_len - line_len; + while (*value_ptr == '\n') { + value_ptr++; + value_len--; + } + prefix_len = cont_prefix_len; + write_level = level + 1; + xref = NULL; + tag = cont_tag; + nl_pos = strchr(value_ptr, '\n'); + } + else + value_ptr = NULL; + } } } + return 0; } @@ -117,6 +205,8 @@ int gedcom_write_set_encoding(const char* charset, encoding = new_encoding; write_encoding_details = width | bom; } + else + return 1; } } else { @@ -125,6 +215,8 @@ int gedcom_write_set_encoding(const char* charset, encoding = new_encoding; write_encoding_details = ONE_BYTE; } + else + return 1; } return 0; } @@ -192,14 +284,19 @@ int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails) return result; } -char* get_tag_string(int elt_or_rec, char* tag) +char* get_tag_string(int elt_or_rec, int tag) { - char* result = tag_data[elt_or_rec].tag_name; + int tagnum = tag_data[elt_or_rec].tag; + if (!tagnum) tagnum = tag; - if (result) - return result; - else if (tag) - return tag; + if (tagnum) { + if (tagnum >= TAG_NUM_START && tagnum <= TAG_NUM_END) + return tag_name[tagnum - TAG_NUM_START]; + else { + gedcom_error(_("Not a valid tag: %d"), tagnum); + return NULL; + } + } else { gedcom_error(_("The element or record type '%s' requires a specific tag" "for writing"), @@ -241,40 +338,114 @@ int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent) return hndl->ctxt_level; } -int gedcom_write_record_str(Gedcom_write_hndl hndl, - Gedcom_rec rec, char* tag, - struct xref_value* xref, char* val) +char* convert_at(const char* input) +{ + if (input) { + const char* ptr = input; + reset_buffer(&convert_at_buffer); + while (*ptr) { + if (*ptr == '@') { + SAFE_BUF_ADDCHAR(&convert_at_buffer, '@'); + SAFE_BUF_ADDCHAR(&convert_at_buffer, '@'); + } + else { + SAFE_BUF_ADDCHAR(&convert_at_buffer, *ptr); + } + ptr++; + } + return get_buf_string(&convert_at_buffer); + } + else + return NULL; +} + +int _gedcom_write_val(Gedcom_write_hndl hndl, + int rec_or_elt, int tag, int parent_rec_or_elt, + char* xrefstr, char* val) { int result = 1; int level = 0; char* tag_str = NULL; - char* xref_str = NULL; - tag_str = get_tag_string(rec, tag); - level = get_level(hndl, rec, -1); - if (tag_str && check_type(rec, (val ? GV_CHAR_PTR : GV_NULL))) { - if (xref) - xref_str = xref->string; - result = write_simple(hndl, level, xref_str, tag_str, val); + tag_str = get_tag_string(rec_or_elt, tag); + level = get_level(hndl, rec_or_elt, parent_rec_or_elt); + if (tag_str && (level != -1)) { + if (supports_continuation(rec_or_elt, OPT_CONT|OPT_CONC|OPT_CONT_AS_CONC)) + result = write_long(hndl, rec_or_elt, level, xrefstr, tag_str, val); + else + result = write_simple(hndl, level, xrefstr, tag_str, val); } return result; } +int gedcom_write_record_str(Gedcom_write_hndl hndl, + Gedcom_rec rec, char* xrefstr, char* val) +{ + int result = 1; + if (check_type(rec, (val ? GV_CHAR_PTR : GV_NULL))) + result = _gedcom_write_val(hndl, rec, 0, -1, xrefstr, convert_at(val)); + return result; +} + int gedcom_write_element_str(Gedcom_write_hndl hndl, - Gedcom_elt elt, char* tag, int parent_rec_or_elt, + Gedcom_elt elt, int tag, int parent_rec_or_elt, char* val) { int result = 1; - int level = -1; - char* tag_str = NULL; + if (check_type(elt, (val ? GV_CHAR_PTR : GV_NULL))) + result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL, + convert_at(val)); + return result; +} - tag_str = get_tag_string(elt, tag); - level = get_level(hndl, elt, parent_rec_or_elt); - if (tag_str && (level != -1) - && check_type(elt, (val ? GV_CHAR_PTR : GV_NULL))) { - result = write_simple(hndl, level, NULL, tag_str, val); - } +int gedcom_write_element_xref(Gedcom_write_hndl hndl, + Gedcom_elt elt, int tag, int parent_rec_or_elt, + struct xref_value* val) +{ + int result = 1; + if (check_type(elt, (val ? GV_XREF_PTR : GV_NULL))) + result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL, + val->string); + return result; +} + +int gedcom_write_element_date(Gedcom_write_hndl hndl, + Gedcom_elt elt, int tag, int parent_rec_or_elt, + struct date_value* val) +{ + int result = 1; + if (check_type(elt, (val ? GV_DATE_VALUE : GV_NULL))) + result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL, + gedcom_date_to_string(val)); + return result; +} +int gedcom_write_element_age(Gedcom_write_hndl hndl, + Gedcom_elt elt, int tag, int parent_rec_or_elt, + struct age_value* val) +{ + int result = 1; + if (check_type(elt, (val ? GV_AGE_VALUE : GV_NULL))) + result = _gedcom_write_val(hndl, elt, tag, parent_rec_or_elt, NULL, + gedcom_age_to_string(val)); + return result; +} + +int gedcom_write_user_str(Gedcom_write_hndl hndl, int level, char* tag, + char* xrefstr, char* value) +{ + int result = 1; + if (tag && tag[0] == '_') + result = write_simple(hndl, level, xrefstr, tag, convert_at(value)); + return result; +} + +int gedcom_write_user_xref(Gedcom_write_hndl hndl, int level, char* tag, + char* xrefstr, struct xref_value* val) +{ + int result = 1; + if (tag && tag[0] == '_') + result = write_simple(hndl, level, xrefstr, tag, val->string); return result; }