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