Beginnings of write support.
authorPeter Verthez <Peter.Verthez@advalvas.be>
Wed, 4 Dec 2002 18:45:12 +0000 (18:45 +0000)
committerPeter Verthez <Peter.Verthez@advalvas.be>
Wed, 4 Dec 2002 18:45:12 +0000 (18:45 +0000)
gedcom/Makefile.am
gedcom/encoding.c
gedcom/encoding.h
gedcom/tag_data.h [new file with mode: 0644]
gedcom/write.c [new file with mode: 0644]

index 661c7f6bbff731015774ac803455d18b8a154377..f219f9ab1d9aeef7dc05a2218d10f51fbf19df44 100644 (file)
@@ -28,8 +28,9 @@ libgedcom_la_SOURCES = lex.gedcom_1byte_.c \
                       xref.c \
                       age.c \
                       compat.c \
-                      buffer.c
-libgedcom_la_LDFLAGS = -export-dynamic -version-info $(LIBVERSION) $(LIBICONV)
+                      buffer.c \
+                      write.c
+libgedcom_la_LDFLAGS = -export-dynamic -version-info $(LIBVERSION)
 libgedcom_la_LIBADD  = calendar/libcalendar.la ../utf8/libutf8.la @INTLLIBS@
 BUILT_SOURCES = lex.gedcom_1byte_.c \
                lex.gedcom_hilo_.c \
@@ -50,7 +51,8 @@ noinst_HEADERS = encoding.h \
                 xref.h \
                 age.h \
                 compat.h \
-                buffer.h
+                buffer.h \
+                tag_data.h
 EXTRA_DIST = gedcom.y \
             gedcom_date.y \
             gedcom_1byte.lex \
index 469854b0d8528aa78203db911c594f70cb8a547c..e0140b2f1dff7d32e24743748f10b42004d74f21 100644 (file)
@@ -22,7 +22,6 @@
 /* $Name$ */
 
 #include <string.h>
-#include <iconv.h>
 #include <stdio.h>
 #include <limits.h>
 #include <stdlib.h>
 #define GCONV_SEARCH_PATH "GCONV_PATH"
 #define MAXBUF 255
 
-/*
-static iconv_t cd_to_internal = (iconv_t) -1;
-*/
-static ENCODING the_enc = ONE_BYTE;
+static Encoding the_enc = ONE_BYTE;
 static hash_t *encodings = NULL;
 
 const char* charwidth_string[] = { "1", "2_HILO", "2_LOHI" };
@@ -83,10 +79,12 @@ void add_encoding(const char *gedcom_n, const char* charwidth,
     MEMORY_ERROR;
 }
 
-char* get_encoding(const char* gedcom_n, ENCODING enc)
+char* get_encoding(const char* gedcom_n, Encoding enc)
 {
   char *key;
   hnode_t *node;
+
+  if (encodings == NULL) return NULL;
   
   key = (char*)malloc(strlen(gedcom_n) + strlen(charwidth_string[enc]) + 3);
 
@@ -222,6 +220,7 @@ void init_encodings()
        if (buffer[strlen(buffer) - 1] != '\n') {
          gedcom_error(_("Line too long in encoding configuration file '%s'"),
                       ENCODING_CONF_FILE);
+         line_no = 0;
          return;
        }
        else if ((buffer[0] != '#') && (strcmp(buffer, "\n") != 0)) {
@@ -231,10 +230,12 @@ void init_encodings()
          else {
            gedcom_error(_("Missing data in encoding configuration file '%s'"),
                         ENCODING_CONF_FILE);
+           line_no = 0;
            return;
          }
        }
       }
+      line_no = 0;
       if (fclose(in) != 0) {
        gedcom_warning(_("Error closing file '%s': %s"),
                       ENCODING_CONF_FILE, strerror(errno));
@@ -243,7 +244,7 @@ void init_encodings()
   }
 }
 
-void set_encoding_width(ENCODING enc)
+void set_encoding_width(Encoding enc)
 {
   the_enc = enc;
 }
index e80616f5923333dc90cbe92bf127d43bcc05e7e7..a44ed0b69adfcc6771a2027b17109f33be81f831 100644 (file)
 #ifndef __ENCODING_H
 #define __ENCODING_H
 
+#include "gedcom.h"
 #include "utf8.h"
 
-typedef enum _ENC {
-  ONE_BYTE = 0,
-  TWO_BYTE_HILO = 1,
-  TWO_BYTE_LOHI = 2
-} ENCODING;
-
 int open_conv_to_internal(const char* fromcode);
 void close_conv_to_internal();
 char* to_internal(const char* str, size_t len, struct conv_buffer *output_buf);
 void init_encodings();
-void set_encoding_width(ENCODING enc);
+char* get_encoding(const char* gedcom_n, Encoding enc);
+void set_encoding_width(Encoding enc);
 void update_gconv_search_path();
 
 #endif /* __ENCODING_H */
diff --git a/gedcom/tag_data.h b/gedcom/tag_data.h
new file mode 100644 (file)
index 0000000..052a226
--- /dev/null
@@ -0,0 +1,138 @@
+/* Tag data header
+   Copyright (C) 2001,2002 The Genes Development Team
+   This file is part of the Gedcom parser library.
+   Contributed by Peter Verthez <Peter.Verthez@advalvas.be>, 2001.
+
+   The Gedcom parser library is free software; you can redistribute it
+   and/or modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The Gedcom parser library is distributed in the hope that it will be
+   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the Gedcom parser library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* $Id$ */
+/* $Name$ */
+
+#include "gedcom.h"
+
+typedef enum _OPT {
+  OPT_CONC = 0x01,
+  OPT_CONT = 0x02
+} Opt;
+
+struct tag_data {
+  char *elt_name;
+  char *tag_name;
+  int   allowed_types;
+  Opt   options;
+};
+
+struct tag_data tag_data[NR_OF_ELTS] =
+{
+  /* REC_HEAD */
+  { "REC_HEAD", "HEAD", GV_NULL, 0 },
+
+  /* REC_FAM */
+  { "REC_FAM", "FAM", GV_NULL, 0 },
+
+  /* REC_INDI */
+  { "REC_INDI", "INDI", GV_NULL, 0 },
+
+  /* REC_OBJE */
+  { "REC_OBJE", "OBJE", GV_NULL, 0 },
+
+  /* REC_NOTE */
+  { "REC_NOTE", "NOTE", GV_CHAR_PTR, 0 },
+
+  /* REC_REPO */
+  { "REC_REPO", "REPO", GV_NULL, 0 },
+
+  /* REC_SOUR */
+  { "REC_SOUR", "SOUR", GV_NULL, 0 },
+
+  /* REC_SUBN */
+  { "REC_SUBN", "SUBN", GV_NULL, 0 },
+
+  /* REC_SUBM */
+  { "REC_SUBM", "SUBM", GV_NULL, 0 },
+
+  /* REC_USER */
+  { "REC_USER", NULL,   GV_NULL | GV_CHAR_PTR | GV_XREF_PTR, 0 },
+
+  /* ELT_HEAD_SOUR */
+  { "ELT_HEAD_SOUR", "SOUR", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_SOUR_VERS */
+  { "ELT_HEAD_SOUR_VERS", "VERS", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_SOUR_NAME */
+  { "ELT_HEAD_SOUR_NAME", "NAME", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_SOUR_CORP */
+  { "ELT_HEAD_SOUR_CORP", "CORP", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_SOUR_DATA */
+  { "ELT_HEAD_SOUR_DATA", "DATA", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_SOUR_DATA_DATE */
+  { "ELT_HEAD_SOUR_DATA_DATE", "DATE", GV_DATE_VALUE, 0 },
+
+  /* ELT_HEAD_SOUR_DATA_COPR */
+  { "ELT_HEAD_SOUR_DATA_COPR", "COPR", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_DEST */
+  { "ELT_HEAD_DEST", "DEST", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_DATE */
+  { "ELT_HEAD_DATE", "DATE", GV_DATE_VALUE, 0 },
+
+  /* ELT_HEAD_DATE_TIME */
+  { "ELT_HEAD_DATE_TIME", "TIME", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_SUBM */
+  { "ELT_HEAD_SUBM", "SUBM", GV_XREF_PTR, 0 },
+
+  /* ELT_HEAD_SUBN */
+  { "ELT_HEAD_SUBN", "SUBN", GV_XREF_PTR, 0 },
+
+  /* ELT_HEAD_FILE */
+  { "ELT_HEAD_FILE", "FILE", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_COPR */
+  { "ELT_HEAD_COPR", "COPR", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_GEDC */
+  { "ELT_HEAD_GEDC", "GEDC", GV_NULL, 0 },
+
+  /* ELT_HEAD_GEDC_VERS */
+  { "ELT_HEAD_GEDC_VERS", "VERS", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_GEDC_FORM */
+  { "ELT_HEAD_GEDC_FORM", "FORM", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_CHAR */
+  { "ELT_HEAD_CHAR", "CHAR", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_CHAR_VERS */
+  { "ELT_HEAD_CHAR_VERS", "VERS", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_LANG */
+  { "ELT_HEAD_LANG", "LANG", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_PLAC */
+  { "ELT_HEAD_PLAC", "PLAC", GV_NULL, 0 },
+
+  /* ELT_HEAD_PLAC_FORM */
+  { "ELT_HEAD_PLAC_FORM", "FORM", GV_CHAR_PTR, 0 },
+
+  /* ELT_HEAD_NOTE */
+  { "ELT_HEAD_NOTE", "NOTE", GV_CHAR_PTR, OPT_CONC | OPT_CONT }
+};
diff --git a/gedcom/write.c b/gedcom/write.c
new file mode 100644 (file)
index 0000000..e74a571
--- /dev/null
@@ -0,0 +1,280 @@
+/* Write functions for Gedcom.
+   Copyright (C) 2001,2002 The Genes Development Team
+   This file is part of the Gedcom parser library.
+   Contributed by Peter Verthez <Peter.Verthez@advalvas.be>, 2001.
+
+   The Gedcom parser library is free software; you can redistribute it
+   and/or modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The Gedcom parser library is distributed in the hope that it will be
+   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the Gedcom parser library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* $Id$ */
+/* $Name$ */
+
+#include "gedcom_internal.h"
+#include "gedcom.h"
+#include "encoding.h"
+#include "tag_data.h"
+#include "buffer.h"
+#include "utf8.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+const char* encoding = "ASCII";
+int write_encoding_details = ONE_BYTE;
+/* SYS_NEWLINE is defined in config.h */
+const char* write_terminator = SYS_NEWLINE;
+
+struct Gedcom_write_struct {
+  int       filedesc;
+  convert_t conv;
+  int       total_conv_fails;
+  const char* term;
+  int       ctxt_stack[MAXGEDCLEVEL+1];
+  int       ctxt_level;
+};
+
+const char* default_encoding[] = {
+  /* ONE_BYTE */      "ASCII",
+  /* TWO_BYTE_HILO */ "UCS-2BE",
+  /* TWO_BYTE_LOHI */ "UCS-2LE"
+};
+
+const char* terminator[] = {
+  /* END_CR */     "\x0D",
+  /* END_LF */     "\x0A",
+  /* END_CR_LF */  "\x0D\x0A",
+  /* END_LF_CR */  "\x0A\x0D"
+};
+
+void cleanup_write_buffer();
+
+struct safe_buffer write_buffer = { NULL, 0, cleanup_write_buffer };
+
+void cleanup_write_buffer()
+{
+  cleanup_buffer(&write_buffer);
+}
+
+int write_simple(Gedcom_write_hndl hndl,
+                int level, char* xref, char* tag, char* value)
+{
+  int res;
+  
+  if (hndl) {
+    char* converted;
+    int conv_fails;
+    size_t outlen;
+    
+    reset_buffer(&write_buffer);
+    res = safe_buf_append(&write_buffer, "%d", level);
+    if (xref)
+      res += safe_buf_append(&write_buffer, " %s", xref);
+    res += safe_buf_append(&write_buffer, " %s", tag);
+    if (value)
+      res += safe_buf_append(&write_buffer, " %s", value);
+    res += safe_buf_append(&write_buffer, hndl->term);
+
+    converted = convert_from_utf8(hndl->conv, get_buf_string(&write_buffer),
+                                 &conv_fails, &outlen);
+    
+    if (converted && (conv_fails == 0))
+      write(hndl->filedesc, converted, outlen);
+    else {
+      hndl->total_conv_fails += conv_fails;
+      gedcom_error
+       (_("Error converting output string: %s (%d conversion failures)"),
+        strerror(errno), conv_fails);
+    }
+  }
+  return 0;
+}
+
+int gedcom_write_set_encoding(const char* charset,
+                             Encoding width, Enc_bom bom)
+{
+  char* new_encoding = NULL;
+  if (!strcmp(charset, "UNICODE")) {
+    if (width == ONE_BYTE) {
+      gedcom_error(_("Unicode cannot be encoded into one byte"));
+      return 1;
+    }
+    else {
+      new_encoding = get_encoding(charset, width);
+      if (new_encoding) {
+       encoding = new_encoding;
+       write_encoding_details = width | bom;
+      }
+    }
+  }
+  else {
+    new_encoding = get_encoding(charset, ONE_BYTE);
+    if (new_encoding) {
+      encoding = new_encoding;
+      write_encoding_details = ONE_BYTE;
+    }
+  }
+  return 0;
+}
+
+int gedcom_write_set_line_terminator(Enc_line_end end)
+{
+  write_terminator = terminator[end];
+  return 0;
+}
+
+Gedcom_write_hndl gedcom_write_open(const char *filename)
+{
+  Gedcom_write_hndl hndl;
+
+  hndl = (Gedcom_write_hndl)malloc(sizeof(struct Gedcom_write_struct));
+
+  if (!hndl)
+    MEMORY_ERROR;
+  else {
+    hndl->total_conv_fails = 0;
+    hndl->conv = initialize_utf8_conversion(encoding, 0);
+    if (!hndl->conv) {
+      gedcom_error(_("Could not open encoding '%s' for writing: %s"),
+                  encoding, strerror(errno));
+      free(hndl);
+      hndl = NULL;
+    }
+    else {
+      hndl->filedesc = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+      if (!hndl->filedesc) {
+       gedcom_error(_("Could not open file '%s' for writing: %s"),
+                    filename, strerror(errno));
+       cleanup_utf8_conversion(hndl->conv);
+       free(hndl);
+       hndl = NULL;
+      }
+      else {
+       hndl->term = write_terminator;
+       hndl->ctxt_level = -1;
+       if (write_encoding_details & WITH_BOM) {
+         if (write_encoding_details & TWO_BYTE_HILO)
+           write(hndl->filedesc, "\xFE\xFF", 2);
+         else if (write_encoding_details & TWO_BYTE_LOHI)
+           write(hndl->filedesc, "\xFF\xFE", 2);
+         else
+           gedcom_warning(_("Byte order mark configured, but no Unicode"));
+       }
+      }
+    }
+  }
+
+  return hndl;
+}
+
+int gedcom_write_close(Gedcom_write_hndl hndl, int* total_conv_fails)
+{
+  int result = 0;
+  if (hndl) {
+    write_simple(hndl, 0, NULL, "TRLR", NULL);
+    if (total_conv_fails)  *total_conv_fails = hndl->total_conv_fails;
+    result = close(hndl->filedesc);
+    cleanup_utf8_conversion(hndl->conv);
+    free(hndl);
+  }
+  return result;
+}
+
+char* get_tag_string(int elt_or_rec, char* tag)
+{
+  char* result = tag_data[elt_or_rec].tag_name;
+
+  if (result)
+    return result;
+  else if (tag)
+    return tag;
+  else {
+    gedcom_error(_("The element or record type '%s' requires a specific tag"
+                  "for writing"),
+                tag_data[elt_or_rec].elt_name);
+    return NULL;
+  }
+}
+
+int check_type(int elt_or_rec, Gedcom_val_type type)
+{
+  int allowed = tag_data[elt_or_rec].allowed_types;
+  if (allowed & type)
+    return 1;
+  else {
+    gedcom_error(_("Wrong data type for writing element or record type '%s'"),
+                tag_data[elt_or_rec].elt_name);
+    return 0;
+  }
+}
+
+int get_level(Gedcom_write_hndl hndl, int elt_or_rec, int parent)
+{
+  if (parent == -1) {
+    hndl->ctxt_level = 0;
+  }
+  else {
+    while (hndl->ctxt_level && hndl->ctxt_stack[hndl->ctxt_level] != parent)
+      hndl->ctxt_level--;
+    if (hndl->ctxt_stack[hndl->ctxt_level] == parent) {
+      hndl->ctxt_level++;
+    }
+    else {
+      gedcom_error(_("Parent %d not found during write of %d"),
+                  parent, elt_or_rec);
+      return -1;
+    }
+  }
+  hndl->ctxt_stack[hndl->ctxt_level] = elt_or_rec;
+  return hndl->ctxt_level;
+}
+
+int gedcom_write_record_str(Gedcom_write_hndl hndl,
+                           Gedcom_rec rec, char* tag,
+                           struct xref_value* xref, char* val)
+{
+  int result = 1;
+  int level = 0;
+  char* tag_str = NULL;
+  char* xref_str = NULL;
+
+  tag_str = get_tag_string(rec, tag);
+  level   = get_level(hndl, rec, -1);
+  if (tag_str && check_type(rec, (val ? GV_CHAR_PTR : GV_NULL))) {
+    if (xref)
+      xref_str = xref->string;
+    result = write_simple(hndl, level, xref_str, tag_str, val);
+  }
+
+  return result;
+}
+
+int gedcom_write_element_str(Gedcom_write_hndl hndl,
+                            Gedcom_elt elt, char* tag, int parent_rec_or_elt,
+                            char* val)
+{
+  int result = 1;
+  int level  = -1;
+  char* tag_str = NULL;
+
+  tag_str = get_tag_string(elt, tag);
+  level   = get_level(hndl, elt, parent_rec_or_elt);
+  if (tag_str && (level != -1)
+      && check_type(elt, (val ? GV_CHAR_PTR : GV_NULL))) {
+    result = write_simple(hndl, level, NULL, tag_str, val);
+  }
+
+  return result;
+}