renamed the package to libgedcom-dev
[gedcom-parse.git] / gedcom / xref.c
1 /* Cross-reference 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 "gedcom.h"
26 #include "gedcom.tabgen.h"
27 #include "xref.h"
28 #include "hash.h"
29
30 struct xref_value def_xref_val = { XREF_NONE, "<error>", NULL };
31 static hash_t *xrefs = NULL;
32
33 const char* xref_type_str[] = { N_("nothing"),
34                                 N_("a family"),
35                                 N_("an individual"),
36                                 N_("a note"),
37                                 N_("a multimedia object"),
38                                 N_("a source repository"),
39                                 N_("a source"),
40                                 N_("a submitter"),
41                                 N_("a submission record"),
42                                 N_("an application-specific record"),
43                               };
44
45 struct xref_node {
46   struct xref_value xref;
47   Xref_type defined_type;
48   Xref_type used_type;
49   int defined_line;
50   int used_line;
51   int use_count;
52 };
53
54 hnode_t *xref_alloc(void *c UNUSED)
55 {
56   return malloc(sizeof *xref_alloc(NULL));
57 }
58
59 void xref_free(hnode_t *n, void *c UNUSED)
60 {
61   struct xref_node *xr = (struct xref_node *)hnode_get(n);
62   free((void*)hnode_getkey(n));
63   free(xr->xref.string);
64   free(xr);
65   free(n);
66 }
67
68 void clear_xref_node(struct xref_node *xr)
69 {
70   xr->xref.type    = XREF_NONE;
71   /* Make sure that the 'string' member always contains a valid string */
72   if (!xr->xref.string)
73     xr->xref.string  = strdup("");
74   if (!xr->xref.string) MEMORY_ERROR;
75   xr->xref.object  = NULL;
76   xr->defined_type = XREF_NONE;
77   xr->used_type    = XREF_NONE;
78   xr->defined_line = -1;
79   xr->used_line    = -1;
80   xr->use_count    = 0;
81 }
82
83 struct xref_node *make_xref_node()
84 {
85   struct xref_node *xr = (struct xref_node *)malloc(sizeof(struct xref_node));
86   if (xr) {
87     xr->xref.string = NULL;
88     clear_xref_node(xr);
89   }
90   else
91     MEMORY_ERROR;
92   return xr;
93 }
94
95 void delete_xref_node(struct xref_node* xr)
96 {
97   if (!xr->xref.string)
98     free(xr->xref.string);
99   free(xr);
100 }
101
102 void cleanup_xrefs()
103 {
104   hash_free(xrefs);
105   xrefs = NULL;
106 }
107
108 void make_xref_table()
109 {
110   if (xrefs)
111     cleanup_xrefs();
112   else
113     /* Only register initially (if xrefs is still NULL) */
114     /* So that it is only registered once */
115     if (atexit(cleanup_xrefs) != 0) {
116       gedcom_warning(_("Could not register xref cleanup function"));
117     }
118   xrefs = hash_create(HASHCOUNT_T_MAX, NULL, NULL);
119   hash_set_allocator(xrefs, xref_alloc, xref_free, NULL);
120 }
121
122 int check_xref_table()
123 {
124   int result = 0;
125   hscan_t hs;
126   hnode_t *node;
127   struct xref_node *xr;
128
129   /* Check for undefined and unused xrefs */
130   hash_scan_begin(&hs, xrefs);
131   while ((node = hash_scan_next(&hs))) {
132     xr = (struct xref_node *)hnode_get(node);
133     if (xr->defined_type == XREF_NONE && xr->used_type != XREF_NONE) {
134       gedcom_error(_("Cross-reference %s used on line %d is not defined"),
135                    xr->xref.string, xr->used_line);
136       result |= 1;
137     }
138     if (xr->used_type == XREF_NONE && xr->defined_type != XREF_NONE) {
139       gedcom_warning(_("Cross-reference %s defined on line %d is never used"),
140                      xr->xref.string, xr->defined_line);
141     }
142   }
143   
144   return result;
145 }
146
147 struct xref_node* add_xref(Xref_type xref_type, const char* xrefstr,
148                            Gedcom_ctxt object)
149 {
150   struct xref_node *xr = NULL;
151   char *key = strdup(xrefstr);
152   if (key) {
153     xr = make_xref_node();
154     xr->xref.type = xref_type;
155     xr->xref.object = object;
156     if (xr->xref.string)
157       free(xr->xref.string);
158     xr->xref.string = strdup(xrefstr);
159     if (! xr->xref.string) {
160       MEMORY_ERROR;
161       free(key);
162       delete_xref_node(xr);
163       xr = NULL;
164     }
165     else {
166       hash_alloc_insert(xrefs, key, xr);
167     }
168   }
169   else
170     MEMORY_ERROR;
171   return xr;
172 }
173
174 void remove_xref(struct xref_node* xr)
175 {
176   hnode_t *node = hash_lookup(xrefs, xr->xref.string);
177   hash_delete_free(xrefs, node);
178 }
179
180 int set_xref_fields(struct xref_node* xr, Xref_ctxt ctxt, Xref_type xref_type)
181 {
182   int result = 0;
183
184   if (xr->defined_type != XREF_NONE && xr->defined_type != xref_type &&
185       xr->defined_type != XREF_ANY) {
186     if (xr->defined_line != 0)
187       gedcom_error(_("Cross-reference %s previously defined as pointer to %s, "
188                      "on line %d"),
189                    xr->xref.string, xref_type_str[xr->defined_type],
190                    xr->defined_line);
191     else
192       gedcom_error(_("Cross-reference %s previously defined as pointer to %s"),
193                    xr->xref.string, xref_type_str[xr->defined_type]);
194
195     result = 1;
196   }  
197   else if (xr->used_type != XREF_NONE && xr->used_type != xref_type) {
198     if (xr->used_line != 0)
199       gedcom_error(_("Cross-reference %s previously used as pointer to %s, "
200                      "on line %d"),
201                    xr->xref.string, xref_type_str[xr->used_type],
202                    xr->used_line);
203     else
204       gedcom_error(_("Cross-reference %s previously used as pointer to %s"),
205                    xr->xref.string, xref_type_str[xr->used_type]);
206
207     result = 1;
208   }
209
210   if (result == 0) {
211     if (ctxt == XREF_USED)
212       xr->use_count++;
213     if (ctxt == XREF_DEFINED && xr->defined_type == XREF_NONE) {
214       xr->defined_type = xref_type;
215       xr->defined_line = line_no;
216     }
217     else if (ctxt == XREF_USED && xr->used_type == XREF_NONE) {
218       xr->used_type = xref_type;
219       xr->used_line = line_no;
220     }
221   }
222   
223   return result;
224 }
225
226 struct xref_value *gedcom_parse_xref(const char *raw_value,
227                                      Xref_ctxt ctxt, Xref_type xref_type)
228 {
229   struct xref_node *xr = NULL;
230   
231   hnode_t *node = hash_lookup(xrefs, raw_value);
232   if (node) {
233     xr = (struct xref_node *)hnode_get(node);
234   }
235   else {
236     xr = add_xref(xref_type, raw_value, NULL);
237   }
238
239   if (xr) {
240     set_xref_fields(xr, ctxt, xref_type);
241     return &(xr->xref);
242   }
243   else
244     return NULL;
245 }
246
247 /* Functions for retrieving, modifying and deleting cross-references */
248
249 int is_valid_pointer(const char *key)
250 {
251   return (strlen(key) <= 22 &&
252           gedcom_check_token(key, STATE_NORMAL, POINTER) == 0);
253 }
254
255 /** Retrieve an xref_value by its key.
256
257     \param key  The given cross-reference key
258
259     \return The object referenced by the key, or \c NULL if the given key
260     isn't a valid cross-reference key (see detailed description of
261     \ref parsed_xref) or isn't used.
262 */ 
263 struct xref_value* gedcom_get_by_xref(const char *key)
264 {
265   if (!is_valid_pointer(key)) {
266     gedcom_error(_("String '%s' is not a valid cross-reference key"), key);
267     return NULL;
268   }
269   else {
270     hnode_t *node = hash_lookup(xrefs, key);
271     if (node) {
272       struct xref_node *xr = (struct xref_node *)hnode_get(node);
273       return &(xr->xref);
274     }
275     else
276       return NULL;
277   }
278 }
279
280 /** Add an xref_value of the given type, with the given key, to the given
281     object, with a use count equal to 0.
282
283     \param type  The type of the referenced object
284     \param xrefstr  The key for the object
285     \param object   The object to be referenced
286
287     \return The new xref_value if success, or \c NULL in one of the following
288     cases:
289      - the key isn't a valid cross-reference key (see detailed description of
290        \ref parsed_xref)
291      - there is already an xref_value with the same key
292      - there was a memory allocation error
293 */
294 struct xref_value* gedcom_add_xref(Xref_type type, const char* xrefstr,
295                                    Gedcom_ctxt object)
296 {
297   struct xref_node *xr = NULL;
298
299   if (!is_valid_pointer(xrefstr)) {
300     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
301   }
302   else {
303     hnode_t *node = hash_lookup(xrefs, xrefstr);
304     if (node) {
305       gedcom_error(_("Cross-reference %s already exists"), xrefstr);
306     }
307     else {
308       xr = add_xref(type, xrefstr, object);
309       if (xr)
310         set_xref_fields(xr, XREF_DEFINED, type);
311     }
312   }
313   if (xr)
314     return &(xr->xref);
315   else
316     return NULL;
317 }
318
319 /** Declare the xref_value corresponding to the given key as being used as the
320     given type.  The use of this function is not mandatory, but it can aid in
321     spotting places in the code where xref_value objects are deleted while
322     they are still referenced.
323
324     \param type  The type of the referenced object
325     \param xrefstr  The key for the object
326
327     \return The xref_value object if success, and its use count is incremented.
328     Returns NULL in one of the following cases:
329      - the key isn't a valid cross-reference key (see detailed description of
330        \ref parsed_xref)
331      - there is no xref_value with the given key
332      - the xref_value was previously added as another type than the type
333        provided here
334  */
335 struct xref_value* gedcom_link_xref(Xref_type type, const char* xrefstr)
336 {
337   struct xref_node *xr = NULL;
338
339   if (!is_valid_pointer(xrefstr)) {
340     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
341   }
342   else {
343     hnode_t *node = hash_lookup(xrefs, xrefstr);
344     if (!node) {
345       gedcom_error(_("Cross-reference %s not defined"), xrefstr);
346     }
347     else {
348       xr = (struct xref_node *)hnode_get(node);
349       if (set_xref_fields(xr, XREF_USED, type) != 0)
350         xr = NULL;
351     }
352   }
353
354   if (xr)
355     return &(xr->xref);
356   else
357     return NULL;
358 }
359
360 /** Declare the xref_value corresponding to the given key no longer used.
361     The use of this function is not mandatory, but it can aid in
362     spotting places in the code where xref_value objects are deleted while
363     they are still referenced.
364
365     \param type  The type of the referenced object
366     \param xrefstr  The key for the object
367
368     \return The xref_value object if success, and its use count is decremented.
369     Returns NULL in one of the following cases:
370      - the key isn't a valid cross-reference key (see detailed description of
371        \ref parsed_xref)
372      - there is no xref_value with the given key
373      - the xref_value was previously added as another type than the type
374        provided here
375  */
376 struct xref_value* gedcom_unlink_xref(Xref_type type, const char* xrefstr)
377 {
378   struct xref_node *xr = NULL;
379   if (!is_valid_pointer(xrefstr)) {
380     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
381   }
382   else {
383     hnode_t *node = hash_lookup(xrefs, xrefstr);
384     if (! node) {
385       gedcom_error(_("Cross-reference %s not defined"), xrefstr);
386     }
387     else {
388       xr = (struct xref_node*) hnode_get(node);
389       if (xr->defined_type != type && xr->defined_type != XREF_ANY) {
390         gedcom_error
391           (_("Cross-reference %s previously defined as pointer to %s"),
392            xr->xref.string, xref_type_str[xr->defined_type]);
393         xr = NULL;
394       }
395       else
396         xr->use_count--;
397     }
398   }
399   if (xr)
400     return &(xr->xref);
401   else
402     return NULL;
403 }
404
405 /** Delete the xref_value corresponding to the given key.
406
407     \param xrefstr  The key for the object
408
409     \return 0 if success; 1 in one of the following cases:
410       - the key isn't a valid cross-reference key (see detailed description of
411        \ref parsed_xref)
412       - there is no xref_value with the given key
413       - the xref_value is still in use, i.e. its use count is not 0 (see
414         gedcom_link_xref() and gedcom_unlink_xref())
415  */
416 int gedcom_delete_xref(const char* xrefstr)
417 {
418   struct xref_node *xr = NULL;
419   int result = 1;
420
421   if (!is_valid_pointer(xrefstr)) {
422     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
423   }
424   else {
425     hnode_t *node = hash_lookup(xrefs, xrefstr);
426     if (! node) {
427       gedcom_error(_("Cross-reference %s not defined"), xrefstr);
428     }
429     else {
430       xr = (struct xref_node*) hnode_get(node);
431       if (xr->use_count != 0)  {
432         gedcom_error(_("Cross-reference %s still in use"), xrefstr);
433       }
434       else {
435         remove_xref(xr);
436         result = 0;
437       }
438     }
439   }
440   return result;
441 }