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