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