Remove superfluous printf statement.
[gedcom-parse.git] / gedcom / encoding.c
1 /*  This program is free software; you can redistribute it and/or modify  *
2  *  it under the terms of the GNU General Public License as published by  *
3  *  the Free Software Foundation; either version 2 of the License, or     *
4  *  (at your option) any later version.                                   *
5
6  (C) 2001 by The Genes Development Team
7  Original author: Peter Verthez (Peter.Verthez@advalvas.be)
8 */
9
10 /* $Id$ */
11 /* $Name$ */
12
13 #include <string.h>
14 #include <iconv.h>
15 #include <search.h>
16 #include <stdio.h>
17 #include <limits.h>
18 #include <stdlib.h>
19 #include "gedcom_internal.h"
20 #include "encoding.h"
21
22 #define INTERNAL_ENCODING "UTF8"
23 #define ENCODING_CONF_FILE "gedcom.enc"
24 #define GCONV_SEARCH_PATH "GCONV_PATH"
25 #define MAXBUF 255
26
27 static iconv_t cd_to_internal = (iconv_t) -1;
28 static void *encoding_mapping = NULL;
29 static ENCODING the_enc = ONE_BYTE;
30
31 struct node {
32   char *gedcom_name;
33   char *iconv_name;
34 };
35
36 char* charwidth_string[] = { "1", "2_HILO", "2_LOHI" };
37
38 int node_compare(const void *node1, const void *node2)
39 {
40   return strcmp(((const struct node *) node1)->gedcom_name,
41                 ((const struct node *) node2)->gedcom_name);
42 }
43
44 void add_encoding(char *gedcom_n, char* charwidth, char *iconv_n)
45 {
46   void **datum;
47   struct node *nodeptr = (struct node *) malloc(sizeof *nodeptr);
48   nodeptr->gedcom_name = (char *) malloc(strlen(gedcom_n)
49                                          + strlen(charwidth) + 3);
50   nodeptr->iconv_name  = (char *) malloc(strlen(iconv_n) + 1);
51   /* sprintf is safe here (malloc'ed before) */
52   sprintf(nodeptr->gedcom_name, "%s(%s)", gedcom_n, charwidth);
53   strcpy(nodeptr->iconv_name, iconv_n);
54   datum = tsearch(nodeptr, &encoding_mapping, node_compare);
55   if ((datum == NULL) || (*datum != nodeptr)) {
56     gedcom_warning("Duplicate entry found for encoding '%s', ignoring",
57                    gedcom_n);
58   }
59 }
60
61 char* get_encoding(char* gedcom_n, ENCODING enc)
62 {
63   void **datum;
64   struct node search_node;
65   char *buffer;
66   buffer = (char*)malloc(strlen(gedcom_n) + strlen(charwidth_string[enc]) + 3);
67   /* sprintf is safe here (malloc'ed before) */
68   sprintf(buffer, "%s(%s)", gedcom_n, charwidth_string[enc]);
69   search_node.gedcom_name = buffer;
70   datum = tfind(&search_node, &encoding_mapping, node_compare);
71   free(buffer);
72   if (datum == NULL) {
73     gedcom_error("No encoding found for '%s'", gedcom_n);
74     return NULL;
75   }
76   else {
77     return ((const struct node *) *datum)->iconv_name;
78   }
79 }
80
81 void init_encodings()
82 {
83   if (encoding_mapping == NULL) {
84     FILE *in;
85     char buffer[MAXBUF + 1];
86     char gedcom_n[MAXBUF + 1];
87     char charwidth[MAXBUF + 1];
88     char iconv_n[MAXBUF + 1];
89     char *gconv_path;
90
91     /* Add gedcom data directory to gconv search path */
92     gconv_path = getenv(GCONV_SEARCH_PATH);
93     if (gconv_path == NULL || strstr(gconv_path, PKGDATADIR) == NULL) {
94       char *new_gconv_path;
95       if (gconv_path == NULL) {
96         new_gconv_path = (char *)malloc(strlen(GCONV_SEARCH_PATH)
97                                         + strlen(PKGDATADIR)
98                                         + 2);
99         sprintf(new_gconv_path, "%s=%s", GCONV_SEARCH_PATH, PKGDATADIR);
100       }
101       else {
102         new_gconv_path = (char *)malloc(strlen(GCONV_SEARCH_PATH)
103                                         + strlen(gconv_path)
104                                         + strlen(PKGDATADIR)
105                                         + 3);
106         sprintf(new_gconv_path, "%s=%s:%s",
107                 GCONV_SEARCH_PATH, gconv_path, PKGDATADIR);
108       }
109       if (putenv(new_gconv_path) != 0) {
110         gedcom_warning("Failed updating environment variable %s",
111                        GCONV_SEARCH_PATH);
112       }
113     }
114     
115     /* Open gedcom configuration file and read */
116     in = fopen(ENCODING_CONF_FILE, "r");
117     if (in == NULL) {
118       char path[PATH_MAX];
119       sprintf(path, "%s/%s", PKGDATADIR, ENCODING_CONF_FILE);
120       in = fopen(path, "r");
121     }
122     if (in == NULL) {
123       gedcom_warning("Could not open encoding configuration file '%s'",
124                      ENCODING_CONF_FILE);
125     }
126     else {
127       while (fgets(buffer, sizeof(buffer), in) != NULL) {
128         if (buffer[strlen(buffer) - 1] != '\n') {
129           gedcom_error("Line too long in encoding configuration file '%s'",
130                        ENCODING_CONF_FILE);
131           return;
132         }
133         else if ((buffer[0] != '#') && (strcmp(buffer, "\n") != 0)) {
134           if (sscanf(buffer, "%s %s %s", gedcom_n, charwidth, iconv_n) == 3) {
135             add_encoding(gedcom_n, charwidth, iconv_n);
136           }
137           else {
138             gedcom_error("Missing data in encoding configuration file '%s'",
139                          ENCODING_CONF_FILE);
140             return;
141           }
142         }
143       }
144       fclose(in);
145     }
146   }
147 }
148
149 void set_encoding_width(ENCODING enc)
150 {
151   the_enc = enc;
152 }
153
154 static char conv_buf[MAXGEDCLINELEN * 2];
155 static size_t conv_buf_size;
156
157 int open_conv_to_internal(char* fromcode)
158 {
159   char *encoding = get_encoding(fromcode, the_enc);
160   if (cd_to_internal != (iconv_t) -1)
161     iconv_close(cd_to_internal);
162   if (encoding == NULL) {
163     cd_to_internal = (iconv_t) -1;
164   }
165   else {
166     memset(conv_buf, 0, sizeof(conv_buf));
167     conv_buf_size = 0;
168     cd_to_internal = iconv_open(INTERNAL_ENCODING, encoding);
169     if (cd_to_internal == (iconv_t) -1) {
170       gedcom_error("Error opening conversion context for encoding %s: %s",
171                    encoding, strerror(errno));
172     }
173   }
174   return (cd_to_internal != (iconv_t) -1);  
175 }
176
177 void close_conv_to_internal()
178 {
179   iconv_close(cd_to_internal);
180   cd_to_internal = (iconv_t) -1;
181 }
182
183 char* to_internal(char* str, size_t len,
184                   char* output_buffer, size_t out_len)
185 {
186   size_t outsize = out_len;
187   char *wrptr = output_buffer;
188   char *rdptr = conv_buf;
189   /* set up input buffer (concatenate to what was left previous time) */
190   /* can't use strcpy, because possible null bytes from unicode */
191   memcpy(conv_buf + conv_buf_size, str, len);
192   conv_buf_size += len;
193   /* set up output buffer (empty it) */
194   memset(output_buffer, 0, out_len);
195   /* do the conversion */
196   iconv(cd_to_internal, &rdptr, &conv_buf_size, &wrptr, &outsize);
197   /* then shift what is left over to the head of the input buffer */
198   memmove(conv_buf, rdptr, conv_buf_size);
199   memset(conv_buf + conv_buf_size, 0, sizeof(conv_buf) - conv_buf_size);
200   return output_buffer;
201 }