X-Git-Url: https://git.dlugolecki.net.pl/?a=blobdiff_plain;f=gedcom%2Fxref.c;h=dfd79bf8b5272fbe697e2cab0973556d234121b6;hb=6103dd898c4de86c68891cc0222543988a2caab7;hp=ee58ad02eb7534891760e22acc341bef250a521d;hpb=7b470d2685d5a2479cf710b41d19d62e062b0dd6;p=gedcom-parse.git diff --git a/gedcom/xref.c b/gedcom/xref.c index ee58ad0..dfd79bf 100644 --- a/gedcom/xref.c +++ b/gedcom/xref.c @@ -22,23 +22,25 @@ /* $Name$ */ #include "gedcom_internal.h" +#include "gedcom.h" +#include "gedcom.tabgen.h" #include "xref.h" #include "hash.h" struct xref_value def_xref_val = { XREF_NONE, "", NULL }; static hash_t *xrefs = NULL; -char* xref_type_str[] = { N_("nothing"), - N_("a family"), - N_("an individual"), - N_("a note"), - N_("a multimedia object"), - N_("a source repository"), - N_("a source"), - N_("a submitter"), - N_("a submission record"), - N_("an application-specific record"), - }; +const char* xref_type_str[] = { N_("nothing"), + N_("a family"), + N_("an individual"), + N_("a note"), + N_("a multimedia object"), + N_("a source repository"), + N_("a source"), + N_("a submitter"), + N_("a submission record"), + N_("an application-specific record"), + }; struct xref_node { struct xref_value xref; @@ -46,14 +48,15 @@ struct xref_node { Xref_type used_type; int defined_line; int used_line; + int use_count; }; -hnode_t *xref_alloc(void *c __attribute__((unused))) +hnode_t *xref_alloc(void *c UNUSED) { return malloc(sizeof *xref_alloc(NULL)); } -void xref_free(hnode_t *n, void *c __attribute__((unused))) +void xref_free(hnode_t *n, void *c UNUSED) { struct xref_node *xr = (struct xref_node *)hnode_get(n); free((void*)hnode_getkey(n)); @@ -68,21 +71,34 @@ void clear_xref_node(struct xref_node *xr) /* Make sure that the 'string' member always contains a valid string */ if (!xr->xref.string) xr->xref.string = strdup(""); + if (!xr->xref.string) MEMORY_ERROR; xr->xref.object = NULL; xr->defined_type = XREF_NONE; xr->used_type = XREF_NONE; xr->defined_line = -1; xr->used_line = -1; + xr->use_count = 0; } struct xref_node *make_xref_node() { struct xref_node *xr = (struct xref_node *)malloc(sizeof(struct xref_node)); - xr->xref.string = NULL; - clear_xref_node(xr); + if (xr) { + xr->xref.string = NULL; + clear_xref_node(xr); + } + else + MEMORY_ERROR; return xr; } +void delete_xref_node(struct xref_node* xr) +{ + if (!xr->xref.string) + free(xr->xref.string); + free(xr); +} + void cleanup_xrefs() { hash_free(xrefs); @@ -96,7 +112,9 @@ void make_xref_table() else /* Only register initially (if xrefs is still NULL) */ /* So that it is only registered once */ - atexit(cleanup_xrefs); + if (atexit(cleanup_xrefs) != 0) { + gedcom_warning(_("Could not register xref cleanup function")); + } xrefs = hash_create(HASHCOUNT_T_MAX, NULL, NULL); hash_set_allocator(xrefs, xref_alloc, xref_free, NULL); } @@ -126,18 +144,86 @@ int check_xref_table() return result; } -struct xref_value *gedcom_get_by_xref(char *key) +struct xref_node* add_xref(Xref_type xref_type, const char* xrefstr, + Gedcom_ctxt object) { - hnode_t *node = hash_lookup(xrefs, key); - if (node) { - struct xref_node *xr = (struct xref_node *)hnode_get(node); - return &(xr->xref); + struct xref_node *xr = NULL; + char *key = strdup(xrefstr); + if (key) { + xr = make_xref_node(); + xr->xref.type = xref_type; + xr->xref.object = object; + if (xr->xref.string) + free(xr->xref.string); + xr->xref.string = strdup(xrefstr); + if (! xr->xref.string) { + MEMORY_ERROR; + free(key); + delete_xref_node(xr); + xr = NULL; + } + else { + hash_alloc_insert(xrefs, key, xr); + } } else - return NULL; + MEMORY_ERROR; + return xr; +} + +void remove_xref(struct xref_node* xr) +{ + hnode_t *node = hash_lookup(xrefs, xr->xref.string); + hash_delete_free(xrefs, node); } -struct xref_value *gedcom_parse_xref(char *raw_value, +int set_xref_fields(struct xref_node* xr, Xref_ctxt ctxt, Xref_type xref_type) +{ + int result = 0; + + if (xr->defined_type != XREF_NONE && xr->defined_type != xref_type && + xr->defined_type != XREF_ANY) { + if (xr->defined_line != 0) + gedcom_error(_("Cross-reference %s previously defined as pointer to %s, " + "on line %d"), + xr->xref.string, xref_type_str[xr->defined_type], + xr->defined_line); + else + gedcom_error(_("Cross-reference %s previously defined as pointer to %s"), + xr->xref.string, xref_type_str[xr->defined_type]); + + result = 1; + } + else if (xr->used_type != XREF_NONE && xr->used_type != xref_type) { + if (xr->used_line != 0) + gedcom_error(_("Cross-reference %s previously used as pointer to %s, " + "on line %d"), + xr->xref.string, xref_type_str[xr->used_type], + xr->used_line); + else + gedcom_error(_("Cross-reference %s previously used as pointer to %s"), + xr->xref.string, xref_type_str[xr->used_type]); + + result = 1; + } + + if (result == 0) { + if (ctxt == XREF_USED) + xr->use_count++; + if (ctxt == XREF_DEFINED && xr->defined_type == XREF_NONE) { + xr->defined_type = xref_type; + xr->defined_line = line_no; + } + else if (ctxt == XREF_USED && xr->used_type == XREF_NONE) { + xr->used_type = xref_type; + xr->used_line = line_no; + } + } + + return result; +} + +struct xref_value *gedcom_parse_xref(const char *raw_value, Xref_ctxt ctxt, Xref_type xref_type) { struct xref_node *xr = NULL; @@ -147,42 +233,144 @@ struct xref_value *gedcom_parse_xref(char *raw_value, xr = (struct xref_node *)hnode_get(node); } else { - char *key = strdup(raw_value); - xr = make_xref_node(); - xr->xref.type = xref_type; - free(xr->xref.string); - xr->xref.string = strdup(raw_value); - hash_alloc_insert(xrefs, key, xr); + xr = add_xref(xref_type, raw_value, NULL); } - - if (ctxt == XREF_DEFINED && xr->defined_type == XREF_NONE) { - xr->defined_type = xref_type; - xr->defined_line = line_no; + + if (xr) { + set_xref_fields(xr, ctxt, xref_type); + return &(xr->xref); } - else if (ctxt == XREF_USED && xr->used_type == XREF_NONE) { - xr->used_type = xref_type; - xr->used_line = line_no; + else + return NULL; +} + +/* Functions for retrieving, modifying and deleting cross-references */ + +int is_valid_pointer(const char *key) +{ + return (strlen(key) <= 22 && + gedcom_check_token(key, STATE_NORMAL, POINTER) == 0); +} + +struct xref_value* gedcom_get_by_xref(const char *key) +{ + if (!is_valid_pointer(key)) { + gedcom_error(_("String '%s' is not a valid cross-reference key"), key); + return NULL; } - - if ((ctxt == XREF_DEFINED && xr->defined_type != xref_type && - xr->defined_type != XREF_ANY) - || (ctxt == XREF_USED && - (xr->defined_type != XREF_NONE && xr->defined_type != xref_type && - xr->defined_type != XREF_ANY))) { - gedcom_error(_("Cross-reference %s previously defined as pointer to %s, " - "on line %d"), - xr->xref.string, xref_type_str[xr->defined_type], - xr->defined_line); - clear_xref_node(xr); - } - else if ((ctxt == XREF_USED && xr->used_type != xref_type) - || (ctxt == XREF_DEFINED && - (xr->used_type != XREF_NONE && xr->used_type != xref_type))) { - gedcom_error(_("Cross-reference %s previously used as pointer to %s, " - "on line %d"), - xr->xref.string, xref_type_str[xr->used_type], xr->used_line); - clear_xref_node(xr); + else { + hnode_t *node = hash_lookup(xrefs, key); + if (node) { + struct xref_node *xr = (struct xref_node *)hnode_get(node); + return &(xr->xref); + } + else + return NULL; } - - return &(xr->xref); +} + +struct xref_value* gedcom_add_xref(Xref_type type, const char* xrefstr, + Gedcom_ctxt object) +{ + struct xref_node *xr = NULL; + + if (!is_valid_pointer(xrefstr)) { + gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr); + } + else { + hnode_t *node = hash_lookup(xrefs, xrefstr); + if (node) { + gedcom_error(_("Cross-reference %s already exists"), xrefstr); + } + else { + xr = add_xref(type, xrefstr, object); + if (xr) + set_xref_fields(xr, XREF_DEFINED, type); + } + } + if (xr) + return &(xr->xref); + else + return NULL; +} + +struct xref_value* gedcom_link_xref(Xref_type type, const char* xrefstr) +{ + struct xref_node *xr = NULL; + + if (!is_valid_pointer(xrefstr)) { + gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr); + } + else { + hnode_t *node = hash_lookup(xrefs, xrefstr); + if (!node) { + gedcom_error(_("Cross-reference %s not defined"), xrefstr); + } + else { + xr = (struct xref_node *)hnode_get(node); + if (set_xref_fields(xr, XREF_USED, type) != 0) + xr = NULL; + } + } + + if (xr) + return &(xr->xref); + else + return NULL; +} + +struct xref_value* gedcom_unlink_xref(Xref_type type, const char* xrefstr) +{ + struct xref_node *xr = NULL; + if (!is_valid_pointer(xrefstr)) { + gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr); + } + else { + hnode_t *node = hash_lookup(xrefs, xrefstr); + if (! node) { + gedcom_error(_("Cross-reference %s not defined"), xrefstr); + } + else { + xr = (struct xref_node*) hnode_get(node); + if (xr->defined_type != type && xr->defined_type != XREF_ANY) { + gedcom_error + (_("Cross-reference %s previously defined as pointer to %s"), + xr->xref.string, xref_type_str[xr->defined_type]); + xr = NULL; + } + else + xr->use_count--; + } + } + if (xr) + return &(xr->xref); + else + return NULL; +} + +int gedcom_delete_xref(const char* xrefstr) +{ + struct xref_node *xr = NULL; + int result = 1; + + if (!is_valid_pointer(xrefstr)) { + gedcom_error(_("String '%s' is not a valid cross-reference key"), xrefstr); + } + else { + hnode_t *node = hash_lookup(xrefs, xrefstr); + if (! node) { + gedcom_error(_("Cross-reference %s not defined"), xrefstr); + } + else { + xr = (struct xref_node*) hnode_get(node); + if (xr->use_count != 0) { + gedcom_error(_("Cross-reference %s still in use"), xrefstr); + } + else { + remove_xref(xr); + result = 0; + } + } + } + return result; }