renamed the package to libgedcom-dev
[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 /** This function creates a new age_value struct and initializes it properly,
107     or copies an existing age value.
108
109     \param copy_from  A given struct age_value to copy (or \c NULL).
110
111     \return If the parameter \c copy_from is NULL, a new value is created and
112     given initial values.  If it is non-NULL, the given value is copied into
113     a new age value.  In both cases, the new value is returned.
114 */
115 struct age_value* gedcom_new_age_value(const struct age_value* copy_from)
116 {
117   struct age_value* age_ptr;
118   age_ptr = (struct age_value*) malloc(sizeof(struct age_value));
119   if (! age_ptr)
120     MEMORY_ERROR;
121   else {
122     if (copy_from)
123       memcpy(age_ptr, copy_from, sizeof(struct age_value));
124     else 
125       init_age(age_ptr);
126   }
127   return age_ptr;
128 }
129
130 /** This function allows to convert the given \c line_value into a struct
131     age_value.
132     
133     \param line_value A string containing the age to parse
134
135     \return The parsed age; note that this return value is statically
136     allocated, and is thus overwritten on each call.
137 */
138 struct age_value gedcom_parse_age(const char* line_value)
139 {
140   const char *ptr = line_value;
141   init_age(&age_s);
142   init_age(&def_age_val);
143
144   if (*ptr == '<') {
145     age_s.mod = AGE_LESS_THAN;
146     ptr++;
147     while (*ptr == ' ') ptr++;
148   }
149   else if (*ptr == '>') {
150     age_s.mod = AGE_GREATER_THAN;
151     ptr++;
152     while (*ptr == ' ') ptr++;
153   }
154
155   if (isdigit((unsigned char)*ptr)) {
156     int result = parse_numeric_age(&age_s, ptr);
157     if (result == 0) {
158       age_s.type = AGE_NUMERIC;
159     }
160   }
161   else if (!strcasecmp(line_value, "CHILD"))
162     age_s.type = AGE_CHILD;
163   else if (!strcasecmp(line_value, "INFANT"))
164     age_s.type = AGE_INFANT;
165   else if (!strcasecmp(line_value, "STILLBORN"))
166     age_s.type = AGE_STILLBORN;
167   else
168     gedcom_error(_("Unrecognized age format"));
169   if (age_s.type == AGE_UNRECOGNIZED)
170     strncpy(age_s.phrase, line_value, MAX_PHRASE_LEN + 1);
171   return age_s;
172 }
173
174 /** This function converts the given struct age_value into its string
175     representation.
176
177     \param val  The given parsed age
178
179     \return The string representation of the parsed age; note that this value
180     is statically allocated, and is thus overwritten on each call
181 */
182 char* gedcom_age_to_string(const struct age_value* val)
183 {
184   int num = 0;
185   reset_buffer(&age_buffer);
186
187   switch (val->mod) {
188     case AGE_LESS_THAN:
189       safe_buf_append(&age_buffer, "<"); break;
190     case AGE_GREATER_THAN:
191       safe_buf_append(&age_buffer, ">"); break;
192     default:
193       break;
194   }
195
196   switch (val->type) {
197     case AGE_UNRECOGNIZED:
198       reset_buffer(&age_buffer);
199       safe_buf_append(&age_buffer, val->phrase); break;
200     case AGE_CHILD:
201       safe_buf_append(&age_buffer, "CHILD"); break;
202     case AGE_INFANT:
203       safe_buf_append(&age_buffer, "INFANT"); break;
204     case AGE_STILLBORN:
205       safe_buf_append(&age_buffer, "STILLBORN"); break;
206     case AGE_NUMERIC:
207       if (val->years != -1) {
208         num = 1;
209         safe_buf_append(&age_buffer, "%dy", val->years);
210       }
211       if (val->months != -1) {
212         if (num)
213           safe_buf_append(&age_buffer, " ");
214         num = 1;
215         safe_buf_append(&age_buffer, "%dm", val->months);
216       }
217       if (val->days != -1) {
218         if (num)
219           safe_buf_append(&age_buffer, " ");
220         num = 1;
221         safe_buf_append(&age_buffer, "%dd", val->days);
222       }
223       break;
224     default:
225       break;
226   }
227   
228   return get_buf_string(&age_buffer);
229 }