| /* |
| * BRLTTY - A background process providing access to the console screen (when in |
| * text mode) for a blind person using a refreshable braille display. |
| * |
| * Copyright (C) 1995-2023 by The BRLTTY Developers. |
| * |
| * BRLTTY comes with ABSOLUTELY NO WARRANTY. |
| * |
| * This is free software, placed under the terms of the |
| * GNU Lesser General Public License, as published by the Free Software |
| * Foundation; either version 2.1 of the License, or (at your option) any |
| * later version. Please see the file LICENSE-LGPL for details. |
| * |
| * Web Page: http://brltty.app/ |
| * |
| * This software is maintained by Dave Mielke <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <iconv.h> |
| #include <locale.h> |
| |
| #ifdef HAVE_LANGINFO_H |
| #include <langinfo.h> |
| #endif /* HAVE_LANGINFO_H */ |
| |
| #include "log.h" |
| #include "charset_internal.h" |
| #include "program.h" |
| |
| #define CHARSET_ICONV_NULL ((iconv_t)-1) |
| #define CHARSET_ICONV_HANDLE(name) iconv_t iconv##name = CHARSET_ICONV_NULL |
| |
| static CHARSET_ICONV_HANDLE(CharToWchar); |
| static CHARSET_ICONV_HANDLE(WcharToChar); |
| |
| #define CHARSET_CONVERT_TYPE_TO_TYPE(name, from, to, ret, eof) \ |
| ret convert##name (from f) { \ |
| if (getCharset()) { \ |
| from *fp = &f; \ |
| size_t fs = sizeof(f); \ |
| to t; \ |
| to *tp = &t; \ |
| size_t ts = sizeof(t); \ |
| if (iconv(iconv##name, (void *)&fp, &fs, (void *)&tp, &ts) != (size_t)-1) return t; \ |
| logMessage(LOG_DEBUG, "iconv (" #from " -> " #to ") error: %s", strerror(errno)); \ |
| } \ |
| return eof; \ |
| } |
| CHARSET_CONVERT_TYPE_TO_TYPE(CharToWchar, char, wchar_t, wint_t, WEOF) |
| CHARSET_CONVERT_TYPE_TO_TYPE(WcharToChar, wchar_t, unsigned char, int, EOF) |
| #undef CHARSET_CONVERT_TYPE_TO_TYPE |
| |
| const char * |
| getLocaleCharset (void) { |
| const char *locale = setlocale(LC_ALL, ""); |
| |
| if (locale && !isPosixLocale(locale)) { |
| #ifdef HAVE_NL_LANGINFO |
| /* some 8-bit locale is set, assume its charset is correct */ |
| return nl_langinfo(CODESET); |
| #endif /* HAVE_NL_LANGINFO */ |
| } |
| |
| return defaultCharset; |
| } |
| |
| static void |
| exitCharsetIconv (void *data) { |
| static iconv_t *const handles[] = { |
| &iconvCharToWchar, |
| &iconvWcharToChar |
| }; |
| |
| iconv_t *const *handle = handles; |
| iconv_t *const *const end = handle + ARRAY_COUNT(handles); |
| |
| while (handle < end) { |
| if (**handle != CHARSET_ICONV_NULL) { |
| iconv_close(**handle); |
| **handle = CHARSET_ICONV_NULL; |
| } |
| |
| handle += 1; |
| } |
| } |
| |
| int |
| registerCharacterSet (const char *charset) { |
| int firstTime = 0; |
| const char *const wcharCharset = getWcharCharset(); |
| |
| typedef struct { |
| iconv_t *const handle; |
| const char *const fromCharset; |
| const char *const toCharset; |
| |
| iconv_t newHandle; |
| } ConvEntry; |
| |
| ConvEntry convTable[] = { |
| { .handle = &iconvCharToWchar, |
| .fromCharset = charset, |
| .toCharset = wcharCharset |
| }, |
| |
| { .handle = &iconvWcharToChar, |
| .fromCharset = wcharCharset, |
| .toCharset = charset |
| }, |
| }; |
| |
| ConvEntry *conv = convTable; |
| const ConvEntry *convEnd = conv + ARRAY_COUNT(convTable); |
| |
| while (conv < convEnd) { |
| if ((conv->newHandle = iconv_open(conv->toCharset, conv->fromCharset)) == CHARSET_ICONV_NULL) { |
| logSystemError("iconv_open"); |
| |
| while (conv > convTable) { |
| conv -= 1; |
| iconv_close(conv->newHandle); |
| } |
| |
| return 0; |
| } |
| |
| conv += 1; |
| } |
| |
| while (conv > convTable) { |
| conv -= 1; |
| |
| if (*conv->handle == CHARSET_ICONV_NULL) { |
| firstTime = 1; |
| } else { |
| iconv_close(*conv->handle); |
| } |
| |
| *conv->handle = conv->newHandle; |
| } |
| |
| if (firstTime) onProgramExit("charset-iconv", exitCharsetIconv, NULL); |
| return 1; |
| } |