9d842cced378336f6b5dea1e959062127cdd855c
[gedcom-parse.git] / encoding.c
1 /* $Id$ */
2 /* $Name$ */
3
4 #include <string.h>
5 #include <iconv.h>
6 #include <search.h>
7 #include <stdio.h>
8 #include "gedcom.h"
9 #include "encoding.h"
10
11 #define INTERNAL_ENCODING "UTF8"
12 #define ENCODING_CONF_FILE "gedcom.enc"
13 #define MAXBUF 255
14
15 static iconv_t cd_to_internal = (iconv_t) -1;
16 static char int_buf[MAXGEDCLINELEN*2];
17 static void *encoding_mapping = NULL;
18 static ENCODING the_enc = ONE_BYTE;
19
20 struct node {
21   char *gedcom_name;
22   char *iconv_name;
23 };
24
25 char* charwidth_string[] = { "1", "2_HILO", "2_LOHI" };
26
27 int node_compare(const void *node1, const void *node2)
28 {
29   return strcmp(((const struct node *) node1)->gedcom_name,
30                 ((const struct node *) node2)->gedcom_name);
31 }
32
33 void add_encoding(char *gedcom_n, char* charwidth, char *iconv_n)
34 {
35   void **datum;
36   struct node *nodeptr = (struct node *) malloc(sizeof *nodeptr);
37   nodeptr->gedcom_name = (char *) malloc(strlen(gedcom_n)
38                                          + strlen(charwidth) + 3);
39   nodeptr->iconv_name  = (char *) malloc(strlen(iconv_n) + 1);
40   sprintf(nodeptr->gedcom_name, "%s(%s)", gedcom_n, charwidth);
41   strcpy(nodeptr->iconv_name, iconv_n);
42   datum = tsearch(nodeptr, &encoding_mapping, node_compare);
43   if ((datum == NULL) || (*datum != nodeptr)) {
44     gedcom_warning("Duplicate entry found for encoding '%s', ignoring",
45                    gedcom_n);
46   }
47 }
48
49 char* get_encoding(char* gedcom_n, ENCODING enc)
50 {
51   void **datum;
52   struct node search_node;
53   char buffer[MAXBUF + 1];
54   sprintf(buffer, "%s(%s)", gedcom_n, charwidth_string[enc]);
55   search_node.gedcom_name = buffer;
56   datum = tfind(&search_node, &encoding_mapping, node_compare);
57   if (datum == NULL) {
58     gedcom_error("No encoding found for '%s'", gedcom_n);
59     return NULL;
60   }
61   else {
62     return ((const struct node *) *datum)->iconv_name;
63   }
64 }
65
66 void init_encodings()
67 {
68   if (encoding_mapping == NULL) {
69     FILE *in;
70     char buffer[MAXBUF + 1];
71     char gedcom_n[MAXBUF + 1];
72     char charwidth[MAXBUF + 1];
73     char iconv_n[MAXBUF + 1];
74     in = fopen(ENCODING_CONF_FILE, "r");
75     if (in != NULL) {
76       while (fgets(buffer, sizeof(buffer), in) != NULL) {
77         if (buffer[strlen(buffer) - 1] != '\n') {
78           gedcom_error("Line too long in encoding configuration file '%s'",
79                        ENCODING_CONF_FILE);
80           return;
81         }
82         else if ((buffer[0] != '#') && (strcmp(buffer, "\n") != 0)) {
83           if (sscanf(buffer, "%s %s %s", gedcom_n, charwidth, iconv_n) == 3) {
84             add_encoding(gedcom_n, charwidth, iconv_n);
85           }
86           else {
87             gedcom_error("Missing data in encoding configuration file '%s'",
88                          ENCODING_CONF_FILE);
89             return;
90           }
91         }
92       }
93       fclose(in);
94     }
95     else {
96       gedcom_warning("Could not open encoding configuration file '%s'",
97                      ENCODING_CONF_FILE);
98     }
99   }
100 }
101
102 void set_encoding_width(ENCODING enc)
103 {
104   the_enc = enc;
105 }
106
107 static char conv_buf[MAXGEDCLINELEN * 2];
108 static int conv_buf_size;
109
110 int open_conv_to_internal(char* fromcode)
111 {
112   char *encoding = get_encoding(fromcode, the_enc);
113   if (cd_to_internal != (iconv_t) -1)
114     iconv_close(cd_to_internal);
115   if (encoding == NULL) {
116     cd_to_internal = (iconv_t) -1;
117   }
118   else {
119     memset(conv_buf, 0, sizeof(conv_buf));
120     conv_buf_size = 0;
121     cd_to_internal = iconv_open(INTERNAL_ENCODING, encoding);
122     if (cd_to_internal == (iconv_t) -1) {
123       gedcom_error("Error opening conversion context for encoding %s: %s",
124                    encoding, strerror(errno));
125     }
126   }
127   return (cd_to_internal != (iconv_t) -1);  
128 }
129
130 void close_conv_to_internal()
131 {
132   iconv_close(cd_to_internal);
133   cd_to_internal = (iconv_t) -1;
134 }
135
136 char* to_internal(char* str, size_t len)
137 {
138   size_t insize;
139   size_t outsize = MAXGEDCLINELEN * 2;
140   char *wrptr = int_buf;
141   char *rdptr = conv_buf;
142   /* set up input buffer (concatenate to what was left previous time) */
143   /* can't use strcpy, because possible null bytes from unicode */
144   memcpy(conv_buf + conv_buf_size, str, len);
145   conv_buf_size += len;
146   insize = conv_buf_size;
147   /* set up output buffer (empty it) */
148   memset(int_buf, 0, sizeof(int_buf));
149   /* do the conversion */
150   iconv(cd_to_internal, &rdptr, &insize, &wrptr, &outsize);
151   /* then shift what is left over to the head of the input buffer */
152   memmove(conv_buf, rdptr, insize);
153   memset(conv_buf + insize, 0, sizeof(conv_buf) - insize);
154   conv_buf_size = insize;
155   return int_buf;
156 }
157