X-Git-Url: https://git.dlugolecki.net.pl/?a=blobdiff_plain;f=gedcom%2Fdate.c;h=a6a8eb52a62d278828e21eb99f8c69a2983881fb;hb=6103dd898c4de86c68891cc0222543988a2caab7;hp=1a2232a11f05a54376eec3e59ce7cf5cce4aa40e;hpb=fffe9618ff8a5cfcac207f231b531615dfb0e38f;p=gedcom-parse.git diff --git a/gedcom/date.c b/gedcom/date.c index 1a2232a..a6a8eb5 100644 --- a/gedcom/date.c +++ b/gedcom/date.c @@ -23,6 +23,9 @@ #include "gedcom_internal.h" #include "sdncal.h" +#include "buffer.h" +#include "compat.h" +#include #include "date.h" struct date_value dv_s; @@ -33,6 +36,14 @@ struct date def_date; const char* curr_line_value; +void cleanup_date_buffer(); +struct safe_buffer date_buffer = { NULL, 0, NULL, 0, cleanup_date_buffer }; + +void cleanup_date_buffer() +{ + cleanup_buffer(&date_buffer); +} + int max_month[] = { 12, /* CAL_GREGORIAN */ 12, /* CAL_JULIAN */ 13, /* CAL_HEBREW */ @@ -40,17 +51,66 @@ int max_month[] = { 12, /* CAL_GREGORIAN */ 0 /* CAL_UNKNOWN */ }; -typedef long int (*cal_func_type) (int, int, int); +char* month_name[][13] = +{ /* CAL_GREGORIAN */ + { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", + "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }, + /* CAL_JULIAN */ + { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", + "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }, + /* CAL_JEWISH */ + { "TSH", "CSH", "KSL", "TVT", "SHV", "ADR", "ADS", + "NSN", "IYR", "SVN", "TMZ", "AAV", "ELL" }, + /* CAL_FRENCH_REF */ + { "VEND", "BRUM", "FRIM", "NIVO", "PLUV", "VENT", "GERM", + "FLOR", "PRAI", "MESS", "THER", "FRUC", "COMP" } +}; + +typedef long int (*to_sdn_func_type) (int, int, int); +typedef void (*from_sdn_func_type) (long int, int*, int*, int*); -cal_func_type cal_func[] = { &GregorianToSdn, /* CAL_GREGORIAN */ - &JulianToSdn, /* CAL_JULIAN */ - &JewishToSdn, /* CAL_JEWISH */ - &FrenchToSdn /* CAL_FRENCH_REV */ - }; +to_sdn_func_type to_sdn_func[] = { + &GregorianToSdn, /* CAL_GREGORIAN */ + &JulianToSdn, /* CAL_JULIAN */ + &JewishToSdn, /* CAL_JEWISH */ + &FrenchToSdn /* CAL_FRENCH_REV */ +}; -void copy_date(struct date *to, struct date from) +from_sdn_func_type from_sdn_func[] = { + &SdnToGregorian, /* CAL_GREGORIAN */ + &SdnToJulian, /* CAL_JULIAN */ + &SdnToJewish, /* CAL_JEWISH */ + &SdnToFrench /* CAL_FRENCH_REV */ +}; + +long int checkedCalToSdn(Calendar_type cal, int year, int month, int day) { - memcpy(to, &from, sizeof(struct date)); + int y, m, d; + long int sdn = (*to_sdn_func[cal])(year,month, day); + if (sdn <= 0) + return -1; + else { + (*from_sdn_func[cal])(sdn, &y, &m, &d); + if ((year == y) && (month == m) && (day == d)) + return sdn; + else + return -1; + } +} + +int checkedSdnToCal(Calendar_type cal, long int sdn, + int* year, int* month, int* day) +{ + (*from_sdn_func[cal])(sdn, year, month, day); + if (*year > 0 && *month > 0 && *day > 0) + return 1; + else + return 0; +} + +void copy_date(struct date *to, struct date *from) +{ + memcpy(to, from, sizeof(struct date)); } void init_date(struct date *d) @@ -68,63 +128,390 @@ void init_date(struct date *d) d->sdn2 = -1; } -struct date_value make_date_value(Date_value_type t, struct date d1, - struct date d2, const char* p) +struct date_value* make_date_value(Date_value_type t, struct date *d1, + struct date *d2, const char* p) { dv_s.type = t; copy_date(&dv_s.date1, d1); copy_date(&dv_s.date2, d2); strncpy(dv_s.phrase, p, MAX_PHRASE_LEN + 1); - return dv_s; + return &dv_s; } -void make_date_complete(struct date *d) +/* PRE: d->cal != CAL_UNKNOWN + INPUT: d->day, d->month, d->year + OUTPUT: d->type, d->sdn1, d->sdn2 +*/ +int numbers_to_sdn(struct date *d) { - if (d->cal == CAL_UNKNOWN) + int result = 0; + if (d->cal == CAL_UNKNOWN) { d->type = DATE_UNRECOGNIZED; + gedcom_date_error(_("Cannot compute SDN for unknown calendar type")); + result = 1; + } else { + struct date begin_date; struct date end_date; - cal_func_type to_sdn; + copy_date(&begin_date, d); if (d->day == -1 || d->month == -1 || d->year == -1) { d->type = DATE_BOUNDED; - copy_date(&end_date, *d); - if (d->month == -1) { - d->month = 1; end_date.month = 1; - d->day = 1; end_date.day = 1; + copy_date(&end_date, d); + if (begin_date.month == -1) { + begin_date.month = 1; end_date.month = 1; + begin_date.day = 1; end_date.day = 1; end_date.year += 1; } - else if (d->day == -1) { - d->day = 1; end_date.day = 1; + else if (begin_date.day == -1) { + begin_date.day = 1; end_date.day = 1; end_date.month += 1; if (end_date.month > max_month[d->cal]) { end_date.month -= max_month[d->cal]; end_date.year += 1; } } + else { + gedcom_date_error(_("Year has to be given in bounded date")); + result = 1; + } } else { d->type = DATE_EXACT; } - to_sdn = cal_func[d->cal]; - d->sdn1 = (*to_sdn)(d->year, d->month, d->day); - if (d->type == DATE_BOUNDED) { - d->sdn2 = (*to_sdn)(end_date.year, end_date.month, end_date.day); - d->sdn2 -= 1; + d->sdn1 = checkedCalToSdn(d->cal, begin_date.year, begin_date.month, + begin_date.day); + if (d->sdn1 == -1) { + gedcom_date_error(_("Error converting date: year %d, month %d, day %d"), + begin_date.year, begin_date.month, begin_date.day); + result = 1; + } + else + if (d->type == DATE_BOUNDED) { + d->sdn2 = checkedCalToSdn(d->cal, end_date.year, end_date.month, + end_date.day); + if (d->sdn2 == -1) { + gedcom_date_error(_("Error converting date: year %d, month %d, day %d"), + end_date.year, end_date.month, end_date.day); + result = 1; + } + else + d->sdn2 -= 1; + } + } + return result; +} + +/* PRE: d->cal != CAL_UNKNOWN + INPUT: d->type, d->sdn1, d->sdn2 + OUTPUT: d->day, d->month, d->year +*/ +int sdn_to_numbers(struct date *d) +{ + int result = 0; + if (d->cal == CAL_UNKNOWN) { + gedcom_date_error(_("Cannot compute from SDN for unknown calendar type")); + result = 1; + } + else { + struct date begin_date; + struct date end_date; + + if (d->sdn1 <= 0) { + gedcom_date_error(_("SDN 1 should be bigger than zero")); + result = 1; + } + else { + copy_date(&begin_date, d); + if (!checkedSdnToCal(d->cal, d->sdn1, &begin_date.year, + &begin_date.month, &begin_date.day)) { + gedcom_date_error(_("SDN 1 isn't a valid date in the given calendar")); + result = 1; + } + else { + switch (d->type) { + case DATE_EXACT: + if (d->sdn2 != -1) { + gedcom_date_error(_("SDN 2 should be -1 for exact dates")); + result = 1; + } + break; + case DATE_BOUNDED: + if (d->sdn2 <= 0) { + gedcom_date_error(_("SDN 2 should be bigger than zero")); + result = 1; + } + else if (d->sdn2 <= d->sdn1) { + gedcom_date_error(_("SDN 2 should be bigger than SDN 1")); + result = 1; + } + else { + copy_date(&end_date, d); + if (!checkedSdnToCal(d->cal, d->sdn2, &end_date.year, + &end_date.month, &end_date.day)) { + gedcom_date_error(_("SDN 2 isn't a valid date in the given calendar")); + result = 1; + } + else { + if (begin_date.year == end_date.year) { + if (begin_date.month == end_date.month) { + if (begin_date.day == end_date.day) { + /* year, month and day are relevant */ + } + else { + /* year and month are relevant */ + begin_date.day = -1; + } + } + else { + /* only year is relevant */ + begin_date.month = -1; + begin_date.day = -1; + } + } + else { + gedcom_date_error(_("SDN1/SDN2 isn't a bounded date")); + result = 1; + } + } + } + break; + default: + break; + } + } + d->year = begin_date.year; + d->month = begin_date.month; + d->day = begin_date.day; + } + } + return result; +} + +/* PRE: d->cal != CAL_UNKNOWN + INPUT: d->day_str, d->month_str, d->year_str + OUTPUT: d->day, d->month, d->year, d->year_type +*/ +int strings_to_numbers(struct date *d) +{ + int result = 0; + if (d->cal == CAL_UNKNOWN) { + gedcom_date_error(_("Cannot compute months for unknown calendar type")); + result = 1; + } + else { + if (d->day_str[0]) { + d->day = get_day_num(d->day_str); + if (d->day == -1) result = 1; + } + else + d->day = -1; + + if (d->month_str[0]) { + d->month = get_month_num(d->cal, d->month_str); + if (d->month == -1) result = 1; + } + else + d->month = -1; + + d->year = get_year_num(d->year_str, &d->year_type); + if (d->year == -1) result = 1; + } + + return result; +} + +/* PRE: d->cal != CAL_UNKNOWN + INPUT: d->day, d->month, d->year, d->year_type + OUTPUT: d->day_str, d->month_str, d->year_str +*/ +int numbers_to_strings(struct date *d) +{ + int result = 0; + if (d->cal == CAL_UNKNOWN) { + gedcom_date_error(_("Cannot compute month names for unknown calendar type")); + result = 1; + } + else { + if (d->day != -1) + sprintf(d->day_str, "%d", d->day); + + if (d->month > 0 && d->month <= max_month[d->cal]) + strcpy(d->month_str, month_name[d->cal][d->month - 1]); + + if (d->year_type == YEAR_SINGLE) + sprintf(d->year_str, "%d", d->year); + else + sprintf(d->year_str, "%d/%d", d->year - 1, (d->year % 100)); + } + return result; +} + +int gedcom_normalize_date(Date_input input, struct date_value *val) +{ + int result = 0; + if (val->type != DV_PHRASE) { + switch (input) { + case DI_FROM_STRINGS: + result |= strings_to_numbers(&val->date1); + result |= numbers_to_sdn(&val->date1); + if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) { + result |= strings_to_numbers(&val->date2); + result |= numbers_to_sdn(&val->date2); + } + break; + case DI_FROM_NUMBERS: + result |= numbers_to_strings(&val->date1); + result |= numbers_to_sdn(&val->date1); + if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) { + result |= numbers_to_strings(&val->date2); + result |= numbers_to_sdn(&val->date2); + } + break; + case DI_FROM_SDN: + result |= sdn_to_numbers(&val->date1); + result |= numbers_to_strings(&val->date1); + if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) { + result |= sdn_to_numbers(&val->date2); + result |= numbers_to_strings(&val->date2); + } + break; + default: + break; + } + } + return result; +} + +struct date_value* gedcom_new_date_value(const struct date_value* copy_from) +{ + struct date_value* dv_ptr; + dv_ptr = (struct date_value*) malloc(sizeof(struct date_value)); + if (!dv_ptr) + MEMORY_ERROR; + else { + if (copy_from) + memcpy(dv_ptr, copy_from, sizeof(struct date_value)); + else { + dv_ptr->type = DV_NO_MODIFIER; + init_date(&dv_ptr->date1); + init_date(&dv_ptr->date2); + dv_ptr->phrase[0] = '\0'; } } + return dv_ptr; } struct date_value gedcom_parse_date(const char* line_value) { + int result = 0; + init_date(&dv_s.date1); + init_date(&dv_s.date2); init_date(&date_s); init_date(&def_date); curr_line_value = line_value; - init_gedcom_date_lex(line_value); - gedcom_date_parse(); - close_gedcom_date_lex(); - make_date_complete(&dv_s.date1); - make_date_complete(&dv_s.date2); + if (compat_mode(C_NO_REQUIRED_VALUES) + && !strncmp(curr_line_value, VALUE_IF_MISSING, 2)) { + gedcom_date_error(_("Empty value changed to '%s'"), VALUE_IF_MISSING); + result = 1; + } + else { + compat_date_start(); + init_gedcom_date_lex(line_value); + gedcom_date_parse(); + close_gedcom_date_lex(); + if (compat_date_check(&dv_s, &curr_line_value)) { + init_gedcom_date_lex(curr_line_value); + gedcom_date_parse(); + close_gedcom_date_lex(); + } + if (dv_s.date1.cal != CAL_UNKNOWN) + result |= numbers_to_sdn(&dv_s.date1); + if (dv_s.date2.cal != CAL_UNKNOWN) + result |= numbers_to_sdn(&dv_s.date2); + } + if (result != 0) { + gedcom_date_error(_("Putting date '%s' in 'phrase' member"), + curr_line_value); + make_date_value(DV_PHRASE, &dv_s.date1, &dv_s.date2, curr_line_value); + } return dv_s; } +void write_date(const struct date* d) +{ + if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0) + gedcom_error(_("Date is not normalized: some fields are invalid")); + else { + switch (d->cal) { + case CAL_GREGORIAN: break; + case CAL_JULIAN: + safe_buf_append(&date_buffer, "@#DJULIAN@ "); break; + case CAL_HEBREW: + safe_buf_append(&date_buffer, "@#DHEBREW@ "); break; + case CAL_FRENCH_REV: + safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break; + case CAL_UNKNOWN: + safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break; + default: + break; + } + if (d->day_str[0]) + safe_buf_append(&date_buffer, "%s ", d->day_str); + if (d->month_str[0]) + safe_buf_append(&date_buffer, "%s ", d->month_str); + safe_buf_append(&date_buffer, "%s", d->year_str); + } +} + +char* gedcom_date_to_string(const struct date_value* val) +{ + init_buffer(&date_buffer); + reset_buffer(&date_buffer); + + switch (val->type) { + case DV_NO_MODIFIER: + write_date(&val->date1); break; + case DV_BEFORE: + safe_buf_append(&date_buffer, "BEF "); + write_date(&val->date1); break; + case DV_AFTER: + safe_buf_append(&date_buffer, "AFT "); + write_date(&val->date1); break; + case DV_BETWEEN: + safe_buf_append(&date_buffer, "BET "); + write_date(&val->date1); + safe_buf_append(&date_buffer, " AND "); + write_date(&val->date2); break; + case DV_FROM: + safe_buf_append(&date_buffer, "FROM "); + write_date(&val->date1); break; + case DV_TO: + safe_buf_append(&date_buffer, "TO "); + write_date(&val->date1); break; + case DV_FROM_TO: + safe_buf_append(&date_buffer, "FROM "); + write_date(&val->date1); + safe_buf_append(&date_buffer, " TO "); + write_date(&val->date2); break; + case DV_ABOUT: + safe_buf_append(&date_buffer, "ABT "); + write_date(&val->date1); break; + case DV_CALCULATED: + safe_buf_append(&date_buffer, "CAL "); + write_date(&val->date1); break; + case DV_ESTIMATED: + safe_buf_append(&date_buffer, "EST "); + write_date(&val->date1); break; + case DV_INTERPRETED: + safe_buf_append(&date_buffer, "INT "); + write_date(&val->date1); + safe_buf_append(&date_buffer, " (%s)", val->phrase); break; + case DV_PHRASE: + safe_buf_append(&date_buffer, "(%s)", val->phrase); break; + default: + break; + } + + return get_buf_string(&date_buffer); +}