Finish doxygen conversion of documentation.
[gedcom-parse.git] / gom / gom_modify.c
1 /* Source code for modifying the gedcom object model.
2    Copyright (C) 2002 The Genes Development Team
3    This file is part of the Gedcom parser library.
4    Contributed by Peter Verthez <Peter.Verthez@advalvas.be>, 2002.
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 <stdlib.h>
25 #include <string.h>
26 #include "utf8tools.h"
27 #include "user_rec.h"
28 #include "gom.h"
29 #include "gom_internal.h"
30
31 /** If some characters cannot be converted by gom_get_string_for_locale(),
32     then these characters are by default replaced by the string "?".  The
33     function gom_set_unknown() can configure the string to be used.
34
35     \param unknown The new replacement string for conversion failures
36 */
37 void gom_set_unknown(const char* unknown)
38 {
39   convert_set_unknown(unknown);
40 }
41
42 /** Returns the string from the Gedcom object model referenced by \c data
43     in UTF-8 format.
44
45     \param data The string from the Gedcom object model
46     \return The string in UTF-8 format (is in fact a pass-through operation)
47 */
48 char* gom_get_string(char* data)
49 {
50   return data;
51 }
52
53 /** Returns the string from the Gedcom object model referenced by \c data
54     in the encoding defined by the current locale.
55
56     Since the conversion from
57     UTF-8 to the locale encoding is not always possible, the function has a
58     second parameter that can return the number of conversion failures for the
59     result string.
60
61     \param data The string from the Gedcom object model
62     \param conversion_failures Pass a pointer to an integer if you want to know
63     the number of conversion failures (filled in on return).  You can pass
64     \c NULL if you're not interested.
65
66     \return The string in the encoding defined by the current locale, with
67     unconvertible characters replaced by the "unknown string" (see
68     gom_set_unknown())
69 */
70 char* gom_get_string_for_locale(char* data, int* conversion_failures)
71 {
72   return convert_utf8_to_locale(gom_get_string(data), conversion_failures);
73 }
74
75 /** Sets the string from the Gedcom object model referenced by \c data to the
76     given input \c utf8_str, which must be a string in UTF-8 encoding.
77
78     The function makes a copy of the input string
79     to store it in the object model.  It also takes care of deallocating the
80     old value of the data if needed.
81
82     Note that this function needs the \em address of the data variable, to
83     be able to modify it.
84
85     \param data The string from the Gedcom object model
86     \param utf8_str A new string, in UTF-8 encoding
87
88     \return The new value if successful, or \c NULL if an error occurred, e.g.:
89       - failure to allocate memory
90       - the given string is not a valid UTF-8 string
91       .
92     In the case of an error, the target data variable is not modified.
93 */
94 char* gom_set_string(char** data, const char* utf8_str)
95 {
96   char* result = NULL;
97   char* newptr;
98
99   if (utf8_str == NULL) {
100     SAFE_FREE(*data);
101   }
102   else {
103     if (!is_utf8_string(utf8_str)) {
104       gedcom_error(_("The input '%s' is not a valid UTF-8 string"), utf8_str);
105     }
106     else {
107       newptr = strdup(utf8_str);
108       if (!newptr)
109         MEMORY_ERROR;
110       else {
111         SAFE_FREE(*data);
112         *data = newptr;
113         result = *data;
114       }
115     }
116   }
117   
118   return result;
119 }
120
121 /** Sets the string from the Gedcom object model referenced by \c data to the
122     given input \c locale_str, which must be a string in the encoding defined
123     by the current locale.
124
125     The function makes a copy of the input string
126     to store it in the object model.  It also takes care of deallocating the
127     old value of the data if needed.
128
129     Note that this function needs the \em address of the data variable, to
130     be able to modify it.
131
132     \param data The string from the Gedcom object model
133     \param locale_str A new string, in encoding defined by the current locale
134
135     \return The new value if successful, or \c NULL if an error occurred, e.g.:
136       - failure to allocate memory
137       - the given string is not a valid string for the current locale
138       .
139     In the case of an error, the target data variable is not modified.
140 */
141 char* gom_set_string_for_locale(char** data, const char* locale_str)
142 {
143   char* result = NULL;
144
145   if (locale_str == NULL) {
146     result = gom_set_string(data, NULL);
147   }
148   else {
149     char* utf8_str = convert_locale_to_utf8(locale_str);
150     
151     if (!utf8_str)
152       gedcom_error(_("The input '%s' is not a valid string for the locale"),
153                    locale_str);
154     else
155       result = gom_set_string(data, utf8_str);
156   }
157
158   return result;
159 }
160
161 void unref_xref_value(struct xref_value *xref)
162 {
163   if (xref)
164     gedcom_unlink_xref(xref->type, xref->string);
165 }
166
167 void UNREFALLFUNC(xref_list)(struct xref_list* obj)
168 {
169   if (obj) {
170     struct xref_list* runner;
171     for (runner = obj; runner; runner = runner->next) {
172       unref_xref_value(runner->xref);
173       UNREFALLFUNC(user_data)(runner->extra);
174     }
175   }
176 }
177
178 void CLEANFUNC(xref_list)(struct xref_list *obj)
179 {
180   if (obj) {
181     DESTROY_CHAIN_ELTS(user_data, obj->extra);
182   }
183 }
184
185 /** This function modifies a data variable in the Gedcom object model of
186     type struct xref_value to point to the given \c xref, taking care of
187     unreferencing the old value, and referencing the new value (resp.
188     decrementing and incrementing the reference count).
189
190     \param data The address of the xref_value member in the object model
191     \param xref The cross-reference key of an existing object, or \c NULL
192     \return The new value of the data variable, or \c NULL if an error
193     occurred, e.g. there was no record found with the given key
194     (in this case, the data variable is not changed).
195 */
196 struct xref_value* gom_set_xref(struct xref_value** data, const char* xref)
197 {
198   struct xref_value* result = NULL;
199   struct xref_value* newval = NULL;
200   
201   if (data) {
202     if (xref) {
203       newval = gedcom_get_by_xref(xref);
204       if (!newval)
205         gedcom_error(_("No record found for xref '%s'"), xref);
206     }
207     
208     /* Unreference the old value if not NULL */
209     if (*data)
210       result = gedcom_unlink_xref((*data)->type, (*data)->string);
211     else
212       result = newval;
213     
214     /* Reference the new value if not NULL */
215     if (result != NULL && newval) {
216       result = gedcom_link_xref(newval->type, newval->string);
217       /* On error, perform rollback to old value (guaranteed to work) */
218       if (result == NULL)
219         gedcom_link_xref((*data)->type, (*data)->string);
220     }
221     
222     if (result != NULL) {
223       *data = newval;
224       result = newval;
225     }
226   }
227   return result;
228 }
229
230 /** This function adds the given cross-reference to the end of the \c data
231     list, taking care of incrementing the reference count of the
232     cross-reference.
233
234     \param data The address of the xref_list member in the object model
235     \param xref The cross-reference key of an existing object
236
237     \return The new entry in the \c data list, or \c NULL if an error occurred.
238 */
239 struct xref_list* gom_add_xref(struct xref_list** data, const char* xref)
240 {
241   struct xref_value* result = NULL;
242   struct xref_value* newval = NULL;
243   struct xref_list* xrl = NULL;
244
245   if (data && xref) {
246     newval = gedcom_get_by_xref(xref);
247     if (!newval)
248       gedcom_error(_("No record found for xref '%s'"), xref);
249     else {
250       result = gedcom_link_xref(newval->type, newval->string);
251       if (result != NULL) {
252         MAKE_CHAIN_ELT(xref_list, *data, xrl);
253         if (xrl) xrl->xref = newval;
254       }
255     }
256   }
257
258   return xrl;
259 }
260
261 struct xref_list* find_xref(struct xref_list** data, const char* xref)
262 {
263   struct xref_list* result = NULL;
264   struct xref_value* xr = gedcom_get_by_xref(xref);
265   if (!xr)
266     gedcom_error(_("No record found for xref '%s'"), xref);
267   else {
268     struct xref_list* xrl;
269     for (xrl = *data ; xrl ; xrl = xrl->next) {
270       if (xrl->xref == xr) {
271         result = xrl;
272         break;
273       }
274     }
275     if (! result)
276       gedcom_error(_("Xref '%s' not part of chain"), xref);
277   }
278   return result;
279 }
280
281 /** This function removes the given cross-reference from the \c data
282     list, taking care of decrementing the reference count of the
283     cross-reference.
284
285     \param data The address of the xref_list member in the object model
286     \param xref The cross-reference key of an existing object
287
288     \retval 0 if successful
289     \retval 1 if error (e.g. because not present in the list)
290 */
291 int gom_remove_xref(struct xref_list** data, const char* xref)
292 {
293   int result = 1;
294
295   if (data && xref) {
296     struct xref_list* xrl = find_xref(data, xref);
297     if (xrl) {
298       UNLINK_CHAIN_ELT(xref_list, *data, xrl);
299       gedcom_unlink_xref(xrl->xref->type, xrl->xref->string);
300       CLEANFUNC(xref_list)(xrl);
301       SAFE_FREE(xrl);
302       result = 0;
303     }
304   }
305
306   return result;
307 }
308
309 /** This function moves the given cross-reference up or down the \c data
310     list.
311
312     If the cross-reference cannot be moved up (because the first in the list)
313     or down (because the last in the list), a warning is generated, but the
314     function still returns success.
315
316     \param dir The direction to move into
317     \param data The address of the xref_list member in the object model
318     \param xref The cross-reference key of an existing object
319
320     \retval 0 if successful
321     \retval 1 if error (e.g. because not present in the list)
322 */
323 int gom_move_xref(Gom_direction dir, struct xref_list** data, const char* xref)
324 {
325   int result = 1;
326
327   if (data && xref) {
328     struct xref_list* xrl = find_xref(data, xref);
329     if (xrl) {
330       MOVE_CHAIN_ELT(xref_list, dir, *data, xrl);
331       result = 0;
332     }
333   }
334
335   return result;
336 }