Copied from old documentation. Removed all Gedcom_val details.
[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 struct xref_value* gedcom_get_by_xref(const char *key)
256 {
257   if (!is_valid_pointer(key)) {
258     gedcom_error(_("String '%s' is not a valid cross-reference key"), key);
259     return NULL;
260   }
261   else {
262     hnode_t *node = hash_lookup(xrefs, key);
263     if (node) {
264       struct xref_node *xr = (struct xref_node *)hnode_get(node);
265       return &(xr->xref);
266     }
267     else
268       return NULL;
269   }
270 }
271
272 struct xref_value* gedcom_add_xref(Xref_type type, const char* xrefstr,
273                                    Gedcom_ctxt object)
274 {
275   struct xref_node *xr = NULL;
276
277   if (!is_valid_pointer(xrefstr)) {
278     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
279   }
280   else {
281     hnode_t *node = hash_lookup(xrefs, xrefstr);
282     if (node) {
283       gedcom_error(_("Cross-reference %s already exists"), xrefstr);
284     }
285     else {
286       xr = add_xref(type, xrefstr, object);
287       if (xr)
288         set_xref_fields(xr, XREF_DEFINED, type);
289     }
290   }
291   if (xr)
292     return &(xr->xref);
293   else
294     return NULL;
295 }
296
297 struct xref_value* gedcom_link_xref(Xref_type type, const char* xrefstr)
298 {
299   struct xref_node *xr = NULL;
300
301   if (!is_valid_pointer(xrefstr)) {
302     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
303   }
304   else {
305     hnode_t *node = hash_lookup(xrefs, xrefstr);
306     if (!node) {
307       gedcom_error(_("Cross-reference %s not defined"), xrefstr);
308     }
309     else {
310       xr = (struct xref_node *)hnode_get(node);
311       if (set_xref_fields(xr, XREF_USED, type) != 0)
312         xr = NULL;
313     }
314   }
315
316   if (xr)
317     return &(xr->xref);
318   else
319     return NULL;
320 }
321
322 struct xref_value* gedcom_unlink_xref(Xref_type type, const char* xrefstr)
323 {
324   struct xref_node *xr = NULL;
325   if (!is_valid_pointer(xrefstr)) {
326     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
327   }
328   else {
329     hnode_t *node = hash_lookup(xrefs, xrefstr);
330     if (! node) {
331       gedcom_error(_("Cross-reference %s not defined"), xrefstr);
332     }
333     else {
334       xr = (struct xref_node*) hnode_get(node);
335       if (xr->defined_type != type && xr->defined_type != XREF_ANY) {
336         gedcom_error
337           (_("Cross-reference %s previously defined as pointer to %s"),
338            xr->xref.string, xref_type_str[xr->defined_type]);
339         xr = NULL;
340       }
341       else
342         xr->use_count--;
343     }
344   }
345   if (xr)
346     return &(xr->xref);
347   else
348     return NULL;
349 }
350
351 int gedcom_delete_xref(const char* xrefstr)
352 {
353   struct xref_node *xr = NULL;
354   int result = 1;
355
356   if (!is_valid_pointer(xrefstr)) {
357     gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr);
358   }
359   else {
360     hnode_t *node = hash_lookup(xrefs, xrefstr);
361     if (! node) {
362       gedcom_error(_("Cross-reference %s not defined"), xrefstr);
363     }
364     else {
365       xr = (struct xref_node*) hnode_get(node);
366       if (xr->use_count != 0)  {
367         gedcom_error(_("Cross-reference %s still in use"), xrefstr);
368       }
369       else {
370         remove_xref(xr);
371         result = 0;
372       }
373     }
374   }
375   return result;
376 }