Writing dates and ages.
[gedcom-parse.git] / gedcom / age.c
1 /* Age 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 <ctype.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include "gedcom_internal.h"
30 #include "buffer.h"
31 #include "age.h"
32
33 struct age_value age_s;
34 struct age_value def_age_val;
35
36 void cleanup_age_buffer();
37 struct safe_buffer age_buffer = { NULL, 0, NULL, 0, cleanup_age_buffer };
38
39 void cleanup_age_buffer()
40 {
41   cleanup_buffer(&age_buffer);
42 }
43
44 void copy_age(struct age_value *to, struct age_value from)
45 {
46   memcpy(to, &from, sizeof(struct age_value));
47 }
48
49 void init_age(struct age_value *age)
50 {
51   age->type = AGE_UNRECOGNIZED;
52   age->mod  = AGE_NO_MODIFIER;
53   age->years = -1;
54   age->months = -1;
55   age->days = -1;
56 }
57
58 int parse_numeric_age(struct age_value *age, const char *ptr)
59 {
60   char *endptr;
61   while (ptr) {
62     long int number = strtol(ptr, &endptr, 10);
63     if (errno == ERANGE || number < 0 || number > INT_MAX) {
64       gedcom_error(_("Number out of range in age"));
65       return 1;
66     }
67     else {
68       ptr = endptr;
69       if (*ptr == '\0')
70         break;
71       else if (*ptr == 'Y' || *ptr == 'y') {
72         if (age->years == -1)
73           age->years = number;
74         else {
75           gedcom_error(_("Duplicate year indication in age"));
76           return 1;
77         }
78       }
79       else if (*ptr == 'M' || *ptr == 'm') {
80         if (age->months == -1)
81           age->months = number;
82         else {
83           gedcom_error(_("Duplicate month indication in age"));
84           return 1;
85         }
86       }
87       else if (*ptr == 'D' || *ptr == 'd') {
88         if (age->days == -1)
89           age->days = number;
90         else {
91           gedcom_error(_("Duplicate day indication in age"));
92           return 1;
93         }
94       }
95       else {
96         gedcom_error(_("Unrecognized indication in age: '%s'"), ptr);
97         return 1;
98       }
99       ptr++;
100       while (*ptr == ' ') ptr++;
101     }
102   }
103   return 0;
104 }
105
106 struct age_value gedcom_parse_age(const char* line_value)
107 {
108   const char *ptr = line_value;
109   init_age(&age_s);
110   init_age(&def_age_val);
111
112   if (*ptr == '<') {
113     age_s.mod = AGE_LESS_THAN;
114     ptr++;
115     while (*ptr == ' ') ptr++;
116   }
117   else if (*ptr == '>') {
118     age_s.mod = AGE_GREATER_THAN;
119     ptr++;
120     while (*ptr == ' ') ptr++;
121   }
122
123   if (isdigit((unsigned char)*ptr)) {
124     int result = parse_numeric_age(&age_s, ptr);
125     if (result == 0) {
126       age_s.type = AGE_NUMERIC;
127     }
128   }
129   else if (!strcasecmp(line_value, "CHILD"))
130     age_s.type = AGE_CHILD;
131   else if (!strcasecmp(line_value, "INFANT"))
132     age_s.type = AGE_INFANT;
133   else if (!strcasecmp(line_value, "STILLBORN"))
134     age_s.type = AGE_STILLBORN;
135   else
136     gedcom_error(_("Unrecognized age format"));
137   if (age_s.type == AGE_UNRECOGNIZED)
138     strncpy(age_s.phrase, line_value, MAX_PHRASE_LEN + 1);
139   return age_s;
140 }
141
142 char* gedcom_age_to_string(struct age_value* val)
143 {
144   int num = 0;
145   reset_buffer(&age_buffer);
146
147   switch (val->mod) {
148     case AGE_LESS_THAN:
149       safe_buf_append(&age_buffer, "<"); break;
150     case AGE_GREATER_THAN:
151       safe_buf_append(&age_buffer, ">"); break;
152     default:
153       break;
154   }
155
156   switch (val->type) {
157     case AGE_UNRECOGNIZED:
158       return val->phrase; break;
159     case AGE_CHILD:
160       safe_buf_append(&age_buffer, "CHILD"); break;
161     case AGE_INFANT:
162       safe_buf_append(&age_buffer, "INFANT"); break;
163     case AGE_STILLBORN:
164       safe_buf_append(&age_buffer, "STILLBORN"); break;
165     case AGE_NUMERIC:
166       if (val->years != -1) {
167         num = 1;
168         safe_buf_append(&age_buffer, "%dy", val->years);
169       }
170       if (val->months != -1) {
171         if (num)
172           safe_buf_append(&age_buffer, " ");
173         num = 1;
174         safe_buf_append(&age_buffer, "%dm", val->months);
175       }
176       if (val->days != -1) {
177         if (num)
178           safe_buf_append(&age_buffer, " ");
179         num = 1;
180         safe_buf_append(&age_buffer, "%dd", val->days);
181       }
182       break;
183     default:
184       break;
185   }
186   
187   return get_buf_string(&age_buffer);
188 }