1 /* 2 * kmp_i18n.cpp 3 */ 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 40 kmp_msg_t __kmp_msg_empty = { kmp_mt_dummy, 0, "", 0 }; 41 kmp_msg_t __kmp_msg_null = { kmp_mt_dummy, 0, NULL, 0 }; 42 static char const * no_message_available = "(No message available)"; 43 44 enum 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 49 typedef enum kmp_i18n_cat_status kmp_i18n_cat_status_t; 50 static 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 60 static void __kmp_i18n_do_catopen(); 61 static 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 66 void 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 )) 91 static nl_catd cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile? 92 static char const * name = ( KMP_VERSION_MAJOR == 4 ? "libguide.cat" : "libomp.cat" ); 93 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 101 void 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 145 int error = errno; // Save errno immediately. 146 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_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 } 168 } 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 ]; 173 // Expected version of the catalog. 174 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 178 // String returned by catgets is invalid after closing the catalog, so copy it. 179 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 203 void 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 215 char 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 265 static HMODULE cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile? 266 static char const * name = ( KMP_VERSION_MAJOR == 4 ? "libguide40ui.dll" : "libompui.dll" ); 267 268 static 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(). 272 static UINT const default_code_page = CP_OEMCP; 273 static UINT code_page = default_code_page; 274 275 static char const * ___catgets( kmp_i18n_id_t id ); 276 static UINT get_code_page(); 277 static void kmp_i18n_table_free( kmp_i18n_table_t * table ); 278 279 280 static UINT 281 get_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 305 static void 306 kmp_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; 325 } // kmp_i18n_table_free 326 327 328 void 329 __kmp_i18n_do_catopen( 330 ) { 331 332 LCID locale_id = GetThreadLocale(); 333 WORD lang_id = LANGIDFROMLCID( locale_id ); 334 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 ) { 345 status = KMP_I18N_ABSENT; // mark catalog as absent so it will not be re-opened. 346 goto end; 347 }; // 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 ); 367 if ( ! brc ) { // Error occurred. 368 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 ); 377 if ( drc == 0 ) { // Error occurred. 378 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 403 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. 406 /* 407 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: 412 413 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. 416 417 Issue a hint in this case to let cause of trouble more understandable. 418 */ 419 kmp_msg_t err_code = KMP_SYSERRCODE(error); 420 __kmp_msg( 421 kmp_ms_warning, 422 KMP_MSG( CantOpenMessageCatalog, path.str ), 423 err_code, 424 ( error == ERROR_BAD_EXE_FORMAT ? KMP_HNT( BadExeFormat, path.str, KMP_ARCH_STR ) : __kmp_msg_null ), 425 __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 ); 432 } 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 468 void 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, 484 printf() also replaces all the occurrences of "\n" with "\r\n" (again!), so sequences like 485 "\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 490 message catalog, libompui.dll. For example, message 491 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 503 ___strip_crs() serves for this purpose: it removes all the occurrences of "\r" in-place and 504 returns new length of string. 505 */ 506 static 507 int 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 527 static 528 char 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 617 char 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 677 void 678 __kmp_i18n_dump_catalog( 679 kmp_str_buf_t * buffer 680 ) { 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 687 static struct kmp_i18n_id_range_t ranges[] = { 688 { 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 695 int num_of_ranges = sizeof( ranges ) / sizeof( struct kmp_i18n_id_range_t ); 696 int range; 697 kmp_i18n_id_t id; 698 699 for ( range = 0; range < num_of_ranges; ++ range ) { 700 __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 ) ); 705 }; // for id 706 }; // for range 707 708 __kmp_printf( "%s", buffer->str ); 709 710 } // __kmp_i18n_dump_catalog 711 712 // ------------------------------------------------------------------------------------------------- 713 714 kmp_msg_t 715 __kmp_msg_format( 716 unsigned id_arg, 717 ... 718 ) { 719 720 kmp_msg_t msg; 721 va_list args; 722 kmp_str_buf_t buffer; 723 __kmp_str_buf_init( & buffer ); 724 725 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 732 #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 772 static 773 char * 774 sys_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 822 #if defined(__GLIBC__) && defined(_GNU_SOURCE) 823 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 832 #else // OS X*, FreeBSD* etc. 833 834 // XSI version of strerror_r. 835 836 int size = 2048; 837 char * buffer = (char *) KMP_INTERNAL_MALLOC( size ); 838 int rc; 839 if (buffer == NULL) { 840 KMP_FATAL(MemoryAllocFailed); 841 } 842 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 ); 850 if (buffer == NULL) { 851 KMP_FATAL(MemoryAllocFailed); 852 } 853 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 879 kmp_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 ); 888 msg.len = KMP_STRLEN( msg.str ); 889 return msg; 890 891 } // __kmp_msg_error_code 892 893 // ------------------------------------------------------------------------------------------------- 894 895 kmp_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 ); 904 msg.len = KMP_STRLEN( msg.str ); 905 return msg; 906 907 } // __kmp_msg_error_mesg 908 909 // ------------------------------------------------------------------------------------------------- 910 911 void 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 ); 944 __kmp_str_free(&message.str); 945 __kmp_str_buf_cat( & buffer, fmsg.str, fmsg.len ); 946 __kmp_str_free(&fmsg.str); 947 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 ); 970 __kmp_str_free(&message.str); 971 __kmp_str_buf_cat( & buffer, fmsg.str, fmsg.len ); 972 __kmp_str_free(&fmsg.str); 973 }; // 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 // 996