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