Copied from old documentation. Removed all Gedcom_val details.
[gedcom-parse.git] / gedcom / xref.c
index ee58ad02eb7534891760e22acc341bef250a521d..dfd79bf8b5272fbe697e2cab0973556d234121b6 100644 (file)
 /* $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, "<error>", 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;
 }