More verbosity in date errors.
[gedcom-parse.git] / gedcom / date.c
1 /* Date manipulation routines.
2    Copyright (C) 2001,2002 The Genes Development Team
3    This file is part of the Gedcom parser library.
4    Contributed by Peter Verthez <Peter.Verthez@advalvas.be>, 2001.
5
6    The Gedcom parser library is free software; you can redistribute it
7    and/or modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The Gedcom parser library is distributed in the hope that it will be
12    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the Gedcom parser library; if not, write to the
18    Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 /* $Id$ */
22 /* $Name$ */
23
24 #include "gedcom_internal.h"
25 #include "sdncal.h"
26 #include "buffer.h"
27 #include "compat.h"
28 #include <string.h>
29 #include "date.h"
30
31 struct date_value dv_s;
32 struct date date_s;
33
34 struct date_value def_date_val;
35 struct date def_date;
36
37 const char* curr_line_value;
38
39 void cleanup_date_buffer();
40 struct safe_buffer date_buffer = { NULL, 0, NULL, 0, cleanup_date_buffer };
41
42 void cleanup_date_buffer()
43 {
44   cleanup_buffer(&date_buffer);
45 }
46
47 int max_month[] = { 12,  /* CAL_GREGORIAN */
48                     12,  /* CAL_JULIAN */
49                     13,  /* CAL_HEBREW */
50                     13,  /* CAL_FRENCH_REV */
51                     0    /* CAL_UNKNOWN */
52                   };
53
54 char* month_name[][13] =
55 { /* CAL_GREGORIAN */
56   { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
57     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
58   /* CAL_JULIAN */
59   { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
60     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
61   /* CAL_JEWISH */
62   { "TSH", "CSH", "KSL", "TVT", "SHV", "ADR", "ADS",
63     "NSN", "IYR", "SVN", "TMZ", "AAV", "ELL" },
64   /* CAL_FRENCH_REF */
65   { "VEND", "BRUM", "FRIM", "NIVO", "PLUV", "VENT", "GERM",
66     "FLOR", "PRAI", "MESS", "THER", "FRUC", "COMP" }
67 };
68
69 typedef long int (*to_sdn_func_type) (int, int, int);
70 typedef void (*from_sdn_func_type) (long int, int*, int*, int*);
71
72 to_sdn_func_type to_sdn_func[] = {
73   &GregorianToSdn,   /* CAL_GREGORIAN */
74   &JulianToSdn,      /* CAL_JULIAN */
75   &JewishToSdn,      /* CAL_JEWISH */
76   &FrenchToSdn       /* CAL_FRENCH_REV */
77 };
78
79 from_sdn_func_type from_sdn_func[] = {
80   &SdnToGregorian,   /* CAL_GREGORIAN */
81   &SdnToJulian,      /* CAL_JULIAN */
82   &SdnToJewish,      /* CAL_JEWISH */
83   &SdnToFrench       /* CAL_FRENCH_REV */
84 };
85
86 long int checkedCalToSdn(Calendar_type cal, int year, int month, int day)
87 {
88   int y, m, d;
89   long int sdn = (*to_sdn_func[cal])(year,month, day);
90   if (sdn <= 0)
91     return -1;
92   else {
93     (*from_sdn_func[cal])(sdn, &y, &m, &d);
94     if ((year == y) && (month == m) && (day == d))
95       return sdn;
96     else
97       return -1;
98   }
99 }
100
101 int checkedSdnToCal(Calendar_type cal, long int sdn,
102                     int* year, int* month, int* day)
103 {
104   (*from_sdn_func[cal])(sdn, year, month, day);
105   if (*year > 0 && *month > 0 && *day > 0)
106     return 1;
107   else
108     return 0;
109 }
110
111 void copy_date(struct date *to, struct date *from)
112 {
113   memcpy(to, from, sizeof(struct date));
114 }
115
116 void init_date(struct date *d)
117 {
118   d->cal = CAL_UNKNOWN;
119   d->day_str[0] = '\0';
120   d->month_str[0] = '\0';
121   d->year_str[0] = '\0';
122   d->day = -1;
123   d->month = -1;
124   d->year = -1;
125   d->year_type = YEAR_SINGLE;
126   d->type = DATE_UNRECOGNIZED;
127   d->sdn1 = -1;
128   d->sdn2 = -1;
129 }
130
131 struct date_value* make_date_value(Date_value_type t, struct date *d1,
132                                   struct date *d2, const char* p)
133 {
134   dv_s.type = t;
135   copy_date(&dv_s.date1, d1);
136   copy_date(&dv_s.date2, d2);
137   strncpy(dv_s.phrase, p, MAX_PHRASE_LEN + 1);
138   return &dv_s;
139 }
140
141 /* PRE:     d->cal != CAL_UNKNOWN
142    INPUT:   d->day, d->month, d->year
143    OUTPUT:  d->type, d->sdn1, d->sdn2
144 */
145 int numbers_to_sdn(struct date *d)
146 {
147   int result = 0;
148   if (d->cal == CAL_UNKNOWN) {
149     d->type = DATE_UNRECOGNIZED;
150     gedcom_date_error(_("Cannot compute SDN for unknown calendar type"));
151     result = 1;
152   }
153   else {
154     struct date begin_date;
155     struct date end_date;
156     copy_date(&begin_date, d);
157     if (d->day == -1 || d->month == -1 || d->year == -1) {
158       d->type = DATE_BOUNDED;
159       copy_date(&end_date, d);
160       if (begin_date.month == -1) {
161         begin_date.month = 1; end_date.month = 1;
162         begin_date.day   = 1; end_date.day   = 1;
163         end_date.year += 1;
164       }
165       else if (begin_date.day == -1) {
166         begin_date.day   = 1; end_date.day   = 1;
167         end_date.month += 1;
168         if (end_date.month > max_month[d->cal]) {
169           end_date.month -= max_month[d->cal];
170           end_date.year  += 1;
171         }
172       }
173       else {
174         gedcom_date_error(_("Year has to be given in bounded date"));
175         result = 1;
176       }
177     }
178     else {
179       d->type = DATE_EXACT;
180     }
181
182     d->sdn1 = checkedCalToSdn(d->cal, begin_date.year, begin_date.month,
183                               begin_date.day);
184     if (d->sdn1 == -1) {
185       gedcom_date_error(_("Error converting date: year %d, month %d, day %d"),
186                    begin_date.year, begin_date.month, begin_date.day);
187       result = 1;
188     }
189     else
190       if (d->type == DATE_BOUNDED) {
191         d->sdn2 = checkedCalToSdn(d->cal, end_date.year, end_date.month,
192                                   end_date.day);
193         if (d->sdn2 == -1) {
194           gedcom_date_error(_("Error converting date: year %d, month %d, day %d"),
195                        end_date.year, end_date.month, end_date.day);
196           result = 1;
197         }
198         else
199           d->sdn2 -= 1;
200       }
201   }
202   return result;
203 }
204
205 /* PRE:     d->cal != CAL_UNKNOWN
206    INPUT:   d->type, d->sdn1, d->sdn2
207    OUTPUT:  d->day, d->month, d->year
208 */
209 int sdn_to_numbers(struct date *d)
210 {
211   int result = 0;
212   if (d->cal == CAL_UNKNOWN) {
213     gedcom_date_error(_("Cannot compute from SDN for unknown calendar type"));
214     result = 1;
215   }
216   else {
217     struct date begin_date;
218     struct date end_date;
219
220     if (d->sdn1 <= 0) {
221       gedcom_date_error(_("SDN 1 should be bigger than zero"));
222       result = 1;
223     }
224     else {
225       copy_date(&begin_date, d);
226       if (!checkedSdnToCal(d->cal, d->sdn1, &begin_date.year,
227                            &begin_date.month, &begin_date.day)) {
228         gedcom_date_error(_("SDN 1 isn't a valid date in the given calendar"));
229         result = 1;
230       }
231       else {
232         switch (d->type) {
233           case DATE_EXACT:
234             if (d->sdn2 != -1) {
235               gedcom_date_error(_("SDN 2 should be -1 for exact dates"));
236               result = 1;
237             }
238             break;
239           case DATE_BOUNDED:
240             if (d->sdn2 <= 0) {
241               gedcom_date_error(_("SDN 2 should be bigger than zero"));
242               result = 1;
243             }
244             else if (d->sdn2 <= d->sdn1) {
245               gedcom_date_error(_("SDN 2 should be bigger than SDN 1"));
246               result = 1;
247             }
248             else {
249               copy_date(&end_date, d);
250               if (!checkedSdnToCal(d->cal, d->sdn2, &end_date.year,
251                                    &end_date.month, &end_date.day)) {
252                 gedcom_date_error(_("SDN 2 isn't a valid date in the given calendar"));
253                 result = 1;
254               }
255               else {
256                 if (begin_date.year == end_date.year) {
257                   if (begin_date.month == end_date.month) {
258                     if (begin_date.day == end_date.day) {
259                       /* year, month and day are relevant */
260                     }
261                     else {
262                       /* year and month are relevant */
263                       begin_date.day = -1;
264                     }
265                   }
266                   else {
267                     /* only year is relevant */
268                     begin_date.month = -1;
269                     begin_date.day   = -1;
270                   }
271                 }
272                 else {
273                   gedcom_date_error(_("SDN1/SDN2 isn't a bounded date"));
274                   result = 1;
275                 }
276               }
277             }
278             break;
279           default:
280             break;
281         }
282       }
283       d->year  = begin_date.year;
284       d->month = begin_date.month;
285       d->day   = begin_date.day;
286     }
287   }
288   return result;
289 }
290
291 /* PRE:     d->cal != CAL_UNKNOWN
292    INPUT:   d->day_str, d->month_str, d->year_str
293    OUTPUT:  d->day, d->month, d->year, d->year_type
294 */
295 int strings_to_numbers(struct date *d)
296 {
297   int result = 0;
298   if (d->cal == CAL_UNKNOWN) {
299     gedcom_date_error(_("Cannot compute months for unknown calendar type"));
300     result = 1;
301   }
302   else {
303     if (d->day_str[0]) {
304       d->day = get_day_num(d->day_str);
305       if (d->day == -1) result = 1;
306     }
307     else
308       d->day = -1;
309     
310     if (d->month_str[0]) {
311       d->month = get_month_num(d->cal, d->month_str);
312       if (d->month == -1) result = 1;
313     }
314     else
315       d->month = -1;
316     
317     d->year = get_year_num(d->year_str, &d->year_type);
318     if (d->year == -1) result = 1;
319   }
320   
321   return result;
322 }
323
324 /* PRE:     d->cal != CAL_UNKNOWN
325    INPUT:   d->day, d->month, d->year, d->year_type
326    OUTPUT:  d->day_str, d->month_str, d->year_str
327 */
328 int numbers_to_strings(struct date *d)
329 {
330   int result = 0;
331   if (d->cal == CAL_UNKNOWN) {
332     gedcom_date_error(_("Cannot compute month names for unknown calendar type"));
333     result = 1;
334   }
335   else {
336     if (d->day != -1)
337       sprintf(d->day_str, "%d", d->day);
338     
339     if (d->month > 0 && d->month <= max_month[d->cal])
340       strcpy(d->month_str, month_name[d->cal][d->month - 1]);
341     
342     if (d->year_type == YEAR_SINGLE)
343       sprintf(d->year_str, "%d", d->year);
344     else
345       sprintf(d->year_str, "%d/%d", d->year - 1, (d->year % 100));
346   }
347   return result;
348 }
349
350 int gedcom_normalize_date(Date_input input, struct date_value *val)
351 {
352   int result = 0;
353   if (val->type != DV_PHRASE) {
354     switch (input) {
355       case DI_FROM_STRINGS:
356         result |= strings_to_numbers(&val->date1);
357         result |= numbers_to_sdn(&val->date1);
358         if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
359           result |= strings_to_numbers(&val->date2);
360           result |= numbers_to_sdn(&val->date2);
361         }
362         break;
363       case DI_FROM_NUMBERS:
364         result |= numbers_to_strings(&val->date1);
365         result |= numbers_to_sdn(&val->date1);
366         if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
367           result |= numbers_to_strings(&val->date2);
368           result |= numbers_to_sdn(&val->date2);
369         }
370         break;
371       case DI_FROM_SDN:
372         result |= sdn_to_numbers(&val->date1);
373         result |= numbers_to_strings(&val->date1);
374         if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
375           result |= sdn_to_numbers(&val->date2);
376           result |= numbers_to_strings(&val->date2);
377         }
378         break;
379       default:
380         break;
381     }
382   }
383   return result;
384 }
385
386 struct date_value* gedcom_new_date_value(const struct date_value* copy_from)
387 {
388   struct date_value* dv_ptr;
389   dv_ptr = (struct date_value*) malloc(sizeof(struct date_value));
390   if (!dv_ptr)
391     MEMORY_ERROR;
392   else {
393     if (copy_from)
394       memcpy(dv_ptr, copy_from, sizeof(struct date_value));
395     else {
396       dv_ptr->type = DV_NO_MODIFIER;
397       init_date(&dv_ptr->date1);
398       init_date(&dv_ptr->date2);
399       dv_ptr->phrase[0] = '\0';
400     }
401   }
402   return dv_ptr;
403 }
404
405 struct date_value gedcom_parse_date(const char* line_value)
406 {
407   int result = 0;
408   init_date(&dv_s.date1);
409   init_date(&dv_s.date2);
410   init_date(&date_s);
411   init_date(&def_date);
412   curr_line_value = line_value;
413   if (compat_mode(C_NO_REQUIRED_VALUES)
414       && !strncmp(curr_line_value, VALUE_IF_MISSING, 2)) {
415     gedcom_date_error(_("Empty value changed to '%s'"), VALUE_IF_MISSING);
416     result = 1;
417   }
418   else {
419     init_gedcom_date_lex(line_value);
420     gedcom_date_parse();
421     close_gedcom_date_lex();
422     if (dv_s.date1.cal != CAL_UNKNOWN)
423       result |= numbers_to_sdn(&dv_s.date1);
424     if (dv_s.date2.cal != CAL_UNKNOWN)
425       result |= numbers_to_sdn(&dv_s.date2);
426   }
427   if (result != 0) {
428     gedcom_date_error(_("Putting date '%s' in 'phrase' member"),
429                       curr_line_value);
430     make_date_value(DV_PHRASE, &dv_s.date1, &dv_s.date2, curr_line_value);
431   }
432   return dv_s;
433 }
434
435 void write_date(const struct date* d)
436 {
437   if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
438     gedcom_error(_("Date is not normalized: some fields are invalid"));
439   else {
440     switch (d->cal) {
441       case CAL_GREGORIAN: break;
442       case CAL_JULIAN:
443         safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
444       case CAL_HEBREW:
445         safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
446       case CAL_FRENCH_REV:
447         safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
448       case CAL_UNKNOWN:
449         safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
450       default:
451         break;
452     }
453     if (d->day_str[0])
454       safe_buf_append(&date_buffer, "%s ", d->day_str);
455     if (d->month_str[0])
456       safe_buf_append(&date_buffer, "%s ", d->month_str);
457     safe_buf_append(&date_buffer, "%s", d->year_str);
458   }
459 }
460
461 char* gedcom_date_to_string(const struct date_value* val)
462 {
463   init_buffer(&date_buffer);
464   reset_buffer(&date_buffer);
465   
466   switch (val->type) {
467     case DV_NO_MODIFIER:
468       write_date(&val->date1); break;
469     case DV_BEFORE:
470       safe_buf_append(&date_buffer, "BEF ");
471       write_date(&val->date1); break;
472     case DV_AFTER:
473       safe_buf_append(&date_buffer, "AFT ");
474       write_date(&val->date1); break;
475     case DV_BETWEEN:
476       safe_buf_append(&date_buffer, "BET ");
477       write_date(&val->date1);
478       safe_buf_append(&date_buffer, " AND ");
479       write_date(&val->date2); break;
480     case DV_FROM:
481       safe_buf_append(&date_buffer, "FROM ");
482       write_date(&val->date1); break;
483     case DV_TO:
484       safe_buf_append(&date_buffer, "TO ");
485       write_date(&val->date1); break;
486     case DV_FROM_TO:
487       safe_buf_append(&date_buffer, "FROM ");
488       write_date(&val->date1);
489       safe_buf_append(&date_buffer, " TO ");
490       write_date(&val->date2); break;
491     case DV_ABOUT:
492       safe_buf_append(&date_buffer, "ABT ");
493       write_date(&val->date1); break;
494     case DV_CALCULATED:
495       safe_buf_append(&date_buffer, "CAL ");
496       write_date(&val->date1); break;
497     case DV_ESTIMATED:
498       safe_buf_append(&date_buffer, "EST ");
499       write_date(&val->date1); break;
500     case DV_INTERPRETED:
501       safe_buf_append(&date_buffer, "INT ");
502       write_date(&val->date1);
503       safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
504     case DV_PHRASE:
505       safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
506     default:
507       break;
508   }
509   
510   return get_buf_string(&date_buffer);
511 }