1 /*
2 * GNU Typist - interactive typing tutor program for UNIX systems
3 *
4 * Copyright (C) 2011 GNU Typist Development Team <bug-gtypist@gnu.org>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21 #include "utf8.h"
22
23 #ifdef HAVE_PDCURSES
24 #include <curses.h>
25 #else
26 #include <ncurses.h>
27 #endif
28
29 #include <stdlib.h>
30 #include <iconv.h>
31 #include <errno.h>
32 #include <ctype.h>
33 #include <wctype.h>
34 #ifdef MINGW
35 #include <Windows.h>
36 #endif
37 #include "gettext.h"
38 #define _(String) gettext (String)
39
40 extern char* locale_encoding;
41 extern int isUTF8Locale;
42
widen(const char * text)43 wchar_t* widen(const char* text)
44 {
45 #ifdef MINGW
46 /* MultiByteToWideChar() (as opposed to mbstowcs) will convert to UTF-16,
47 (2-byte wchar_t) which is all that minGW+PDCurses support */
48 int numChars = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, NULL);
49 wchar_t* wideText = malloc((numChars+1) * sizeof(wchar_t));
50 int convresult = MultiByteToWideChar(CP_UTF8, 0, text,
51 -1, wideText,
52 numChars);
53 #else
54 int numChars = utf8len(text);
55 wchar_t* wideText = malloc((numChars+1) * sizeof(wchar_t));
56 int convresult = mbstowcs(wideText, text, numChars+1);
57 #endif
58
59 if (convresult != numChars)
60 {
61 fatal_error(_("couldn't convert UTF-8 to wide characters"), "?");
62 }
63
64 return wideText;
65 }
66
convertUTF8ToCurrentEncoding(const char * UTF8Input)67 char* convertUTF8ToCurrentEncoding(const char* UTF8Input)
68 {
69 iconv_t cd = iconv_open(locale_encoding, "UTF-8");
70 if (cd == (iconv_t) -1)
71 {
72 endwin();
73 printf("Error in iconv_open()\n");
74 }
75 size_t inleft = strlen(UTF8Input);
76 char* inptr = (char*)UTF8Input;
77 size_t outleft = inleft;
78 char* outptr = (char*)malloc(outleft + 1);
79 char* outptr_orig = outptr;
80 size_t nconv = iconv(cd, &inptr, &inleft, &outptr, &outleft);
81
82 /*
83 endwin();
84 printf("inleft=%d, outleft=%d, nconv=%d\n", inleft, outleft, nconv);
85 */
86 /* TODO: catch EILSEQ?? => no, all errors should lead to termination of gtypist! */
87
88 if (nconv == (size_t) -1)
89 {
90 int err = errno;
91 char buffer[2048];
92 sprintf(buffer, "iconv() failed on '%s': %s\n"
93 "You should probably use a UTF-8 locale for the selected lesson!\n",
94 UTF8Input,
95 strerror(err));
96 fatal_error(_(buffer), "?");
97 }
98
99 iconv_close(cd);
100 int numberChars = strlen(UTF8Input) - outleft;
101 outptr_orig[numberChars] = '\0';
102 return outptr_orig;
103 }
104
convertFromUTF8(const char * UTF8Text)105 wchar_t* convertFromUTF8(const char* UTF8Text)
106 {
107 if (isUTF8Locale)
108 {
109 return widen(UTF8Text);
110 }
111 else
112 {
113 char* textWithCurrentEncoding = convertUTF8ToCurrentEncoding(UTF8Text);
114 int numChars = strlen(textWithCurrentEncoding);
115 wchar_t* wrappedAs_wchar_t = (wchar_t*)malloc((numChars+1) * sizeof(wchar_t));
116 int i;
117 for (i = 0; i < numChars; i++)
118 {
119 wrappedAs_wchar_t[i] = (unsigned char)textWithCurrentEncoding[i];
120 }
121 wrappedAs_wchar_t[numChars] = L'\0';
122 free(textWithCurrentEncoding);
123 return wrappedAs_wchar_t;
124 }
125 }
126
mvwideaddstr(int y,int x,const char * UTF8Text)127 void mvwideaddstr(int y, int x, const char* UTF8Text)
128 {
129 move(y,x);
130 wideaddstr(UTF8Text);
131 }
132
wideaddstr(const char * UTF8Text)133 void wideaddstr(const char* UTF8Text)
134 {
135 if (isUTF8Locale)
136 {
137 addstr(UTF8Text);
138 }
139 else
140 {
141 char* textWithCurrentEncoding = convertUTF8ToCurrentEncoding(UTF8Text);
142 addstr(textWithCurrentEncoding);
143 free(textWithCurrentEncoding);
144 }
145 }
146
wideaddstr_rev(const char * UTF8Text)147 void wideaddstr_rev(const char* UTF8Text)
148 {
149 attron(A_REVERSE);
150 wideaddstr(UTF8Text);
151 attroff(A_REVERSE);
152 }
153
wideaddch(wchar_t c)154 void wideaddch(wchar_t c)
155 {
156 cchar_t c2;
157 wchar_t wc[2];
158 int result;
159
160 if (!isUTF8Locale)
161 {
162 addch(c);
163 return;
164 }
165
166 wc[0] = c;
167 wc[1] = L'\0';
168
169 result = setcchar(&c2, wc, 0, 0, NULL);
170 if (result != OK)
171 {
172 fatal_error(_("error in setcchar()"), "?");
173 }
174 add_wch(&c2);
175 }
176
wideaddch_rev(wchar_t c)177 void wideaddch_rev(wchar_t c)
178 {
179 attron(A_REVERSE);
180 wideaddch(c);
181 attroff(A_REVERSE);
182 }
183
utf8len(const char * UTF8Text)184 int utf8len(const char* UTF8Text)
185 {
186 if (isUTF8Locale)
187 {
188 #ifdef MINGW
189 return MultiByteToWideChar(CP_UTF8, 0, UTF8Text, -1, NULL, NULL) - 1;
190 #else
191 return mbstowcs(NULL, UTF8Text, 0);
192 #endif
193 }
194 else
195 {
196 /* the behavior of mbstowcs depends on LC_CTYPE! That's why
197 we cannot use mbstowcs() for non-utf8 locales */
198 char* textWithCurrentEncoding = convertUTF8ToCurrentEncoding(UTF8Text);
199 int len = strlen(textWithCurrentEncoding);
200 free(textWithCurrentEncoding);
201 return len;
202 }
203 }
204
iswideupper(wchar_t c)205 int iswideupper(wchar_t c)
206 {
207 if (isUTF8Locale)
208 {
209 return iswupper(c);
210 }
211 else
212 {
213 return isupper(c);
214 }
215 }
216
towideupper(wchar_t c)217 wchar_t towideupper(wchar_t c)
218 {
219 if (isUTF8Locale)
220 {
221 return towupper(c);
222 }
223 else
224 {
225 return toupper(c);
226 }
227 }
228
get_widech(int * c)229 int get_widech(int* c)
230 {
231 int ch;
232
233 if (isUTF8Locale)
234 {
235 int retcode = get_wch( &ch );
236 if( retcode == ERR )
237 return ERR;
238 /*
239 ncurses' KEY_BACKSPACE (0x107) collides with polish "c with
240 acute (0x107) => we need to encode KEY_BACKSPACE!
241 */
242 if (retcode == KEY_CODE_YES && ch == KEY_BACKSPACE)
243 {
244 ch = GTYPIST_KEY_BACKSPACE;
245 }
246
247
248 #ifdef MINGW
249 // MinGW defines wint_t as a short int, for compatibility with Windows.
250 ch = ch & 0x0ffff;
251 #endif
252 }
253 else
254 {
255 ch = getch();
256 if( ch == ERR )
257 return ERR;
258 }
259
260 #ifdef HAVE_PDCURSES
261 // make sure PDCurses returns recognisable newline characters
262 if( ch == 0x0D )
263 ch = 0x0A;
264 #endif
265
266 #ifndef HAVE_PDCURSES /* this fix is not necessary for the Windows port */
267 /* Avoid that Alt+<anything> is treated as ESC, which could lead
268 to accidentally aborting a lesson */
269 if (ch == 27) /* ASCII_ESC */
270 {
271 /* undo the halfdelay() from getch_fl() */
272 cbreak();
273 /* switch to non-blocking mode */
274 nodelay(stdscr, TRUE);
275 int ch2;
276 if (get_wch(&ch2) != -1)
277 {
278 /* this is NOT escape because curses sends another key */
279 ch = ch2;
280 }
281 /* switch to blocking mode */
282 nodelay(stdscr, FALSE);
283 }
284 #endif
285
286 *c = ch;
287
288 return OK;
289 }
290
291 /*
292 Local Variables:
293 tab-width: 8
294 End:
295 */
296
297