Added functions for retrieving, adding, modifying and deleting cross-references.
[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 __attribute__((unused)))
55 {
56   return malloc(sizeof *xref_alloc(NULL));
57 }
58
59 void xref_free(hnode_t *n, void *c __attribute__((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   const 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) MEMORY_ERROR;
160     hash_alloc_insert(xrefs, key, xr);
161   }
162   else
163     MEMORY_ERROR;
164   return xr;
165 }
166
167 void remove_xref(struct xref_node* xr)
168 {
169   hnode_t *node = hash_lookup(xrefs, xr->xref.string);
170   hash_delete_free(xrefs, node);
171 }
172
173 int set_xref_fields(struct xref_node* xr, Xref_ctxt ctxt, Xref_type xref_type)
174 {
175   int result = 0;
176
177   if (xr->defined_type != XREF_NONE && xr->defined_type != xref_type &&
178       xr->defined_type != XREF_ANY) {
179     if (xr->defined_line != 0)
180       gedcom_error(_("Cross-reference %s previously defined as pointer to %s, "
181                      "on line %d"),
182                    xr->xref.string, xref_type_str[xr->defined_type],
183                    xr->defined_line);
184     else
185       gedcom_error(_("Cross-reference %s previously defined as pointer to %s"),
186                    xr->xref.string, xref_type_str[xr->defined_type]);
187
188     result = 1;
189   }  
190   else if (xr->used_type != XREF_NONE && xr->used_type != xref_type) {
191     if (xr->used_line != 0)
192       gedcom_error(_("Cross-reference %s previously used as pointer to %s, "
193                      "on line %d"),
194                    xr->xref.string, xref_type_str[xr->used_type],
195                    xr->used_line);
196     else
197       gedcom_error(_("Cross-reference %s previously used as pointer to %s"),
198                    xr->xref.string, xref_type_str[xr->used_type]);
199
200     result = 1;
201   }
202
203   if (result == 0) {
204     if (ctxt == XREF_USED)
205       xr->use_count++;
206     if (ctxt == XREF_DEFINED && xr->defined_type == XREF_NONE) {
207       xr->defined_type = xref_type;
208       xr->defined_line = line_no;
209     }
210     else if (ctxt == XREF_USED && xr->used_type == XREF_NONE) {
211       xr->used_type = xref_type;
212       xr->used_line = line_no;
213     }
214   }
215   
216   return result;
217 }
218
219 struct xref_value *gedcom_parse_xref(const char *raw_value,
220                                      Xref_ctxt ctxt, Xref_type xref_type)
221 {
222   struct xref_node *xr = NULL;
223   
224   hnode_t *node = hash_lookup(xrefs, raw_value);
225   if (node) {
226     xr = (struct xref_node *)hnode_get(node);
227   }
228   else {
229     xr = add_xref(xref_type, raw_value, NULL);
230   }
231
232   set_xref_fields(xr, ctxt, xref_type);
233   return &(xr->xref);
234 }
235
236 /* Functions for retrieving, modifying and deleting cross-references */
237
238 struct xref_value* gedcom_get_by_xref(const char *key)
239 {
240   if (gedcom_check_token(key, STATE_NORMAL, POINTER) != 0) {
241     gedcom_error(_("String '%s' is not a valid cross-reference key"), key);
242     return NULL;
243   }
244   else {
245     hnode_t *node = hash_lookup(xrefs, key);
246     if (node) {
247       struct xref_node *xr = (struct xref_node *)hnode_get(node);
248       return &(xr->xref);
249     }
250     else
251       return NULL;
252   }
253 }
254
255 struct xref_value* gedcom_add_xref(Xref_type type, const char* xrefstr,
256                                    Gedcom_ctxt object)
257 {
258   struct xref_node *xr = NULL;
259
260   if (gedcom_check_token(xrefstr, STATE_NORMAL, POINTER) != 0) {
261     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
262   }
263   else {
264     hnode_t *node = hash_lookup(xrefs, xrefstr);
265     if (node) {
266       gedcom_error(_("Cross-reference %s already exists"), xrefstr);
267     }
268     else {
269       xr = add_xref(type, xrefstr, object);    
270       set_xref_fields(xr, XREF_DEFINED, type);
271     }
272   }
273   if (xr)
274     return &(xr->xref);
275   else
276     return NULL;
277 }
278
279 struct xref_value* gedcom_link_xref(Xref_type type, const char* xrefstr)
280 {
281   struct xref_node *xr = NULL;
282
283   if (gedcom_check_token(xrefstr, STATE_NORMAL, POINTER) != 0) {
284     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
285   }
286   else {
287     hnode_t *node = hash_lookup(xrefs, xrefstr);
288     if (!node) {
289       gedcom_error(_("Cross-reference %s not defined"), xrefstr);
290     }
291     else {
292       xr = (struct xref_node *)hnode_get(node);
293       if (set_xref_fields(xr, XREF_USED, type) != 0)
294         xr = NULL;
295     }
296   }
297
298   if (xr)
299     return &(xr->xref);
300   else
301     return NULL;
302 }
303
304 struct xref_value* gedcom_unlink_xref(Xref_type type, const char* xrefstr)
305 {
306   struct xref_node *xr = NULL;
307   if (gedcom_check_token(xrefstr, STATE_NORMAL, POINTER) != 0) {
308     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
309   }
310   else {
311     hnode_t *node = hash_lookup(xrefs, xrefstr);
312     if (! node) {
313       gedcom_error(_("Cross-reference %s not defined"), xrefstr);
314     }
315     else {
316       xr = (struct xref_node*) hnode_get(node);
317       if (xr->defined_type != type && xr->defined_type != XREF_ANY) {
318         gedcom_error
319           (_("Cross-reference %s previously defined as pointer to %s"),
320            xr->xref.string, xref_type_str[xr->defined_type]);
321         xr = NULL;
322       }
323       else
324         xr->use_count--;
325     }
326   }
327   if (xr)
328     return &(xr->xref);
329   else
330     return NULL;
331 }
332
333 int gedcom_delete_xref(const char* xrefstr)
334 {
335   struct xref_node *xr = NULL;
336   int result = 1;
337
338   if (gedcom_check_token(xrefstr, STATE_NORMAL, POINTER) != 0) {
339     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
340   }
341   else {
342     hnode_t *node = hash_lookup(xrefs, xrefstr);
343     if (! node) {
344       gedcom_error(_("Cross-reference %s not defined"), xrefstr);
345     }
346     else {
347       xr = (struct xref_node*) hnode_get(node);
348       if (xr->use_count != 0)  {
349         gedcom_error(_("Cross-reference %s still in use"), xrefstr);
350       }
351       else {
352         remove_xref(xr);
353         result = 0;
354       }
355     }
356   }
357   return result;
358 }