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