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