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