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