1 /* messages.c
2 * Error, warning, and info messages in your host language,
3 * as determined by the variable $LANG.
4 * Messages can be generated in iso-8859-1, but utf8 is recommended.
5 * This file is part of the edbrowse project, released under GPL.
6 */
7
8 #include "eb.h"
9
10 #include <locale.h>
11
12 /* English by default */
13 static const char **messageArray;
14 char eb_language[8];
15 int eb_lang;
16 /* startup .ebrc files in various languages */
17 const char *ebrc_string;
18 static const char *qrg_string;
19 bool cons_utf8, iuConvert = true;
20 char type8859 = 1;
21 bool helpMessagesOn;
22 bool errorExit;
23
selectLanguage(void)24 void selectLanguage(void)
25 {
26 char *s = getenv("LANG"); // This is likely to fail in windows
27 char *dot;
28
29 // default English
30 strcpy(eb_language, "en");
31 eb_lang = 1;
32 messageArray = msg_en;
33 ebrc_string = ebrc_en;
34 qrg_string = qrg_en;
35
36 #ifndef DOSLIKE
37 if (!s)
38 return;
39 if (!*s)
40 return;
41
42 if (strstrCI(s, "utf8") || strstrCI(s, "utf-8"))
43 cons_utf8 = true;
44
45 /* We roll our own international messages in this file, so you wouldn't think
46 * we need setlocale, but pcre needs the locale for expressions like \w,
47 * and for ranges like [A-z],
48 * and to convert to upper or lower case etc.
49 * So I set LC_ALL, which covers both LC_CTYPE and LC_COLLATE.
50 * By calling strcoll, the directory scan is in the same order as ls.
51 * See dircmp() in stringfile.c */
52
53 setlocale(LC_ALL, "");
54
55 /* But LC_TIME controls time/date formatting, I.E., strftime. The one
56 * place we do that, we need standard day/month abbreviations, not
57 * localized ones. So LC_TIME needs to be C. */
58 setlocale(LC_TIME, "C");
59 #else // DOSLIKE
60
61 /* I'm going to assume Windows runs utf8 */
62 cons_utf8 = true;
63
64 if (!s)
65 s = setlocale(LC_ALL, "");
66 if (!s)
67 return;
68 if (!*s)
69 return;
70 setlocale(LC_TIME, "C");
71 #endif // DOSLIKE y/n
72
73 strncpy(eb_language, s, 7);
74 eb_language[7] = 0;
75 caseShift(eb_language, 'l');
76 dot = strchr(eb_language, '.');
77 if (dot)
78 *dot = 0;
79
80 if (!strncmp(eb_language, "en", 2))
81 return; /* english is already the default */
82
83 if (!strncmp(eb_language, "fr", 2)) {
84 eb_lang = 2;
85 messageArray = msg_fr;
86 ebrc_string = ebrc_fr;
87 qrg_string = qrg_fr;
88 type8859 = 1;
89 return;
90 }
91
92 if (!strncmp(eb_language, "pt_br", 5)) {
93 eb_lang = 3;
94 messageArray = msg_pt_br;
95 ebrc_string = ebrc_pt_br;
96 qrg_string = qrg_pt_br;
97 type8859 = 1;
98 return;
99 }
100
101 if (!strncmp(eb_language, "pl", 2)) {
102 eb_lang = 4;
103 messageArray = msg_pl;
104 ebrc_string = ebrc_pl;
105 type8859 = 2;
106 return;
107 }
108
109 if (!strncmp(eb_language, "de", 2)) {
110 eb_lang = 5;
111 messageArray = msg_de;
112 ebrc_string = ebrc_de;
113 type8859 = 1;
114 return;
115 }
116
117 if (!strncmp(eb_language, "ru", 2)) {
118 eb_lang = 6;
119 messageArray = msg_ru;
120 type8859 = 5;
121 return;
122 }
123
124 if (!strncmp(eb_language, "it", 2)) {
125 eb_lang = 7;
126 messageArray = msg_it;
127 type8859 = 1;
128 return;
129 }
130
131 /* This error is really annoying if it pops up every time you invoke edbrowse.
132 fprintf(stderr, "Sorry, language %s is not implemented\n", buf);
133 */
134 } /* selectLanguage */
135
136 /*********************************************************************
137 WARNING: this routine, which is at the heart of the international prints
138 i_puts i_printf, is not threadsafe in iso8859 mode.
139 Well utf8 has been the default console standard for 15 years now,
140 and I'm almost ready to chuck iso8859 altogether, so for now,
141 let's just say you can't use threading in 8859 mode.
142 If you try to turn it on via the bg (background) command, I won't let you.
143 I really don't think this will come up, everybody is utf8 by now.
144 *********************************************************************/
145
i_getString(int msg)146 const char *i_getString(int msg)
147 {
148 const char **a = messageArray;
149 const char *s;
150 char *t;
151 int t_len;
152 static char utfbuf[1000];
153
154 if (msg >= EdbrowseMessageCount)
155 s = emptyString;
156 else
157 s = a[msg];
158 if (!s)
159 s = msg_en[msg];
160 if (!s)
161 s = "spurious message";
162
163 if (cons_utf8)
164 return s;
165
166 /* We have to convert it. */
167 utf2iso((uchar *) s, strlen(s), (uchar **) & t, &t_len);
168 strcpy(utfbuf, t);
169 nzFree(t);
170 return utfbuf;
171 } /* i_getString */
172
173 /*********************************************************************
174 Internationalize the standard puts and printf.
175 These are simple informational messages, where you don't need to error out,
176 or check the debug level, or store the error in a buffer.
177 The i_ prefix means international.
178 *********************************************************************/
179
i_puts(int msg)180 void i_puts(int msg)
181 {
182 eb_puts(i_getString(msg));
183 } /* i_puts */
184
185 static void eb_vprintf(const char *fmt, va_list args);
186
i_printf(int msg,...)187 void i_printf(int msg, ...)
188 {
189 const char *realmsg = i_getString(msg);
190 va_list p;
191 va_start(p, msg);
192 eb_vprintf(realmsg, p);
193 va_end(p);
194 if (debugFile) {
195 va_start(p, msg);
196 vfprintf(debugFile, realmsg, p);
197 va_end(p);
198 }
199 } /* i_printf */
200
201 /* Print and exit. This puts newline on, like puts. */
i_printfExit(int msg,...)202 void i_printfExit(int msg, ...)
203 {
204 const char *realmsg = i_getString(msg);
205 va_list p;
206 va_start(p, msg);
207 eb_vprintf(realmsg, p);
208 nl();
209 va_end(p);
210 if (debugFile) {
211 va_start(p, msg);
212 vfprintf(debugFile, realmsg, p);
213 fprintf(debugFile, "\n");
214 va_end(p);
215 }
216 ebClose(99);
217 } /* i_printfExit */
218
219 /* i_stringAndMessage: concatenate a message to an existing string. */
i_stringAndMessage(char ** s,int * l,int messageNum)220 void i_stringAndMessage(char **s, int *l, int messageNum)
221 {
222 const char *messageText = i_getString(messageNum);
223 stringAndString(s, l, messageText);
224 } /* i_stringAndMessage */
225
226 /*********************************************************************
227 The following error display functions are specific to edbrowse,
228 rather than extended versions of the standard unix print functions.
229 Thus I don't need the i_ prefix.
230 *********************************************************************/
231
232 char errorMsg[1024];
233
234 /* Show the error message, not just the question mark, after these commands. */
235 static const char showerror_cmd[] = "AbefMqrw^&";
236
237 /* Set the error message. Type h to see the message. */
setError(int msg,...)238 void setError(int msg, ...)
239 {
240 va_list p;
241 char *a; // result of vasprintf
242 int l;
243
244 if (msg < 0) {
245 errorMsg[0] = 0;
246 return;
247 }
248
249 va_start(p, msg);
250 if (vasprintf(&a, i_getString(msg), p) < 0)
251 i_printfExit(MSG_MemAllocError, 4096);
252 va_end(p);
253 // If the error message is crazy long, truncate it.
254 l = sizeof(errorMsg) - 1;
255 strncpy(errorMsg, a, l);
256 nzFree(a);
257 } /* setError */
258
showError(void)259 void showError(void)
260 {
261 if (errorMsg[0])
262 eb_puts(errorMsg);
263 else
264 i_puts(MSG_NoErrors);
265 } /* showError */
266
showErrorConditional(char cmd)267 void showErrorConditional(char cmd)
268 {
269 if (helpMessagesOn || strchr(showerror_cmd, cmd))
270 showError();
271 else
272 eb_puts("?");
273 } /* showErrorConditional */
274
showErrorAbort(void)275 void showErrorAbort(void)
276 {
277 showError();
278 ebClose(99);
279 } /* showErrorAbort */
280
281 /* error exit check function */
eeCheck(void)282 void eeCheck(void)
283 {
284 if (errorExit)
285 ebClose(1);
286 }
287
288 /*********************************************************************
289 Now for the international version of caseShift.
290 This converts anything that might reasonably be a letter in your locale.
291 But it isn't ready for prime time.
292 I'd have to handle utf8 or not,
293 and then understand upper and lower case letters per language.
294 So this is commented out.
295 It was just a preliminary effort anyways, based on iso8859-1.
296 *********************************************************************/
297
298 #if 0
299
300 static const char upperMore[] = "";
301
302 static const char lowerMore[] = "";
303
304 static const char letterMore[] = "";
305
306 static bool i_isalphaByte(unsigned char c)
307 {
308 if (isalphaByte(c))
309 return true;
310 if (c == false)
311 return 0;
312 if (strchr(letterMore, c))
313 return true;
314 return false;
315 } /* i_isalphaByte */
316
317 /* assumes the arg is a letter */
318 static unsigned char i_tolower(unsigned char c)
319 {
320 char *s;
321 if (isalphaByte(c))
322 return tolower(c);
323 s = strchr(upperMore, c);
324 if (s)
325 c = lowerMore[s - upperMore];
326 return c;
327 } /* i_tolower */
328
329 static unsigned char i_toupper(unsigned char c)
330 {
331 char *s;
332 if (isalphaByte(c))
333 return toupper(c);
334 s = strchr(lowerMore, c);
335 if (s)
336 c = upperMore[s - lowerMore];
337 return c;
338 } /* i_toupper */
339
340 /* This is a variation on the original routine, found in stringfile.c */
341 void i_caseShift(unsigned char *s, char action)
342 {
343 unsigned char c;
344 /* The McDonalds conversion is very English - should we do it in all languages? */
345 int mc = 0;
346 bool ws = true;
347
348 for (; c = *s; ++s) {
349 if (action == 'u') {
350 if (i_isalphaByte(c))
351 *s = i_toupper(c);
352 continue;
353 }
354
355 if (action == 'l') {
356 if (i_isalphaByte(c))
357 *s = i_tolower(c);
358 continue;
359 }
360
361 /* mixed case left */
362 if (i_isalphaByte(c)) {
363 if (ws)
364 c = i_toupper(c);
365 else
366 c = i_tolower(c);
367 if (ws && c == 'M')
368 mc = 1;
369 else if (mc == 1 && c == 'c')
370 mc = 2;
371 else if (mc == 2) {
372 c = i_toupper(c);
373 mc = 0;
374 } else
375 mc = 0;
376 *s = c;
377 ws = false;
378 continue;
379 }
380
381 ws = true, mc = 0;
382 } /* loop */
383 } /* caseShift */
384
385 #endif
386
eb_puts(const char * s)387 void eb_puts(const char *s)
388 {
389 #ifdef DOSLIKE
390 wchar_t *chars = NULL;
391 DWORD written, mode;
392 HANDLE output_handle;
393 int needed;
394 output_handle = GetStdHandle(STD_OUTPUT_HANDLE);
395 if (GetConsoleMode(output_handle, &mode) == 0) {
396 puts(s);
397 return;
398 }
399 needed = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
400 if (needed == 0) {
401 return;
402 }
403 //Add space for the newline
404 chars = (wchar_t *) allocMem(sizeof(wchar_t) * (needed + 2));
405 MultiByteToWideChar(CP_UTF8, 0, s, -1, chars, needed);
406 chars[needed - 1] = L'\r';
407 chars[needed] = L'\n';
408 chars[needed + 1] = L'\0';
409 WriteConsoleW(output_handle, (void *)chars, needed + 1, &written, NULL);
410 free(chars);
411 #else
412 puts(s);
413 #endif
414
415 if (debugFile)
416 fprintf(debugFile, "%s\n", s);
417 } /* eb_puts */
418
eb_vprintf(const char * fmt,va_list args)419 static void eb_vprintf(const char *fmt, va_list args)
420 {
421 #ifdef DOSLIKE
422 wchar_t *chars = NULL;
423 DWORD written, mode;
424 HANDLE output_handle;
425 int needed;
426 char *a; // result of vasprintf
427 output_handle = GetStdHandle(STD_OUTPUT_HANDLE);
428 if (GetConsoleMode(output_handle, &mode) == 0) {
429 // this is better than doing nothing.
430 vprintf(fmt, args);
431 return;
432 }
433 if (vasprintf(&a, fmt, args) < 0)
434 return;
435 needed = MultiByteToWideChar(CP_UTF8, 0, a, -1, NULL, 0);
436 if (needed == 0) {
437 free(a);
438 return;
439 }
440 chars = (wchar_t *) allocMem(sizeof(wchar_t) * needed);
441 MultiByteToWideChar(CP_UTF8, 0, a, -1, chars, needed);
442 WriteConsoleW(output_handle, (void *)chars, needed - 1, &written, NULL);
443 free(chars);
444 free(a);
445 #else
446 vprintf(fmt, args);
447 #endif
448 } /* eb_vprintf */
449
helpUtility(void)450 bool helpUtility(void)
451 {
452 int cx;
453
454 if (!cxQuit(context, 0))
455 return false;
456
457 // maybe we already have a buffer with the help guide in it
458 for (cx = 1; cx < MAXSESSION; ++cx) {
459 struct ebWindow *w = sessionList[cx].lw;
460 if (!w)
461 continue;
462 if (!w->f0.fileName)
463 continue;
464 if (!stringEqual(w->f0.fileName, "qrg.browse"))
465 continue;
466 cxSwitch(cx, false);
467 i_printf(MSG_MovedSession, cx);
468 return true;
469 }
470
471 cx = sideBuffer(0, qrg_string, -1, "qrg");
472 if (cx == 0)
473 return false;
474 cxSwitch(cx, false);
475 i_printf(MSG_MovedSession, cx);
476 browseCurrentBuffer();
477 cw->dot = 1;
478 return true;
479 }
480