1 /*
2 * kmp_i18n.cpp
3 */
4
5 //===----------------------------------------------------------------------===//
6 //
7 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
8 // See https://llvm.org/LICENSE.txt for license information.
9 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "kmp_i18n.h"
14
15 #include "kmp.h"
16 #include "kmp_debug.h"
17 #include "kmp_io.h" // __kmp_printf.
18 #include "kmp_lock.h"
19 #include "kmp_os.h"
20
21 #include <errno.h>
22 #include <locale.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "kmp_environment.h"
28 #include "kmp_i18n_default.inc"
29 #include "kmp_str.h"
30
31 #undef KMP_I18N_OK
32
33 #define get_section(id) ((id) >> 16)
34 #define get_number(id) ((id)&0xFFFF)
35
36 kmp_msg_t __kmp_msg_null = {kmp_mt_dummy, 0, NULL, 0};
37 static char const *no_message_available = "(No message available)";
38
39 static void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message,
40 va_list ap);
41
42 enum kmp_i18n_cat_status {
43 KMP_I18N_CLOSED, // Not yet opened or closed.
44 KMP_I18N_OPENED, // Opened successfully, ready to use.
45 KMP_I18N_ABSENT // Opening failed, message catalog should not be used.
46 }; // enum kmp_i18n_cat_status
47 typedef enum kmp_i18n_cat_status kmp_i18n_cat_status_t;
48 static volatile kmp_i18n_cat_status_t status = KMP_I18N_CLOSED;
49
50 /* Message catalog is opened at first usage, so we have to synchronize opening
51 to avoid race and multiple openings.
52
53 Closing does not require synchronization, because catalog is closed very late
54 at library shutting down, when no other threads are alive. */
55
56 static void __kmp_i18n_do_catopen();
57 static kmp_bootstrap_lock_t lock = KMP_BOOTSTRAP_LOCK_INITIALIZER(lock);
58 // `lock' variable may be placed into __kmp_i18n_catopen function because it is
59 // used only by that function. But we afraid a (buggy) compiler may treat it
60 // wrongly. So we put it outside of function just in case.
61
__kmp_i18n_catopen()62 void __kmp_i18n_catopen() {
63 if (status == KMP_I18N_CLOSED) {
64 __kmp_acquire_bootstrap_lock(&lock);
65 if (status == KMP_I18N_CLOSED) {
66 __kmp_i18n_do_catopen();
67 }
68 __kmp_release_bootstrap_lock(&lock);
69 }
70 } // func __kmp_i18n_catopen
71
72 /* Linux* OS and OS X* part */
73 #if KMP_OS_UNIX
74 #define KMP_I18N_OK
75
76 #include <nl_types.h>
77
78 #define KMP_I18N_NULLCAT ((nl_catd)(-1))
79 static nl_catd cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?
80 static char const *name =
81 (KMP_VERSION_MAJOR == 4 ? "libguide.cat" : "libomp.cat");
82
83 /* Useful links:
84 http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html#tag_08_02
85 http://www.opengroup.org/onlinepubs/000095399/functions/catopen.html
86 http://www.opengroup.org/onlinepubs/000095399/functions/setlocale.html
87 */
88
__kmp_i18n_do_catopen()89 void __kmp_i18n_do_catopen() {
90 int english = 0;
91 char *lang = __kmp_env_get("LANG");
92 // TODO: What about LC_ALL or LC_MESSAGES?
93
94 KMP_DEBUG_ASSERT(status == KMP_I18N_CLOSED);
95 KMP_DEBUG_ASSERT(cat == KMP_I18N_NULLCAT);
96
97 english = lang == NULL || // In all these cases English language is used.
98 strcmp(lang, "") == 0 || strcmp(lang, " ") == 0 ||
99 // Workaround for Fortran RTL bug DPD200137873 "Fortran runtime
100 // resets LANG env var to space if it is not set".
101 strcmp(lang, "C") == 0 || strcmp(lang, "POSIX") == 0;
102
103 if (!english) { // English language is not yet detected, let us continue.
104 // Format of LANG is: [language[_territory][.codeset][@modifier]]
105 // Strip all parts except language.
106 char *tail = NULL;
107 __kmp_str_split(lang, '@', &lang, &tail);
108 __kmp_str_split(lang, '.', &lang, &tail);
109 __kmp_str_split(lang, '_', &lang, &tail);
110 english = (strcmp(lang, "en") == 0);
111 }
112
113 KMP_INTERNAL_FREE(lang);
114
115 // Do not try to open English catalog because internal messages are
116 // exact copy of messages in English catalog.
117 if (english) {
118 status = KMP_I18N_ABSENT; // mark catalog as absent so it will not
119 // be re-opened.
120 return;
121 }
122
123 cat = catopen(name, 0);
124 // TODO: Why do we pass 0 in flags?
125 status = (cat == KMP_I18N_NULLCAT ? KMP_I18N_ABSENT : KMP_I18N_OPENED);
126
127 if (status == KMP_I18N_ABSENT) {
128 if (__kmp_generate_warnings > kmp_warnings_low) {
129 // AC: only issue warning in case explicitly asked to
130 int error = errno; // Save errno immediately.
131 char *nlspath = __kmp_env_get("NLSPATH");
132 char *lang = __kmp_env_get("LANG");
133
134 // Infinite recursion will not occur -- status is KMP_I18N_ABSENT now, so
135 // __kmp_i18n_catgets() will not try to open catalog, but will return
136 // default message.
137 kmp_msg_t err_code = KMP_ERR(error);
138 __kmp_msg(kmp_ms_warning, KMP_MSG(CantOpenMessageCatalog, name), err_code,
139 KMP_HNT(CheckEnvVar, "NLSPATH", nlspath),
140 KMP_HNT(CheckEnvVar, "LANG", lang), __kmp_msg_null);
141 if (__kmp_generate_warnings == kmp_warnings_off) {
142 __kmp_str_free(&err_code.str);
143 }
144
145 KMP_INFORM(WillUseDefaultMessages);
146 KMP_INTERNAL_FREE(nlspath);
147 KMP_INTERNAL_FREE(lang);
148 }
149 } else { // status == KMP_I18N_OPENED
150 int section = get_section(kmp_i18n_prp_Version);
151 int number = get_number(kmp_i18n_prp_Version);
152 char const *expected = __kmp_i18n_default_table.sect[section].str[number];
153 // Expected version of the catalog.
154 kmp_str_buf_t version; // Actual version of the catalog.
155 __kmp_str_buf_init(&version);
156 __kmp_str_buf_print(&version, "%s", catgets(cat, section, number, NULL));
157
158 // String returned by catgets is invalid after closing catalog, so copy it.
159 if (strcmp(version.str, expected) != 0) {
160 __kmp_i18n_catclose(); // Close bad catalog.
161 status = KMP_I18N_ABSENT; // And mark it as absent.
162 if (__kmp_generate_warnings > kmp_warnings_low) {
163 // AC: only issue warning in case explicitly asked to
164 // And now print a warning using default messages.
165 char const *name = "NLSPATH";
166 char const *nlspath = __kmp_env_get(name);
167 __kmp_msg(kmp_ms_warning,
168 KMP_MSG(WrongMessageCatalog, name, version.str, expected),
169 KMP_HNT(CheckEnvVar, name, nlspath), __kmp_msg_null);
170 KMP_INFORM(WillUseDefaultMessages);
171 KMP_INTERNAL_FREE(CCAST(char *, nlspath));
172 } // __kmp_generate_warnings
173 }
174 __kmp_str_buf_free(&version);
175 }
176 } // func __kmp_i18n_do_catopen
177
__kmp_i18n_catclose()178 void __kmp_i18n_catclose() {
179 if (status == KMP_I18N_OPENED) {
180 KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
181 catclose(cat);
182 cat = KMP_I18N_NULLCAT;
183 }
184 status = KMP_I18N_CLOSED;
185 } // func __kmp_i18n_catclose
186
__kmp_i18n_catgets(kmp_i18n_id_t id)187 char const *__kmp_i18n_catgets(kmp_i18n_id_t id) {
188
189 int section = get_section(id);
190 int number = get_number(id);
191 char const *message = NULL;
192
193 if (1 <= section && section <= __kmp_i18n_default_table.size) {
194 if (1 <= number && number <= __kmp_i18n_default_table.sect[section].size) {
195 if (status == KMP_I18N_CLOSED) {
196 __kmp_i18n_catopen();
197 }
198 if (status == KMP_I18N_OPENED) {
199 message = catgets(cat, section, number,
200 __kmp_i18n_default_table.sect[section].str[number]);
201 }
202 if (message == NULL) {
203 message = __kmp_i18n_default_table.sect[section].str[number];
204 }
205 }
206 }
207 if (message == NULL) {
208 message = no_message_available;
209 }
210 return message;
211
212 } // func __kmp_i18n_catgets
213
214 #endif // KMP_OS_UNIX
215
216 /* Windows* OS part. */
217
218 #if KMP_OS_WINDOWS
219 #define KMP_I18N_OK
220
221 #include "kmp_environment.h"
222 #include <windows.h>
223
224 #define KMP_I18N_NULLCAT NULL
225 static HMODULE cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?
226 static char const *name =
227 (KMP_VERSION_MAJOR == 4 ? "libguide40ui.dll" : "libompui.dll");
228
229 static kmp_i18n_table_t table = {0, NULL};
230 // Messages formatted by FormatMessage() should be freed, but catgets()
231 // interface assumes user will not free messages. So we cache all the retrieved
232 // messages in the table, which are freed at catclose().
233 static UINT const default_code_page = CP_OEMCP;
234 static UINT code_page = default_code_page;
235
236 static char const *___catgets(kmp_i18n_id_t id);
237 static UINT get_code_page();
238 static void kmp_i18n_table_free(kmp_i18n_table_t *table);
239
get_code_page()240 static UINT get_code_page() {
241
242 UINT cp = default_code_page;
243 char const *value = __kmp_env_get("KMP_CODEPAGE");
244 if (value != NULL) {
245 if (_stricmp(value, "ANSI") == 0) {
246 cp = CP_ACP;
247 } else if (_stricmp(value, "OEM") == 0) {
248 cp = CP_OEMCP;
249 } else if (_stricmp(value, "UTF-8") == 0 || _stricmp(value, "UTF8") == 0) {
250 cp = CP_UTF8;
251 } else if (_stricmp(value, "UTF-7") == 0 || _stricmp(value, "UTF7") == 0) {
252 cp = CP_UTF7;
253 } else {
254 // !!! TODO: Issue a warning?
255 }
256 }
257 KMP_INTERNAL_FREE((void *)value);
258 return cp;
259
260 } // func get_code_page
261
kmp_i18n_table_free(kmp_i18n_table_t * table)262 static void kmp_i18n_table_free(kmp_i18n_table_t *table) {
263 int s;
264 int m;
265 for (s = 0; s < table->size; ++s) {
266 for (m = 0; m < table->sect[s].size; ++m) {
267 // Free message.
268 KMP_INTERNAL_FREE((void *)table->sect[s].str[m]);
269 table->sect[s].str[m] = NULL;
270 }
271 table->sect[s].size = 0;
272 // Free section itself.
273 KMP_INTERNAL_FREE((void *)table->sect[s].str);
274 table->sect[s].str = NULL;
275 }
276 table->size = 0;
277 KMP_INTERNAL_FREE((void *)table->sect);
278 table->sect = NULL;
279 } // kmp_i18n_table_free
280
__kmp_i18n_do_catopen()281 void __kmp_i18n_do_catopen() {
282
283 LCID locale_id = GetThreadLocale();
284 WORD lang_id = LANGIDFROMLCID(locale_id);
285 WORD primary_lang_id = PRIMARYLANGID(lang_id);
286 kmp_str_buf_t path;
287
288 KMP_DEBUG_ASSERT(status == KMP_I18N_CLOSED);
289 KMP_DEBUG_ASSERT(cat == KMP_I18N_NULLCAT);
290
291 __kmp_str_buf_init(&path);
292
293 // Do not try to open English catalog because internal messages are exact copy
294 // of messages in English catalog.
295 if (primary_lang_id == LANG_ENGLISH) {
296 status = KMP_I18N_ABSENT; // mark catalog as absent so it will not
297 // be re-opened.
298 goto end;
299 }
300
301 // Construct resource DLL name.
302 /* Simple LoadLibrary( name ) is not suitable due to security issue (see
303 http://www.microsoft.com/technet/security/advisory/2269637.mspx). We have
304 to specify full path to the message catalog. */
305 {
306 // Get handle of our DLL first.
307 HMODULE handle;
308 BOOL brc = GetModuleHandleEx(
309 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
310 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
311 reinterpret_cast<LPCSTR>(&__kmp_i18n_do_catopen), &handle);
312 if (!brc) { // Error occurred.
313 status = KMP_I18N_ABSENT; // mark catalog as absent so it will not be
314 // re-opened.
315 goto end;
316 // TODO: Enable multiple messages (KMP_MSG) to be passed to __kmp_msg; and
317 // print a proper warning.
318 }
319
320 // Now get path to the our DLL.
321 for (;;) {
322 DWORD drc = GetModuleFileName(handle, path.str, path.size);
323 if (drc == 0) { // Error occurred.
324 status = KMP_I18N_ABSENT;
325 goto end;
326 }
327 if (drc < path.size) {
328 path.used = drc;
329 break;
330 }
331 __kmp_str_buf_reserve(&path, path.size * 2);
332 }
333
334 // Now construct the name of message catalog.
335 kmp_str_fname fname;
336 __kmp_str_fname_init(&fname, path.str);
337 __kmp_str_buf_clear(&path);
338 __kmp_str_buf_print(&path, "%s%lu/%s", fname.dir,
339 (unsigned long)(locale_id), name);
340 __kmp_str_fname_free(&fname);
341 }
342
343 // For security reasons, use LoadLibraryEx() and load message catalog as a
344 // data file.
345 cat = LoadLibraryEx(path.str, NULL, LOAD_LIBRARY_AS_DATAFILE);
346 status = (cat == KMP_I18N_NULLCAT ? KMP_I18N_ABSENT : KMP_I18N_OPENED);
347
348 if (status == KMP_I18N_ABSENT) {
349 if (__kmp_generate_warnings > kmp_warnings_low) {
350 // AC: only issue warning in case explicitly asked to
351 DWORD error = GetLastError();
352 // Infinite recursion will not occur -- status is KMP_I18N_ABSENT now, so
353 // __kmp_i18n_catgets() will not try to open catalog but will return
354 // default message.
355 /* If message catalog for another architecture found (e.g. OpenMP RTL for
356 IA-32 architecture opens libompui.dll for Intel(R) 64) Windows* OS
357 returns error 193 (ERROR_BAD_EXE_FORMAT). However, FormatMessage fails
358 to return a message for this error, so user will see:
359
360 OMP: Warning #2: Cannot open message catalog "1041\libompui.dll":
361 OMP: System error #193: (No system error message available)
362 OMP: Info #3: Default messages will be used.
363
364 Issue hint in this case so cause of trouble is more understandable. */
365 kmp_msg_t err_code = KMP_SYSERRCODE(error);
366 __kmp_msg(kmp_ms_warning, KMP_MSG(CantOpenMessageCatalog, path.str),
367 err_code, (error == ERROR_BAD_EXE_FORMAT
368 ? KMP_HNT(BadExeFormat, path.str, KMP_ARCH_STR)
369 : __kmp_msg_null),
370 __kmp_msg_null);
371 if (__kmp_generate_warnings == kmp_warnings_off) {
372 __kmp_str_free(&err_code.str);
373 }
374 KMP_INFORM(WillUseDefaultMessages);
375 }
376 } else { // status == KMP_I18N_OPENED
377
378 int section = get_section(kmp_i18n_prp_Version);
379 int number = get_number(kmp_i18n_prp_Version);
380 char const *expected = __kmp_i18n_default_table.sect[section].str[number];
381 kmp_str_buf_t version; // Actual version of the catalog.
382 __kmp_str_buf_init(&version);
383 __kmp_str_buf_print(&version, "%s", ___catgets(kmp_i18n_prp_Version));
384 // String returned by catgets is invalid after closing catalog, so copy it.
385 if (strcmp(version.str, expected) != 0) {
386 // Close bad catalog.
387 __kmp_i18n_catclose();
388 status = KMP_I18N_ABSENT; // And mark it as absent.
389 if (__kmp_generate_warnings > kmp_warnings_low) {
390 // And now print a warning using default messages.
391 __kmp_msg(kmp_ms_warning,
392 KMP_MSG(WrongMessageCatalog, path.str, version.str, expected),
393 __kmp_msg_null);
394 KMP_INFORM(WillUseDefaultMessages);
395 } // __kmp_generate_warnings
396 }
397 __kmp_str_buf_free(&version);
398 }
399 code_page = get_code_page();
400
401 end:
402 __kmp_str_buf_free(&path);
403 return;
404 } // func __kmp_i18n_do_catopen
405
__kmp_i18n_catclose()406 void __kmp_i18n_catclose() {
407 if (status == KMP_I18N_OPENED) {
408 KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
409 kmp_i18n_table_free(&table);
410 FreeLibrary(cat);
411 cat = KMP_I18N_NULLCAT;
412 }
413 code_page = default_code_page;
414 status = KMP_I18N_CLOSED;
415 } // func __kmp_i18n_catclose
416
417 /* We use FormatMessage() to get strings from catalog, get system error
418 messages, etc. FormatMessage() tends to return Windows* OS-style
419 end-of-lines, "\r\n". When string is printed, printf() also replaces all the
420 occurrences of "\n" with "\r\n" (again!), so sequences like "\r\r\r\n"
421 appear in output. It is not too good.
422
423 Additional mess comes from message catalog: Our catalog source en_US.mc file
424 (generated by message-converter.pl) contains only "\n" characters, but
425 en_US_msg_1033.bin file (produced by mc.exe) may contain "\r\n" or just "\n".
426 This mess goes from en_US_msg_1033.bin file to message catalog,
427 libompui.dll. For example, message
428
429 Error
430
431 (there is "\n" at the end) is compiled by mc.exe to "Error\r\n", while
432
433 OMP: Error %1!d!: %2!s!\n
434
435 (there is "\n" at the end as well) is compiled to "OMP: Error %1!d!:
436 %2!s!\r\n\n".
437
438 Thus, stripping all "\r" normalizes string and returns it to canonical form,
439 so printf() will produce correct end-of-line sequences.
440
441 ___strip_crs() serves for this purpose: it removes all the occurrences of
442 "\r" in-place and returns new length of string. */
___strip_crs(char * str)443 static int ___strip_crs(char *str) {
444 int in = 0; // Input character index.
445 int out = 0; // Output character index.
446 for (;;) {
447 if (str[in] != '\r') {
448 str[out] = str[in];
449 ++out;
450 }
451 if (str[in] == 0) {
452 break;
453 }
454 ++in;
455 }
456 return out - 1;
457 } // func __strip_crs
458
___catgets(kmp_i18n_id_t id)459 static char const *___catgets(kmp_i18n_id_t id) {
460
461 char *result = NULL;
462 PVOID addr = NULL;
463 wchar_t *wmsg = NULL;
464 DWORD wlen = 0;
465 char *msg = NULL;
466 int len = 0;
467 int rc;
468
469 KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
470 wlen = // wlen does *not* include terminating null.
471 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
472 FORMAT_MESSAGE_FROM_HMODULE |
473 FORMAT_MESSAGE_IGNORE_INSERTS,
474 cat, id,
475 0, // LangId
476 (LPWSTR)&addr,
477 0, // Size in elements, not in bytes.
478 NULL);
479 if (wlen <= 0) {
480 goto end;
481 }
482 wmsg = (wchar_t *)addr; // Warning: wmsg may be not nul-terminated!
483
484 // Calculate length of multibyte message.
485 // Since wlen does not include terminating null, len does not include it also.
486 len = WideCharToMultiByte(code_page,
487 0, // Flags.
488 wmsg, wlen, // Wide buffer and size.
489 NULL, 0, // Buffer and size.
490 NULL, NULL // Default char and used default char.
491 );
492 if (len <= 0) {
493 goto end;
494 }
495
496 // Allocate memory.
497 msg = (char *)KMP_INTERNAL_MALLOC(len + 1);
498
499 // Convert wide message to multibyte one.
500 rc = WideCharToMultiByte(code_page,
501 0, // Flags.
502 wmsg, wlen, // Wide buffer and size.
503 msg, len, // Buffer and size.
504 NULL, NULL // Default char and used default char.
505 );
506 if (rc <= 0 || rc > len) {
507 goto end;
508 }
509 KMP_DEBUG_ASSERT(rc == len);
510 len = rc;
511 msg[len] = 0; // Put terminating null to the end.
512
513 // Stripping all "\r" before stripping last end-of-line simplifies the task.
514 len = ___strip_crs(msg);
515
516 // Every message in catalog is terminated with "\n". Strip it.
517 if (len >= 1 && msg[len - 1] == '\n') {
518 --len;
519 msg[len] = 0;
520 }
521
522 // Everything looks ok.
523 result = msg;
524 msg = NULL;
525
526 end:
527
528 if (msg != NULL) {
529 KMP_INTERNAL_FREE(msg);
530 }
531 if (wmsg != NULL) {
532 LocalFree(wmsg);
533 }
534
535 return result;
536
537 } // ___catgets
538
__kmp_i18n_catgets(kmp_i18n_id_t id)539 char const *__kmp_i18n_catgets(kmp_i18n_id_t id) {
540
541 int section = get_section(id);
542 int number = get_number(id);
543 char const *message = NULL;
544
545 if (1 <= section && section <= __kmp_i18n_default_table.size) {
546 if (1 <= number && number <= __kmp_i18n_default_table.sect[section].size) {
547 if (status == KMP_I18N_CLOSED) {
548 __kmp_i18n_catopen();
549 }
550 if (cat != KMP_I18N_NULLCAT) {
551 if (table.size == 0) {
552 table.sect = (kmp_i18n_section_t *)KMP_INTERNAL_CALLOC(
553 (__kmp_i18n_default_table.size + 2), sizeof(kmp_i18n_section_t));
554 table.size = __kmp_i18n_default_table.size;
555 }
556 if (table.sect[section].size == 0) {
557 table.sect[section].str = (const char **)KMP_INTERNAL_CALLOC(
558 __kmp_i18n_default_table.sect[section].size + 2,
559 sizeof(char const *));
560 table.sect[section].size =
561 __kmp_i18n_default_table.sect[section].size;
562 }
563 if (table.sect[section].str[number] == NULL) {
564 table.sect[section].str[number] = ___catgets(id);
565 }
566 message = table.sect[section].str[number];
567 }
568 if (message == NULL) {
569 // Catalog is not opened or message is not found, return default
570 // message.
571 message = __kmp_i18n_default_table.sect[section].str[number];
572 }
573 }
574 }
575 if (message == NULL) {
576 message = no_message_available;
577 }
578 return message;
579
580 } // func __kmp_i18n_catgets
581
582 #endif // KMP_OS_WINDOWS
583
584 // -----------------------------------------------------------------------------
585
586 #ifndef KMP_I18N_OK
587 #error I18n support is not implemented for this OS.
588 #endif // KMP_I18N_OK
589
590 // -----------------------------------------------------------------------------
591
__kmp_i18n_dump_catalog(kmp_str_buf_t * buffer)592 void __kmp_i18n_dump_catalog(kmp_str_buf_t *buffer) {
593
594 struct kmp_i18n_id_range_t {
595 kmp_i18n_id_t first;
596 kmp_i18n_id_t last;
597 }; // struct kmp_i18n_id_range_t
598
599 static struct kmp_i18n_id_range_t ranges[] = {
600 {kmp_i18n_prp_first, kmp_i18n_prp_last},
601 {kmp_i18n_str_first, kmp_i18n_str_last},
602 {kmp_i18n_fmt_first, kmp_i18n_fmt_last},
603 {kmp_i18n_msg_first, kmp_i18n_msg_last},
604 {kmp_i18n_hnt_first, kmp_i18n_hnt_last}}; // ranges
605
606 int num_of_ranges = sizeof(ranges) / sizeof(struct kmp_i18n_id_range_t);
607 int range;
608 kmp_i18n_id_t id;
609
610 for (range = 0; range < num_of_ranges; ++range) {
611 __kmp_str_buf_print(buffer, "*** Set #%d ***\n", range + 1);
612 for (id = (kmp_i18n_id_t)(ranges[range].first + 1); id < ranges[range].last;
613 id = (kmp_i18n_id_t)(id + 1)) {
614 __kmp_str_buf_print(buffer, "%d: <<%s>>\n", id, __kmp_i18n_catgets(id));
615 }
616 }
617
618 __kmp_printf("%s", buffer->str);
619
620 } // __kmp_i18n_dump_catalog
621
622 // -----------------------------------------------------------------------------
__kmp_msg_format(unsigned id_arg,...)623 kmp_msg_t __kmp_msg_format(unsigned id_arg, ...) {
624
625 kmp_msg_t msg;
626 va_list args;
627 kmp_str_buf_t buffer;
628 __kmp_str_buf_init(&buffer);
629
630 va_start(args, id_arg);
631
632 // We use unsigned for the ID argument and explicitly cast it here to the
633 // right enumerator because variadic functions are not compatible with
634 // default promotions.
635 kmp_i18n_id_t id = (kmp_i18n_id_t)id_arg;
636
637 #if KMP_OS_UNIX
638 // On Linux* OS and OS X*, printf() family functions process parameter
639 // numbers, for example: "%2$s %1$s".
640 __kmp_str_buf_vprint(&buffer, __kmp_i18n_catgets(id), args);
641 #elif KMP_OS_WINDOWS
642 // On Windows, printf() family functions does not recognize GNU style
643 // parameter numbers, so we have to use FormatMessage() instead. It recognizes
644 // parameter numbers, e. g.: "%2!s! "%1!s!".
645 {
646 LPTSTR str = NULL;
647 int len;
648 FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
649 __kmp_i18n_catgets(id), 0, 0, (LPTSTR)(&str), 0, &args);
650 len = ___strip_crs(str);
651 __kmp_str_buf_cat(&buffer, str, len);
652 LocalFree(str);
653 }
654 #else
655 #error
656 #endif
657 va_end(args);
658 __kmp_str_buf_detach(&buffer);
659
660 msg.type = (kmp_msg_type_t)(id >> 16);
661 msg.num = id & 0xFFFF;
662 msg.str = buffer.str;
663 msg.len = buffer.used;
664
665 return msg;
666
667 } // __kmp_msg_format
668
669 // -----------------------------------------------------------------------------
sys_error(int err)670 static char *sys_error(int err) {
671
672 char *message = NULL;
673
674 #if KMP_OS_WINDOWS
675
676 LPVOID buffer = NULL;
677 int len;
678 DWORD rc;
679 rc = FormatMessage(
680 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
681 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
682 (LPTSTR)&buffer, 0, NULL);
683 if (rc > 0) {
684 // Message formatted. Copy it (so we can free it later with normal free().
685 message = __kmp_str_format("%s", (char *)buffer);
686 len = ___strip_crs(message); // Delete carriage returns if any.
687 // Strip trailing newlines.
688 while (len > 0 && message[len - 1] == '\n') {
689 --len;
690 }
691 message[len] = 0;
692 } else {
693 // FormatMessage() failed to format system error message. GetLastError()
694 // would give us error code, which we would convert to message... this it
695 // dangerous recursion, which cannot clarify original error, so we will not
696 // even start it.
697 }
698 if (buffer != NULL) {
699 LocalFree(buffer);
700 }
701
702 #else // Non-Windows* OS: Linux* OS or OS X*
703
704 /* There are 2 incompatible versions of strerror_r:
705
706 char * strerror_r( int, char *, size_t ); // GNU version
707 int strerror_r( int, char *, size_t ); // XSI version
708 */
709
710 #if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || \
711 (defined(__BIONIC__) && defined(_GNU_SOURCE) && \
712 __ANDROID_API__ >= __ANDROID_API_M__)
713 // GNU version of strerror_r.
714
715 char buffer[2048];
716 char *const err_msg = strerror_r(err, buffer, sizeof(buffer));
717 // Do not eliminate this assignment to temporary variable, otherwise compiler
718 // would not issue warning if strerror_r() returns `int' instead of expected
719 // `char *'.
720 message = __kmp_str_format("%s", err_msg);
721
722 #else // OS X*, FreeBSD* etc.
723 // XSI version of strerror_r.
724 int size = 2048;
725 char *buffer = (char *)KMP_INTERNAL_MALLOC(size);
726 int rc;
727 if (buffer == NULL) {
728 KMP_FATAL(MemoryAllocFailed);
729 }
730 rc = strerror_r(err, buffer, size);
731 if (rc == -1) {
732 rc = errno; // XSI version sets errno.
733 }
734 while (rc == ERANGE) { // ERANGE means the buffer is too small.
735 KMP_INTERNAL_FREE(buffer);
736 size *= 2;
737 buffer = (char *)KMP_INTERNAL_MALLOC(size);
738 if (buffer == NULL) {
739 KMP_FATAL(MemoryAllocFailed);
740 }
741 rc = strerror_r(err, buffer, size);
742 if (rc == -1) {
743 rc = errno; // XSI version sets errno.
744 }
745 }
746 if (rc == 0) {
747 message = buffer;
748 } else { // Buffer is unused. Free it.
749 KMP_INTERNAL_FREE(buffer);
750 }
751
752 #endif
753
754 #endif /* KMP_OS_WINDOWS */
755
756 if (message == NULL) {
757 // TODO: I18n this message.
758 message = __kmp_str_format("%s", "(No system error message available)");
759 }
760 return message;
761 } // sys_error
762
763 // -----------------------------------------------------------------------------
__kmp_msg_error_code(int code)764 kmp_msg_t __kmp_msg_error_code(int code) {
765
766 kmp_msg_t msg;
767 msg.type = kmp_mt_syserr;
768 msg.num = code;
769 msg.str = sys_error(code);
770 msg.len = KMP_STRLEN(msg.str);
771 return msg;
772
773 } // __kmp_msg_error_code
774
775 // -----------------------------------------------------------------------------
__kmp_msg_error_mesg(char const * mesg)776 kmp_msg_t __kmp_msg_error_mesg(char const *mesg) {
777
778 kmp_msg_t msg;
779 msg.type = kmp_mt_syserr;
780 msg.num = 0;
781 msg.str = __kmp_str_format("%s", mesg);
782 msg.len = KMP_STRLEN(msg.str);
783 return msg;
784
785 } // __kmp_msg_error_mesg
786
787 // -----------------------------------------------------------------------------
__kmp_msg(kmp_msg_severity_t severity,kmp_msg_t message,va_list args)788 void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, va_list args) {
789 kmp_i18n_id_t format; // format identifier
790 kmp_msg_t fmsg; // formatted message
791 kmp_str_buf_t buffer;
792
793 if (severity != kmp_ms_fatal && __kmp_generate_warnings == kmp_warnings_off)
794 return; // no reason to form a string in order to not print it
795
796 __kmp_str_buf_init(&buffer);
797
798 // Format the primary message.
799 switch (severity) {
800 case kmp_ms_inform: {
801 format = kmp_i18n_fmt_Info;
802 } break;
803 case kmp_ms_warning: {
804 format = kmp_i18n_fmt_Warning;
805 } break;
806 case kmp_ms_fatal: {
807 format = kmp_i18n_fmt_Fatal;
808 } break;
809 default: { KMP_DEBUG_ASSERT(0); }
810 }
811 fmsg = __kmp_msg_format(format, message.num, message.str);
812 __kmp_str_free(&message.str);
813 __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len);
814 __kmp_str_free(&fmsg.str);
815
816 // Format other messages.
817 for (;;) {
818 message = va_arg(args, kmp_msg_t);
819 if (message.type == kmp_mt_dummy && message.str == NULL) {
820 break;
821 }
822 switch (message.type) {
823 case kmp_mt_hint: {
824 format = kmp_i18n_fmt_Hint;
825 // we cannot skip %1$ and only use %2$ to print the message without the
826 // number
827 fmsg = __kmp_msg_format(format, message.str);
828 } break;
829 case kmp_mt_syserr: {
830 format = kmp_i18n_fmt_SysErr;
831 fmsg = __kmp_msg_format(format, message.num, message.str);
832 } break;
833 default: { KMP_DEBUG_ASSERT(0); }
834 }
835 __kmp_str_free(&message.str);
836 __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len);
837 __kmp_str_free(&fmsg.str);
838 }
839
840 // Print formatted messages.
841 // This lock prevents multiple fatal errors on the same problem.
842 // __kmp_acquire_bootstrap_lock( & lock ); // GEH - This lock causing tests
843 // to hang on OS X*.
844 __kmp_printf("%s", buffer.str);
845 __kmp_str_buf_free(&buffer);
846
847 // __kmp_release_bootstrap_lock( & lock ); // GEH - this lock causing tests
848 // to hang on OS X*.
849
850 } // __kmp_msg
851
__kmp_msg(kmp_msg_severity_t severity,kmp_msg_t message,...)852 void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, ...) {
853 va_list args;
854 va_start(args, message);
855 __kmp_msg(severity, message, args);
856 va_end(args);
857 }
858
__kmp_fatal(kmp_msg_t message,...)859 void __kmp_fatal(kmp_msg_t message, ...) {
860 va_list args;
861 va_start(args, message);
862 __kmp_msg(kmp_ms_fatal, message, args);
863 va_end(args);
864 #if KMP_OS_WINDOWS
865 // Delay to give message a chance to appear before reaping
866 __kmp_thread_sleep(500);
867 #endif
868 __kmp_abort_process();
869 } // __kmp_fatal
870
871 // end of file //
872