Freeciv-3.3
Loading...
Searching...
No Matches
fciconv.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 2003-2004 - The Freeciv Project
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18#include <errno.h>
19#include <stdarg.h>
20#include <stdio.h>
21#include <string.h>
22
23#ifdef HAVE_ICONV
24#include <iconv.h>
25#endif
26
27#ifdef HAVE_LANGINFO_CODESET
28#include <langinfo.h>
29#endif
30
31#ifdef HAVE_LIBCHARSET
32#include <libcharset.h>
33#endif
34
35/* utility */
36#include "fciconv.h"
37#include "fcintl.h"
38#include "log.h"
39#include "mem.h"
40#include "support.h"
41
42static bool is_init = FALSE;
43static char convert_buffer[4096];
44static const char *transliteration_string;
45
46#ifdef HAVE_ICONV
48#else /* HAVE_ICONV */
49/* Hack to confuse the compiler into working. */
50# define local_encoding get_local_encoding()
51# define data_encoding get_local_encoding()
52# define internal_encoding get_local_encoding()
53#endif /* HAVE_ICONV */
54
55static char *saved_from = nullptr;
56static char *saved_to = nullptr;
57
58static char *convert_string(const char *text,
59 const char *from,
60 const char *to,
61 char *buf, size_t bufsz)
62 fc__attribute((nonnull (1,2,3)));
63
64/***********************************************************************/
72{
74#ifdef HAVE_ICONV
76 transliteration_string = "//TRANSLIT";
77 }
78
79 /* Set the data encoding - first check $FREECIV_DATA_ENCODING,
80 * then fall back to the default. */
81 data_encoding = getenv("FREECIV_DATA_ENCODING");
82 if (!data_encoding) {
84 }
85
86 /* Set the local encoding - first check $FREECIV_LOCAL_ENCODING,
87 * then ask the system. */
88 local_encoding = getenv("FREECIV_LOCAL_ENCODING");
89 if (!local_encoding) {
90#ifdef HAVE_LIBCHARSET
92#else /* HAVE_LIBCHARSET */
93#ifdef HAVE_LANGINFO_CODESET
95#else /* HAVE_LANGINFO_CODESET */
96 local_encoding = "";
97#endif /* HAVE_LANGINFO_CODESET */
98#endif /* HAVE_LIBCHARSET */
99 if (fc_strcasecmp(local_encoding, "ANSI_X3.4-1968") == 0
100 || fc_strcasecmp(local_encoding, "ASCII") == 0
101 || fc_strcasecmp(local_encoding, "US-ASCII") == 0) {
102 /* HACK: use latin1 instead of ascii in typical cases when the
103 * encoding is unconfigured. */
104 local_encoding = "ISO-8859-1";
105 }
106
107 if (fc_strcasecmp(local_encoding, "646") == 0) {
108 /* HACK: On Solaris the encoding always comes up as "646" (ascii),
109 * which iconv doesn't understand. Work around it by using UTF-8
110 * instead. */
111 local_encoding = "UTF-8";
112 }
113 }
114
115 /* Set the internal encoding - first check $FREECIV_INTERNAL_ENCODING,
116 * then check the passed-in default value, then fall back to the local
117 * encoding. */
118 internal_encoding = getenv("FREECIV_INTERNAL_ENCODING");
119 if (!internal_encoding) {
121
122 if (!internal_encoding) {
124 }
125 }
126
127#ifdef FREECIV_ENABLE_NLS
129#endif
130
131#ifdef FREECIV_DEBUG
132 fprintf(stderr, "Encodings: Data=%s, Local=%s, Internal=%s\n",
134#endif /* FREECIV_DEBUG */
135
136#else /* HAVE_ICONV */
137 /* log_* may not work at this point. */
139 _("You are running Freeciv without using iconv. Unless\n"
140 "you are using the UTF-8 character set, some characters\n"
141 "may not be displayed properly. You can download iconv\n"
142 "at https://gnu.org/.\n"));
143#endif /* HAVE_ICONV */
144
145 is_init = TRUE;
146}
147
148/***********************************************************************/
151const char *get_data_encoding(void)
152{
153 fc_assert_ret_val(is_init, nullptr);
154 return data_encoding;
155}
156
157/***********************************************************************/
160const char *get_local_encoding(void)
161{
162#ifdef HAVE_ICONV
163 fc_assert_ret_val(is_init, nullptr);
164 return local_encoding;
165#else /* HAVE_ICONV */
166# ifdef HAVE_LIBCHARSET
167 return locale_charset();
168# else /* HAVE_LIBCHARSET */
169# ifdef HAVE_LANGINFO_CODESET
170 return nl_langinfo(CODESET);
171# else /* HAVE_LANGINFO_CODESET */
172 return "";
173# endif /* HAVE_LANGINFO_CODESET */
174# endif /* HAVE_LIBCHARSET */
175#endif /* HAVE_ICONV */
176}
177
178/***********************************************************************/
182const char *get_internal_encoding(void)
183{
184 fc_assert_ret_val(is_init, nullptr);
185
186 return internal_encoding;
187}
188
189/***********************************************************************/
197static char *convert_string(const char *text,
198 const char *from,
199 const char *to,
200 char *buf, size_t bufsz)
201{
202#ifdef HAVE_ICONV
203 iconv_t cd = iconv_open(to, from);
204 size_t from_len = strlen(text) + 1, to_len;
205 bool alloc = (buf == nullptr);
206
207 if (cd == (iconv_t) (-1)) {
208 /* Do not do potentially recursive call to freeciv logging here,
209 * but use fprintf(stderr) */
210 /* Use the real OS-provided strerror and errno rather than Freeciv's
211 * abstraction, as that wouldn't do the correct thing with third-party
212 * iconv on Windows. */
213
214 if (saved_from == nullptr
215 || strcmp(saved_from, from)
216 || strcmp(saved_to, to)) {
217
218 /* TRANS: "Could not convert text from <encoding a> to <encoding b>:"
219 * <externally translated error string>." */
220 fprintf(stderr, _("Could not convert text from %s to %s: %s.\n"),
221 from, to, strerror(errno));
222
223 if (saved_from != nullptr) {
225 free(saved_to);
226 }
227
228 saved_from = fc_strdup(from);
229 saved_to = fc_strdup(to);
230 }
231
232 /* The best we can do? */
233 if (alloc) {
234 return fc_strdup(text);
235 } else {
236 fc_snprintf(buf, bufsz, "%s", text);
237 return buf;
238 }
239 }
240
241 if (alloc) {
243 } else {
244 to_len = bufsz;
245 }
246
247 do {
248 size_t flen = from_len, tlen = to_len, res;
249 const char *mytext = text;
250 char *myresult;
251
252 if (alloc) {
254 }
255
256 myresult = buf;
257
258 /* Since we may do multiple translations, we may need to reset iconv
259 * in between. */
260 iconv(cd, nullptr, nullptr, nullptr, nullptr);
261
262 res = iconv(cd, (ICONV_CONST char **)&mytext, &flen, &myresult, &tlen);
263 if (res == (size_t) (-1)) {
264 if (errno != E2BIG) {
265 /* Invalid input. */
266
267 fprintf(stderr, "Invalid string conversion from %s to %s: %s.\n",
268 from, to, strerror(errno));
270 if (alloc) {
271 free(buf);
272 return fc_strdup(text); /* The best we can do? */
273 } else {
274 fc_snprintf(buf, bufsz, "%s", text);
275 return buf;
276 }
277 }
278 } else {
279 /* Success. */
281
282 /* There may be wasted space here, but there's nothing we can do
283 * about it. */
284 return buf;
285 }
286
287 if (alloc) {
288 /* Not enough space; try again. */
289 buf[to_len - 1] = 0;
290
291 free(buf);
292 to_len *= 2;
293 }
294 } while (alloc);
295
296 return buf;
297#else /* HAVE_ICONV */
298 if (buf) {
299 strncpy(buf, text, bufsz);
300 buf[bufsz - 1] = '\0';
301 return buf;
302 } else {
303 return fc_strdup(text);
304 }
305#endif /* HAVE_ICONV */
306}
307
308#define CONV_FUNC_MALLOC(src, dst) \
309char *src ## _to_ ## dst ## _string_malloc(const char *text) \
310{ \
311 const char *encoding1 = (dst ## _encoding); \
312 char encoding[strlen(encoding1) + strlen(transliteration_string) + 1]; \
313 \
314 fc_snprintf(encoding, sizeof(encoding), \
315 "%s%s", encoding1, transliteration_string); \
316 return convert_string(text, (src ## _encoding), \
317 (encoding), nullptr, 0); \
318}
319
320#define CONV_FUNC_BUFFER(src, dst) \
321char *src ## _to_ ## dst ## _string_buffer(const char *text, \
322 char *buf, size_t bufsz) \
323{ \
324 const char *encoding1 = (dst ## _encoding); \
325 char encoding[strlen(encoding1) + strlen(transliteration_string) + 1]; \
326 \
327 fc_snprintf(encoding, sizeof(encoding), \
328 "%s%s", encoding1, transliteration_string); \
329 return convert_string(text, (src ## _encoding), \
330 encoding, buf, bufsz); \
331}
332
333#define CONV_FUNC_STATIC(src, dst) \
334char *src ## _to_ ## dst ## _string_static(const char *text) \
335{ \
336 (src ## _to_ ## dst ## _string_buffer)(text, \
337 convert_buffer, \
338 sizeof(convert_buffer)); \
339 return convert_buffer; \
340}
341
346
349
351
352/***********************************************************************/
355void fc_fprintf(FILE *stream, const char *format, ...)
356{
357 va_list ap;
358 char string[4096];
359 const char *output;
360 static bool recursion = FALSE;
361
362 /* The recursion variable is used to prevent a recursive loop. If
363 * an iconv conversion fails, then log_* will be called and an
364 * fc_fprintf will be done. But below we do another iconv conversion
365 * on the error messages, which is of course likely to fail also. */
366 if (recursion) {
367 return;
368 }
369
370 va_start(ap, format);
371 fc_vsnprintf(string, sizeof(string), format, ap);
372 va_end(ap);
373
374 recursion = TRUE;
375 if (is_init) {
376 output = internal_to_local_string_static(string);
377 } else {
378 output = string;
379 }
381
382 fputs(output, stream);
383 fflush(stream);
384}
385
386/***********************************************************************/
396size_t get_internal_string_length(const char *text)
397{
398 int text2[(strlen(text) + 1)]; /* UCS-4 text */
399 int i;
400 int len = 0;
401
402 convert_string(text, internal_encoding, "UCS-4",
403 (char *)text2, sizeof(text2));
404 for (i = 0; ; i++) {
405 if (text2[i] == 0) {
406 return len;
407 }
408 if (text2[i] != 0x0000FEFF && text2[i] != 0xFFFE0000) {
409 /* Not BOM */
410 len++;
411 }
412 }
413}
414
415/***********************************************************************/
419{
420 if (saved_from != nullptr) {
422 saved_from = nullptr;
423 }
424
425 if (saved_to != nullptr) {
426 free(saved_to);
427 saved_to = nullptr;
428 }
429}
char * incite_cost
Definition comments.c:76
size_t get_internal_string_length(const char *text)
Definition fciconv.c:396
static char * saved_to
Definition fciconv.c:56
#define CONV_FUNC_BUFFER(src, dst)
Definition fciconv.c:320
static char convert_buffer[4096]
Definition fciconv.c:43
static char * saved_from
Definition fciconv.c:55
static const char * transliteration_string
Definition fciconv.c:44
void fc_iconv_close(void)
Definition fciconv.c:418
#define CONV_FUNC_MALLOC(src, dst)
Definition fciconv.c:308
const char * get_data_encoding(void)
Definition fciconv.c:151
#define internal_encoding
Definition fciconv.c:52
static bool is_init
Definition fciconv.c:42
#define data_encoding
Definition fciconv.c:51
static char * convert_string(const char *text, const char *from, const char *to, char *buf, size_t bufsz) fc__attribute((nonnull(1
Definition fciconv.c:197
const char * get_internal_encoding(void)
Definition fciconv.c:182
#define CONV_FUNC_STATIC(src, dst)
Definition fciconv.c:333
static char void init_character_encodings(const char *my_internal_encoding, bool my_use_transliteration)
Definition fciconv.c:70
#define local_encoding
Definition fciconv.c:50
const char * get_local_encoding(void)
Definition fciconv.c:160
#define FC_DEFAULT_DATA_ENCODING
Definition fciconv.h:89
void fc_fprintf(FILE *stream, const char *format,...) fc__attribute((__format__(__printf__
#define _(String)
Definition fcintl.h:67
static const int bufsz
Definition helpdlg.c:70
#define fc_assert_ret_val(condition, val)
Definition log.h:195
#define fc_strdup(str)
Definition mem.h:43
#define fc_malloc(sz)
Definition mem.h:34
int len
Definition packhand.c:127
static int recursion[AIT_LAST]
Definition srv_log.c:45
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:960
int fc_strcasecmp(const char *str0, const char *str1)
Definition support.c:186
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:886
#define fc__attribute(x)
Definition support.h:99
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47