blob: 8dad2553b008221545a62d3838201babb83541ae [file] [log] [blame]
Jim Cownie5e8470a2013-09-27 10:38:44 +00001/*
2 * kmp_i18n.c
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 =
112 lang == NULL || // In all these cases English language is used.
113 strcmp( lang, "" ) == 0 ||
114 strcmp( lang, " " ) == 0 ||
115 // 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;
119
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 ) {
135 status = KMP_I18N_ABSENT; // mark catalog as absent so it will not be re-opened.
136 return;
137 }
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 ) {
144 if (__kmp_generate_warnings > kmp_warnings_low) { // AC: only issue warning in case explicitly asked to
Jonathan Peyton1406f012015-05-22 22:35:51 +0000145 int error = errno; // Save errno immediately.
Jim Cownie5e8470a2013-09-27 10:38:44 +0000146 char * nlspath = __kmp_env_get( "NLSPATH" );
147 char * lang = __kmp_env_get( "LANG" );
148
149 // 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(
152 kmp_ms_warning,
153 KMP_MSG( CantOpenMessageCatalog, name ),
154 KMP_ERR( error ),
155 KMP_HNT( CheckEnvVar, "NLSPATH", nlspath ),
156 KMP_HNT( CheckEnvVar, "LANG", lang ),
157 __kmp_msg_null
158 );
159 KMP_INFORM( WillUseDefaultMessages );
160 KMP_INTERNAL_FREE( nlspath );
161 KMP_INTERNAL_FREE( lang );
162 }
163 } else { // status == KMP_I18N_OPENED
164
165 int section = get_section( kmp_i18n_prp_Version );
166 int number = get_number( kmp_i18n_prp_Version );
167 char const * expected = __kmp_i18n_default_table.sect[ section ].str[ number ];
168 // Expected version of the catalog.
169 kmp_str_buf_t version; // Actual version of the catalog.
170 __kmp_str_buf_init( & version );
171 __kmp_str_buf_print( & version, "%s", catgets( cat, section, number, NULL ) );
172
173 // String returned by catgets is invalid after closing the catalog, so copy it.
174 if ( strcmp( version.str, expected ) != 0 ) {
175 __kmp_i18n_catclose(); // Close bad catalog.
176 status = KMP_I18N_ABSENT; // And mark it as absent.
177 if (__kmp_generate_warnings > kmp_warnings_low) { // AC: only issue warning in case explicitly asked to
178 // And now print a warning using default messages.
179 char const * name = "NLSPATH";
180 char const * nlspath = __kmp_env_get( name );
181 __kmp_msg(
182 kmp_ms_warning,
183 KMP_MSG( WrongMessageCatalog, name, version.str, expected ),
184 KMP_HNT( CheckEnvVar, name, nlspath ),
185 __kmp_msg_null
186 );
187 KMP_INFORM( WillUseDefaultMessages );
188 KMP_INTERNAL_FREE( (void *) nlspath );
189 } // __kmp_generate_warnings
190 }; // if
191 __kmp_str_buf_free( & version );
192
193 }; // if
194
195} // func __kmp_i18n_do_catopen
196
197
198void
199__kmp_i18n_catclose(
200) {
201 if ( status == KMP_I18N_OPENED ) {
202 KMP_DEBUG_ASSERT( cat != KMP_I18N_NULLCAT );
203 catclose( cat );
204 cat = KMP_I18N_NULLCAT;
205 }; // if
206 status = KMP_I18N_CLOSED;
207} // func __kmp_i18n_catclose
208
209
210char const *
211__kmp_i18n_catgets(
212 kmp_i18n_id_t id
213) {
214
215 int section = get_section( id );
216 int number = get_number( id );
217 char const * message = NULL;
218
219 if ( 1 <= section && section <= __kmp_i18n_default_table.size ) {
220 if ( 1 <= number && number <= __kmp_i18n_default_table.sect[ section ].size ) {
221 if ( status == KMP_I18N_CLOSED ) {
222 __kmp_i18n_catopen();
223 }; // if
224 if ( status == KMP_I18N_OPENED ) {
225 message =
226 catgets(
227 cat,
228 section, number,
229 __kmp_i18n_default_table.sect[ section ].str[ number ]
230 );
231 }; // if
232 if ( message == NULL ) {
233 message = __kmp_i18n_default_table.sect[ section ].str[ number ];
234 }; // if
235 }; // if
236 }; // if
237 if ( message == NULL ) {
238 message = no_message_available;
239 }; // if
240 return message;
241
242} // func __kmp_i18n_catgets
243
244
245#endif // KMP_OS_UNIX
246
247/*
248 ================================================================================================
249 Windows* OS part.
250 ================================================================================================
251*/
252
253#if KMP_OS_WINDOWS
254#define KMP_I18N_OK
255
256#include "kmp_environment.h"
257#include <windows.h>
258
259#define KMP_I18N_NULLCAT NULL
260static HMODULE cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?
Jonathan Peyton66338292015-06-01 02:37:28 +0000261static char const * name = ( KMP_VERSION_MAJOR == 4 ? "libguide40ui.dll" : "libompui.dll" );
Jim Cownie5e8470a2013-09-27 10:38:44 +0000262
263static kmp_i18n_table_t table = { 0, NULL };
264 // Messages formatted by FormatMessage() should be freed, but catgets() interface assumes
265 // user will not free messages. So we cache all the retrieved messages in the table, which
266 // are freed at catclose().
267static UINT const default_code_page = CP_OEMCP;
268static UINT code_page = default_code_page;
269
270static char const * ___catgets( kmp_i18n_id_t id );
271static UINT get_code_page();
272static void kmp_i18n_table_free( kmp_i18n_table_t * table );
273
274
275static UINT
276get_code_page(
277) {
278
279 UINT cp = default_code_page;
280 char const * value = __kmp_env_get( "KMP_CODEPAGE" );
281 if ( value != NULL ) {
282 if ( _stricmp( value, "ANSI" ) == 0 ) {
283 cp = CP_ACP;
284 } else if ( _stricmp( value, "OEM" ) == 0 ) {
285 cp = CP_OEMCP;
286 } else if ( _stricmp( value, "UTF-8" ) == 0 || _stricmp( value, "UTF8" ) == 0 ) {
287 cp = CP_UTF8;
288 } else if ( _stricmp( value, "UTF-7" ) == 0 || _stricmp( value, "UTF7" ) == 0 ) {
289 cp = CP_UTF7;
290 } else {
291 // !!! TODO: Issue a warning?
292 }; // if
293 }; // if
294 KMP_INTERNAL_FREE( (void *) value );
295 return cp;
296
297} // func get_code_page
298
299
300static void
301kmp_i18n_table_free(
302 kmp_i18n_table_t * table
303) {
304 int s;
305 int m;
306 for ( s = 0; s < table->size; ++ s ) {
307 for ( m = 0; m < table->sect[ s ].size; ++ m ) {
308 // Free message.
309 KMP_INTERNAL_FREE( (void *) table->sect[ s ].str[ m ] );
310 table->sect[ s ].str[ m ] = NULL;
311 }; // for m
312 table->sect[ s ].size = 0;
313 // Free section itself.
314 KMP_INTERNAL_FREE ( (void *) table->sect[ s ].str );
315 table->sect[ s ].str = NULL;
316 }; // for s
317 table->size = 0;
318 KMP_INTERNAL_FREE( (void *) table->sect );
319 table->sect = NULL;
320} // kmp_i8n_table_free
321
322
323void
324__kmp_i18n_do_catopen(
325) {
326
327 LCID locale_id = GetThreadLocale();
328 WORD lang_id = LANGIDFROMLCID( locale_id );
329 WORD primary_lang_id = PRIMARYLANGID( lang_id );
330 kmp_str_buf_t path;
331
332 KMP_DEBUG_ASSERT( status == KMP_I18N_CLOSED );
333 KMP_DEBUG_ASSERT( cat == KMP_I18N_NULLCAT );
334
335 __kmp_str_buf_init( & path );
336
337 // Do not try to open English catalog because internal messages are
338 // exact copy of messages in English catalog.
339 if ( primary_lang_id == LANG_ENGLISH ) {
340 status = KMP_I18N_ABSENT; // mark catalog as absent so it will not be re-opened.
341 goto end;
342 }; // if
343
344 // Construct resource DLL name.
345 /*
346 Simple
347 LoadLibrary( name )
348 is not suitable due to security issue (see
349 http://www.microsoft.com/technet/security/advisory/2269637.mspx). We have to specify full
350 path to the message catalog.
351 */
352 {
353
354 // Get handle of our DLL first.
355 HMODULE handle;
356 BOOL brc =
357 GetModuleHandleEx(
358 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
359 reinterpret_cast< LPCSTR >( & __kmp_i18n_do_catopen ),
360 & handle
361 );
Alp Toker8f2d3f02014-02-24 10:40:15 +0000362 if ( ! brc ) { // Error occurred.
Jim Cownie5e8470a2013-09-27 10:38:44 +0000363 status = KMP_I18N_ABSENT; // mark catalog as absent so it will not be re-opened.
364 goto end;
365 // TODO: Enable multiple messages (KMP_MSG) to be passed to __kmp_msg; and print
366 // a proper warning.
367 }; // if
368
369 // Now get path to the our DLL.
370 for ( ; ; ) {
371 DWORD drc = GetModuleFileName( handle, path.str, path.size );
Alp Toker8f2d3f02014-02-24 10:40:15 +0000372 if ( drc == 0 ) { // Error occurred.
Jim Cownie5e8470a2013-09-27 10:38:44 +0000373 status = KMP_I18N_ABSENT;
374 goto end;
375 }; // if
376 if ( drc < path.size ) {
377 path.used = drc;
378 break;
379 }; // if
380 __kmp_str_buf_reserve( & path, path.size * 2 );
381 }; // forever
382
383 // Now construct the name of message catalog.
384 kmp_str_fname fname;
385 __kmp_str_fname_init( & fname, path.str );
386 __kmp_str_buf_clear( & path );
387 __kmp_str_buf_print( & path, "%s%lu/%s", fname.dir, (unsigned long)( locale_id ), name );
388 __kmp_str_fname_free( & fname );
389
390 }
391
392 // For security reasons, use LoadLibraryEx() and load message catalog as a data file.
393 cat = LoadLibraryEx( path.str, NULL, LOAD_LIBRARY_AS_DATAFILE );
394 status = ( cat == KMP_I18N_NULLCAT ? KMP_I18N_ABSENT : KMP_I18N_OPENED );
395
396 if ( status == KMP_I18N_ABSENT ) {
397 if (__kmp_generate_warnings > kmp_warnings_low) { // AC: only issue warning in case explicitly asked to
398 DWORD error = GetLastError();
399 // Infinite recursion will not occur -- status is KMP_I18N_ABSENT now, so
400 // __kmp_i18n_catgets() will not try to open catalog but will return default message.
401 /*
402 If message catalog for another architecture found (e.g. OpenMP RTL
Jonathan Peyton66338292015-06-01 02:37:28 +0000403 for IA-32 architecture opens libompui.dll for Intel(R) 64)
Jim Cownie5e8470a2013-09-27 10:38:44 +0000404 Windows* OS returns error 193 (ERROR_BAD_EXE_FORMAT). However,
405 FormatMessage fails to return a message for this error, so user
406 will see:
407
Jonathan Peyton66338292015-06-01 02:37:28 +0000408 OMP: Warning #2: Cannot open message catalog "1041\libompui.dll":
Jim Cownie5e8470a2013-09-27 10:38:44 +0000409 OMP: System error #193: (No system error message available)
410 OMP: Info #3: Default messages will be used.
411
412 Issue a hint in this case to let cause of trouble more understandable.
413 */
414 __kmp_msg(
415 kmp_ms_warning,
416 KMP_MSG( CantOpenMessageCatalog, path.str ),
417 KMP_SYSERRCODE( error ),
418 ( error == ERROR_BAD_EXE_FORMAT ? KMP_HNT( BadExeFormat, path.str, KMP_ARCH_STR ) : __kmp_msg_null ),
419 __kmp_msg_null
420 );
421 KMP_INFORM( WillUseDefaultMessages );
422 }
423 } else { // status == KMP_I18N_OPENED
424
425 int section = get_section( kmp_i18n_prp_Version );
426 int number = get_number( kmp_i18n_prp_Version );
427 char const * expected = __kmp_i18n_default_table.sect[ section ].str[ number ];
428 kmp_str_buf_t version; // Actual version of the catalog.
429 __kmp_str_buf_init( & version );
430 __kmp_str_buf_print( & version, "%s", ___catgets( kmp_i18n_prp_Version ) );
431 // String returned by catgets is invalid after closing the catalog, so copy it.
432 if ( strcmp( version.str, expected ) != 0 ) {
433 // Close bad catalog.
434 __kmp_i18n_catclose();
435 status = KMP_I18N_ABSENT; // And mark it as absent.
436 if (__kmp_generate_warnings > kmp_warnings_low) {
437 // And now print a warning using default messages.
438 __kmp_msg(
439 kmp_ms_warning,
440 KMP_MSG( WrongMessageCatalog, path.str, version.str, expected ),
441 __kmp_msg_null
442 );
443 KMP_INFORM( WillUseDefaultMessages );
444 } // __kmp_generate_warnings
445 }; // if
446 __kmp_str_buf_free( & version );
447
448 }; // if
449 code_page = get_code_page();
450
451 end:
452 __kmp_str_buf_free( & path );
453 return;
454
455} // func __kmp_i18n_do_catopen
456
457
458void
459__kmp_i18n_catclose(
460) {
461 if ( status == KMP_I18N_OPENED ) {
462 KMP_DEBUG_ASSERT( cat != KMP_I18N_NULLCAT );
463 kmp_i18n_table_free( & table );
464 FreeLibrary( cat );
465 cat = KMP_I18N_NULLCAT;
466 }; // if
467 code_page = default_code_page;
468 status = KMP_I18N_CLOSED;
469} // func __kmp_i18n_catclose
470
471/*
472 We use FormatMessage() to get strings from catalog, get system error messages, etc.
473 FormatMessage() tends to return Windows* OS-style end-of-lines, "\r\n". When string is printed,
Alp Toker8f2d3f02014-02-24 10:40:15 +0000474 printf() also replaces all the occurrences of "\n" with "\r\n" (again!), so sequences like
Jim Cownie5e8470a2013-09-27 10:38:44 +0000475 "\r\r\r\n" appear in output. It is not too good.
476
477 Additional mess comes from message catalog: Our catalog source en_US.mc file (generated by
478 message-converter.pl) contains only "\n" characters, but en_US_msg_1033.bin file (produced by
479 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 +0000480 message catalog, libompui.dll. For example, message
Jim Cownie5e8470a2013-09-27 10:38:44 +0000481
482 Error
483
484 (there is "\n" at the end) is compiled by mc.exe to "Error\r\n", while
485
486 OMP: Error %1!d!: %2!s!\n
487
488 (there is "\n" at the end as well) is compiled to "OMP: Error %1!d!: %2!s!\r\n\n".
489
490 Thus, stripping all "\r" normalizes string and returns it to canonical form, so printf() will
491 produce correct end-of-line sequences.
492
Alp Toker8f2d3f02014-02-24 10:40:15 +0000493 ___strip_crs() serves for this purpose: it removes all the occurrences of "\r" in-place and
Jim Cownie5e8470a2013-09-27 10:38:44 +0000494 returns new length of string.
495*/
496static
497int
498___strip_crs(
499 char * str
500) {
501 int in = 0; // Input character index.
502 int out = 0; // Output character index.
503 for ( ; ; ) {
504 if ( str[ in ] != '\r' ) {
505 str[ out ] = str[ in ];
506 ++ out;
507 }; // if
508 if ( str[ in ] == 0 ) {
509 break;
510 }; // if
511 ++ in;
512 }; // forever
513 return out - 1;
514} // func __strip_crs
515
516
517static
518char const *
519___catgets(
520 kmp_i18n_id_t id
521) {
522
523 char * result = NULL;
524 PVOID addr = NULL;
525 wchar_t * wmsg = NULL;
526 DWORD wlen = 0;
527 char * msg = NULL;
528 int len = 0;
529 int rc;
530
531 KMP_DEBUG_ASSERT( cat != KMP_I18N_NULLCAT );
532 wlen = // wlen does *not* include terminating null.
533 FormatMessageW(
534 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
535 FORMAT_MESSAGE_IGNORE_INSERTS,
536 cat,
537 id,
538 0, // LangId
539 (LPWSTR) & addr,
540 0, // Size in elements, not in bytes.
541 NULL
542 );
543 if ( wlen <= 0 ) {
544 goto end;
545 }; // if
546 wmsg = (wchar_t *) addr; // Warning: wmsg may be not nul-terminated!
547
548 // Calculate length of multibyte message.
549 len = // Since wlen does not include terminating null, len does not include it also.
550 WideCharToMultiByte(
551 code_page,
552 0, // Flags.
553 wmsg, wlen, // Wide buffer and size.
554 NULL, 0, // Buffer and size.
555 NULL, NULL // Default char and used default char.
556 );
557 if ( len <= 0 ) {
558 goto end;
559 }; // if
560
561 // Allocate memory.
562 msg = (char *) KMP_INTERNAL_MALLOC( len + 1 );
563
564 // Convert wide message to multibyte one.
565 rc =
566 WideCharToMultiByte(
567 code_page,
568 0, // Flags.
569 wmsg, wlen, // Wide buffer and size.
570 msg, len, // Buffer and size.
571 NULL, NULL // Default char and used default char.
572 );
573 if ( rc <= 0 || rc > len ) {
574 goto end;
575 }; // if
576 KMP_DEBUG_ASSERT( rc == len );
577 len = rc;
578 msg[ len ] = 0; // Put terminating null to the end.
579
580 // Stripping all "\r" before stripping last end-of-line simplifies the task.
581 len = ___strip_crs( msg );
582
583 // Every message in catalog is terminated with "\n". Strip it.
584 if ( len >= 1 && msg[ len - 1 ] == '\n' ) {
585 -- len;
586 msg[ len ] = 0;
587 }; // if
588
589 // Everything looks ok.
590 result = msg;
591 msg = NULL;
592
593 end:
594
595 if ( msg != NULL ) {
596 KMP_INTERNAL_FREE( msg );
597 }; // if
598 if ( wmsg != NULL ) {
599 LocalFree( wmsg );
600 }; // if
601
602 return result;
603
604} // ___catgets
605
606
607char const *
608__kmp_i18n_catgets(
609 kmp_i18n_id_t id
610) {
611
612 int section = get_section( id );
613 int number = get_number( id );
614 char const * message = NULL;
615
616 if ( 1 <= section && section <= __kmp_i18n_default_table.size ) {
617 if ( 1 <= number && number <= __kmp_i18n_default_table.sect[ section ].size ) {
618 if ( status == KMP_I18N_CLOSED ) {
619 __kmp_i18n_catopen();
620 }; // if
621 if ( cat != KMP_I18N_NULLCAT ) {
622 if ( table.size == 0 ) {
623 table.sect = (kmp_i18n_section_t *)
624 KMP_INTERNAL_CALLOC(
625 ( __kmp_i18n_default_table.size + 2 ),
626 sizeof( kmp_i18n_section_t )
627 );
628 table.size = __kmp_i18n_default_table.size;
629 }; // if
630 if ( table.sect[ section ].size == 0 ) {
631 table.sect[ section ].str = (const char **)
632 KMP_INTERNAL_CALLOC(
633 __kmp_i18n_default_table.sect[ section ].size + 2,
634 sizeof( char const * )
635 );
636 table.sect[ section ].size = __kmp_i18n_default_table.sect[ section ].size;
637 }; // if
638 if ( table.sect[ section ].str[ number ] == NULL ) {
639 table.sect[ section ].str[ number ] = ___catgets( id );
640 }; // if
641 message = table.sect[ section ].str[ number ];
642 }; // if
643 if ( message == NULL ) {
644 // Catalog is not opened or message is not found, return default message.
645 message = __kmp_i18n_default_table.sect[ section ].str[ number ];
646 }; // if
647 }; // if
648 }; // if
649 if ( message == NULL ) {
650 message = no_message_available;
651 }; // if
652 return message;
653
654} // func __kmp_i18n_catgets
655
656
657#endif // KMP_OS_WINDOWS
658
659// -------------------------------------------------------------------------------------------------
660
661#ifndef KMP_I18N_OK
662 #error I18n support is not implemented for this OS.
663#endif // KMP_I18N_OK
664
665// -------------------------------------------------------------------------------------------------
666
667void
668__kmp_i18n_dump_catalog(
Jim Cownie181b4bb2013-12-23 17:28:57 +0000669 kmp_str_buf_t * buffer
Jim Cownie5e8470a2013-09-27 10:38:44 +0000670) {
671
672 struct kmp_i18n_id_range_t {
673 kmp_i18n_id_t first;
674 kmp_i18n_id_t last;
675 }; // struct kmp_i18n_id_range_t
676
Jim Cownie181b4bb2013-12-23 17:28:57 +0000677 static struct kmp_i18n_id_range_t ranges[] = {
Jim Cownie5e8470a2013-09-27 10:38:44 +0000678 { kmp_i18n_prp_first, kmp_i18n_prp_last },
679 { kmp_i18n_str_first, kmp_i18n_str_last },
680 { kmp_i18n_fmt_first, kmp_i18n_fmt_last },
681 { kmp_i18n_msg_first, kmp_i18n_msg_last },
682 { kmp_i18n_hnt_first, kmp_i18n_hnt_last }
683 }; // ranges
684
Jim Cownie181b4bb2013-12-23 17:28:57 +0000685 int num_of_ranges = sizeof( ranges ) / sizeof( struct kmp_i18n_id_range_t );
Jim Cownie5e8470a2013-09-27 10:38:44 +0000686 int range;
687 kmp_i18n_id_t id;
688
689 for ( range = 0; range < num_of_ranges; ++ range ) {
Jim Cownie181b4bb2013-12-23 17:28:57 +0000690 __kmp_str_buf_print( buffer, "*** Set #%d ***\n", range + 1 );
691 for ( id = (kmp_i18n_id_t)( ranges[ range ].first + 1 );
692 id < ranges[ range ].last;
693 id = (kmp_i18n_id_t)( id + 1 ) ) {
694 __kmp_str_buf_print( buffer, "%d: <<%s>>\n", id, __kmp_i18n_catgets( id ) );
Jim Cownie5e8470a2013-09-27 10:38:44 +0000695 }; // for id
696 }; // for range
697
Jim Cownie181b4bb2013-12-23 17:28:57 +0000698 __kmp_printf( "%s", buffer->str );
Jim Cownie5e8470a2013-09-27 10:38:44 +0000699
700} // __kmp_i18n_dump_catalog
701
702// -------------------------------------------------------------------------------------------------
703
704kmp_msg_t
705__kmp_msg_format(
706 kmp_i18n_id_t id,
707 ...
708) {
709
710 kmp_msg_t msg;
711 va_list args;
712 kmp_str_buf_t buffer;
713 __kmp_str_buf_init( & buffer );
714
715 va_start( args, id );
716 #if KMP_OS_UNIX
717 // On Linux* OS and OS X*, printf() family functions process parameter numbers, for example:
718 // "%2$s %1$s".
719 __kmp_str_buf_vprint( & buffer, __kmp_i18n_catgets( id ), args );
720 #elif KMP_OS_WINDOWS
721 // On Winodws, printf() family functions does not recognize GNU style parameter numbers,
722 // so we have to use FormatMessage() instead. It recognizes parameter numbers, e. g.:
723 // "%2!s! "%1!s!".
724 {
725 LPTSTR str = NULL;
726 int len;
727 FormatMessage(
728 FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
729 __kmp_i18n_catgets( id ),
730 0, 0,
731 (LPTSTR)( & str ),
732 0,
733 & args
734 );
735 len = ___strip_crs( str );
736 __kmp_str_buf_cat( & buffer, str, len );
737 LocalFree( str );
738 }
739 #else
740 #error
741 #endif
742 va_end( args );
743 __kmp_str_buf_detach( & buffer );
744
745 msg.type = (kmp_msg_type_t)( id >> 16 );
746 msg.num = id & 0xFFFF;
747 msg.str = buffer.str;
748 msg.len = buffer.used;
749
750 return msg;
751
752} // __kmp_msg_format
753
754// -------------------------------------------------------------------------------------------------
755
756static
757char *
758sys_error(
759 int err
760) {
761
762 char * message = NULL;
763
764 #if KMP_OS_WINDOWS
765
766 LPVOID buffer = NULL;
767 int len;
768 DWORD rc;
769 rc =
770 FormatMessage(
771 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
772 NULL,
773 err,
774 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language.
775 (LPTSTR) & buffer,
776 0,
777 NULL
778 );
779 if ( rc > 0 ) {
780 // Message formatted. Copy it (so we can free it later with normal free().
781 message = __kmp_str_format( "%s", (char *) buffer );
782 len = ___strip_crs( message ); // Delete carriage returns if any.
783 // Strip trailing newlines.
784 while ( len > 0 && message[ len - 1 ] == '\n' ) {
785 -- len;
786 }; // while
787 message[ len ] = 0;
788 } else {
789 // FormatMessage() failed to format system error message. GetLastError() would give us
790 // error code, which we would convert to message... this it dangerous recursion, which
791 // cannot clarify original error, so we will not even start it.
792 }; // if
793 if ( buffer != NULL ) {
794 LocalFree( buffer );
795 }; // if
796
797 #else // Non-Windows* OS: Linux* OS or OS X*
798
799 /*
800 There are 2 incompatible versions of strerror_r:
801
802 char * strerror_r( int, char *, size_t ); // GNU version
803 int strerror_r( int, char *, size_t ); // XSI version
804 */
805
806 #if KMP_OS_LINUX
807
808 // GNU version of strerror_r.
809
810 char buffer[ 2048 ];
811 char * const err_msg = strerror_r( err, buffer, sizeof( buffer ) );
812 // Do not eliminate this assignment to temporary variable, otherwise compiler would
813 // not issue warning if strerror_r() returns `int' instead of expected `char *'.
814 message = __kmp_str_format( "%s", err_msg );
815
Jim Cownie4cc4bb42014-10-07 16:25:50 +0000816 #else // OS X*, FreeBSD* etc.
Jim Cownie5e8470a2013-09-27 10:38:44 +0000817
818 // XSI version of strerror_r.
819
820 int size = 2048;
821 // TODO: Add checking result of malloc().
822 char * buffer = (char *) KMP_INTERNAL_MALLOC( size );
823 int rc;
824 rc = strerror_r( err, buffer, size );
825 if ( rc == -1 ) {
826 rc = errno; // XSI version sets errno.
827 }; // if
828 while ( rc == ERANGE ) { // ERANGE means the buffer is too small.
829 KMP_INTERNAL_FREE( buffer );
830 size *= 2;
831 buffer = (char *) KMP_INTERNAL_MALLOC( size );
832 rc = strerror_r( err, buffer, size );
833 if ( rc == -1 ) {
834 rc = errno; // XSI version sets errno.
835 }; // if
836 }; // while
837 if ( rc == 0 ) {
838 message = buffer;
839 } else {
840 // Buffer is unused. Free it.
841 KMP_INTERNAL_FREE( buffer );
842 }; // if
843
844 #endif
845
846 #endif /* KMP_OS_WINDOWS */
847
848 if ( message == NULL ) {
849 // TODO: I18n this message.
850 message = __kmp_str_format( "%s", "(No system error message available)" );
851 }; // if
852 return message;
853
854} // sys_error
855
856// -------------------------------------------------------------------------------------------------
857
858kmp_msg_t
859__kmp_msg_error_code(
860 int code
861) {
862
863 kmp_msg_t msg;
864 msg.type = kmp_mt_syserr;
865 msg.num = code;
866 msg.str = sys_error( code );
Andrey Churbanov74bf17b2015-04-02 13:27:08 +0000867 msg.len = KMP_STRLEN( msg.str );
Jim Cownie5e8470a2013-09-27 10:38:44 +0000868 return msg;
869
870} // __kmp_msg_error_code
871
872// -------------------------------------------------------------------------------------------------
873
874kmp_msg_t
875__kmp_msg_error_mesg(
876 char const * mesg
877) {
878
879 kmp_msg_t msg;
880 msg.type = kmp_mt_syserr;
881 msg.num = 0;
882 msg.str = __kmp_str_format( "%s", mesg );
Andrey Churbanov74bf17b2015-04-02 13:27:08 +0000883 msg.len = KMP_STRLEN( msg.str );
Jim Cownie5e8470a2013-09-27 10:38:44 +0000884 return msg;
885
886} // __kmp_msg_error_mesg
887
888// -------------------------------------------------------------------------------------------------
889
890void
891__kmp_msg(
892 kmp_msg_severity_t severity,
893 kmp_msg_t message,
894 ...
895) {
896
897 va_list args;
898 kmp_i18n_id_t format; // format identifier
899 kmp_msg_t fmsg; // formatted message
900 kmp_str_buf_t buffer;
901
902 if ( severity != kmp_ms_fatal && __kmp_generate_warnings == kmp_warnings_off )
903 return; // no reason to form a string in order to not print it
904
905 __kmp_str_buf_init( & buffer );
906
907 // Format the primary message.
908 switch ( severity ) {
909 case kmp_ms_inform : {
910 format = kmp_i18n_fmt_Info;
911 } break;
912 case kmp_ms_warning : {
913 format = kmp_i18n_fmt_Warning;
914 } break;
915 case kmp_ms_fatal : {
916 format = kmp_i18n_fmt_Fatal;
917 } break;
918 default : {
919 KMP_DEBUG_ASSERT( 0 );
920 };
921 }; // switch
922 fmsg = __kmp_msg_format( format, message.num, message.str );
923 KMP_INTERNAL_FREE( (void *) message.str );
924 __kmp_str_buf_cat( & buffer, fmsg.str, fmsg.len );
925 KMP_INTERNAL_FREE( (void *) fmsg.str );
926
927 // Format other messages.
928 va_start( args, message );
929 for ( ; ; ) {
930 message = va_arg( args, kmp_msg_t );
931 if ( message.type == kmp_mt_dummy && message.str == NULL ) {
932 break;
933 }; // if
934 if ( message.type == kmp_mt_dummy && message.str == __kmp_msg_empty.str ) {
935 continue;
936 }; // if
937 switch ( message.type ) {
938 case kmp_mt_hint : {
939 format = kmp_i18n_fmt_Hint;
940 } break;
941 case kmp_mt_syserr : {
942 format = kmp_i18n_fmt_SysErr;
943 } break;
944 default : {
945 KMP_DEBUG_ASSERT( 0 );
946 };
947 }; // switch
948 fmsg = __kmp_msg_format( format, message.num, message.str );
949 KMP_INTERNAL_FREE( (void *) message.str );
950 __kmp_str_buf_cat( & buffer, fmsg.str, fmsg.len );
951 KMP_INTERNAL_FREE( (void *) fmsg.str );
952 }; // forever
953 va_end( args );
954
955 // Print formatted messages.
956 // This lock prevents multiple fatal errors on the same problem.
957 // __kmp_acquire_bootstrap_lock( & lock ); // GEH - This lock causing tests to hang on OS X*.
958 __kmp_printf( "%s", buffer.str );
959 __kmp_str_buf_free( & buffer );
960
961 if ( severity == kmp_ms_fatal ) {
962 #if KMP_OS_WINDOWS
963 __kmp_thread_sleep( 500 ); /* Delay to give message a chance to appear before reaping */
964 #endif
965 __kmp_abort_process();
966 }; // if
967
968 // __kmp_release_bootstrap_lock( & lock ); // GEH - this lock causing tests to hang on OS X*.
969
970} // __kmp_msg
971
972// -------------------------------------------------------------------------------------------------
973
974// end of file //