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, (error == ERROR_BAD_EXE_FORMAT 368 ? KMP_HNT(BadExeFormat, path.str, KMP_ARCH_STR) 369 : __kmp_msg_null), 370 __kmp_msg_null); 371 if (__kmp_generate_warnings == kmp_warnings_off) { 372 __kmp_str_free(&err_code.str); 373 } 374 KMP_INFORM(WillUseDefaultMessages); 375 } 376 } else { // status == KMP_I18N_OPENED 377 378 int section = get_section(kmp_i18n_prp_Version); 379 int number = get_number(kmp_i18n_prp_Version); 380 char const *expected = __kmp_i18n_default_table.sect[section].str[number]; 381 kmp_str_buf_t version; // Actual version of the catalog. 382 __kmp_str_buf_init(&version); 383 __kmp_str_buf_print(&version, "%s", ___catgets(kmp_i18n_prp_Version)); 384 // String returned by catgets is invalid after closing catalog, so copy it. 385 if (strcmp(version.str, expected) != 0) { 386 // Close bad catalog. 387 __kmp_i18n_catclose(); 388 status = KMP_I18N_ABSENT; // And mark it as absent. 389 if (__kmp_generate_warnings > kmp_warnings_low) { 390 // And now print a warning using default messages. 391 __kmp_msg(kmp_ms_warning, 392 KMP_MSG(WrongMessageCatalog, path.str, version.str, expected), 393 __kmp_msg_null); 394 KMP_INFORM(WillUseDefaultMessages); 395 } // __kmp_generate_warnings 396 } 397 __kmp_str_buf_free(&version); 398 } 399 code_page = get_code_page(); 400 401 end: 402 __kmp_str_buf_free(&path); 403 return; 404 } // func __kmp_i18n_do_catopen 405 406 void __kmp_i18n_catclose() { 407 if (status == KMP_I18N_OPENED) { 408 KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT); 409 kmp_i18n_table_free(&table); 410 FreeLibrary(cat); 411 cat = KMP_I18N_NULLCAT; 412 } 413 code_page = default_code_page; 414 status = KMP_I18N_CLOSED; 415 } // func __kmp_i18n_catclose 416 417 /* We use FormatMessage() to get strings from catalog, get system error 418 messages, etc. FormatMessage() tends to return Windows* OS-style 419 end-of-lines, "\r\n". When string is printed, printf() also replaces all the 420 occurrences of "\n" with "\r\n" (again!), so sequences like "\r\r\r\n" 421 appear in output. It is not too good. 422 423 Additional mess comes from message catalog: Our catalog source en_US.mc file 424 (generated by message-converter.pl) contains only "\n" characters, but 425 en_US_msg_1033.bin file (produced by mc.exe) may contain "\r\n" or just "\n". 426 This mess goes from en_US_msg_1033.bin file to message catalog, 427 libompui.dll. For example, message 428 429 Error 430 431 (there is "\n" at the end) is compiled by mc.exe to "Error\r\n", while 432 433 OMP: Error %1!d!: %2!s!\n 434 435 (there is "\n" at the end as well) is compiled to "OMP: Error %1!d!: 436 %2!s!\r\n\n". 437 438 Thus, stripping all "\r" normalizes string and returns it to canonical form, 439 so printf() will produce correct end-of-line sequences. 440 441 ___strip_crs() serves for this purpose: it removes all the occurrences of 442 "\r" in-place and returns new length of string. */ 443 static int ___strip_crs(char *str) { 444 int in = 0; // Input character index. 445 int out = 0; // Output character index. 446 for (;;) { 447 if (str[in] != '\r') { 448 str[out] = str[in]; 449 ++out; 450 } 451 if (str[in] == 0) { 452 break; 453 } 454 ++in; 455 } 456 return out - 1; 457 } // func __strip_crs 458 459 static char const *___catgets(kmp_i18n_id_t id) { 460 461 char *result = NULL; 462 PVOID addr = NULL; 463 wchar_t *wmsg = NULL; 464 DWORD wlen = 0; 465 char *msg = NULL; 466 int len = 0; 467 int rc; 468 469 KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT); 470 wlen = // wlen does *not* include terminating null. 471 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | 472 FORMAT_MESSAGE_FROM_HMODULE | 473 FORMAT_MESSAGE_IGNORE_INSERTS, 474 cat, id, 475 0, // LangId 476 (LPWSTR)&addr, 477 0, // Size in elements, not in bytes. 478 NULL); 479 if (wlen <= 0) { 480 goto end; 481 } 482 wmsg = (wchar_t *)addr; // Warning: wmsg may be not nul-terminated! 483 484 // Calculate length of multibyte message. 485 // Since wlen does not include terminating null, len does not include it also. 486 len = WideCharToMultiByte(code_page, 487 0, // Flags. 488 wmsg, wlen, // Wide buffer and size. 489 NULL, 0, // Buffer and size. 490 NULL, NULL // Default char and used default char. 491 ); 492 if (len <= 0) { 493 goto end; 494 } 495 496 // Allocate memory. 497 msg = (char *)KMP_INTERNAL_MALLOC(len + 1); 498 499 // Convert wide message to multibyte one. 500 rc = WideCharToMultiByte(code_page, 501 0, // Flags. 502 wmsg, wlen, // Wide buffer and size. 503 msg, len, // Buffer and size. 504 NULL, NULL // Default char and used default char. 505 ); 506 if (rc <= 0 || rc > len) { 507 goto end; 508 } 509 KMP_DEBUG_ASSERT(rc == len); 510 len = rc; 511 msg[len] = 0; // Put terminating null to the end. 512 513 // Stripping all "\r" before stripping last end-of-line simplifies the task. 514 len = ___strip_crs(msg); 515 516 // Every message in catalog is terminated with "\n". Strip it. 517 if (len >= 1 && msg[len - 1] == '\n') { 518 --len; 519 msg[len] = 0; 520 } 521 522 // Everything looks ok. 523 result = msg; 524 msg = NULL; 525 526 end: 527 528 if (msg != NULL) { 529 KMP_INTERNAL_FREE(msg); 530 } 531 if (wmsg != NULL) { 532 LocalFree(wmsg); 533 } 534 535 return result; 536 537 } // ___catgets 538 539 char const *__kmp_i18n_catgets(kmp_i18n_id_t id) { 540 541 int section = get_section(id); 542 int number = get_number(id); 543 char const *message = NULL; 544 545 if (1 <= section && section <= __kmp_i18n_default_table.size) { 546 if (1 <= number && number <= __kmp_i18n_default_table.sect[section].size) { 547 if (status == KMP_I18N_CLOSED) { 548 __kmp_i18n_catopen(); 549 } 550 if (cat != KMP_I18N_NULLCAT) { 551 if (table.size == 0) { 552 table.sect = (kmp_i18n_section_t *)KMP_INTERNAL_CALLOC( 553 (__kmp_i18n_default_table.size + 2), sizeof(kmp_i18n_section_t)); 554 table.size = __kmp_i18n_default_table.size; 555 } 556 if (table.sect[section].size == 0) { 557 table.sect[section].str = (const char **)KMP_INTERNAL_CALLOC( 558 __kmp_i18n_default_table.sect[section].size + 2, 559 sizeof(char const *)); 560 table.sect[section].size = 561 __kmp_i18n_default_table.sect[section].size; 562 } 563 if (table.sect[section].str[number] == NULL) { 564 table.sect[section].str[number] = ___catgets(id); 565 } 566 message = table.sect[section].str[number]; 567 } 568 if (message == NULL) { 569 // Catalog is not opened or message is not found, return default 570 // message. 571 message = __kmp_i18n_default_table.sect[section].str[number]; 572 } 573 } 574 } 575 if (message == NULL) { 576 message = no_message_available; 577 } 578 return message; 579 580 } // func __kmp_i18n_catgets 581 582 #endif // KMP_OS_WINDOWS 583 584 // ----------------------------------------------------------------------------- 585 586 #ifndef KMP_I18N_OK 587 #error I18n support is not implemented for this OS. 588 #endif // KMP_I18N_OK 589 590 // ----------------------------------------------------------------------------- 591 592 void __kmp_i18n_dump_catalog(kmp_str_buf_t *buffer) { 593 594 struct kmp_i18n_id_range_t { 595 kmp_i18n_id_t first; 596 kmp_i18n_id_t last; 597 }; // struct kmp_i18n_id_range_t 598 599 static struct kmp_i18n_id_range_t ranges[] = { 600 {kmp_i18n_prp_first, kmp_i18n_prp_last}, 601 {kmp_i18n_str_first, kmp_i18n_str_last}, 602 {kmp_i18n_fmt_first, kmp_i18n_fmt_last}, 603 {kmp_i18n_msg_first, kmp_i18n_msg_last}, 604 {kmp_i18n_hnt_first, kmp_i18n_hnt_last}}; // ranges 605 606 int num_of_ranges = sizeof(ranges) / sizeof(struct kmp_i18n_id_range_t); 607 int range; 608 kmp_i18n_id_t id; 609 610 for (range = 0; range < num_of_ranges; ++range) { 611 __kmp_str_buf_print(buffer, "*** Set #%d ***\n", range + 1); 612 for (id = (kmp_i18n_id_t)(ranges[range].first + 1); id < ranges[range].last; 613 id = (kmp_i18n_id_t)(id + 1)) { 614 __kmp_str_buf_print(buffer, "%d: <<%s>>\n", id, __kmp_i18n_catgets(id)); 615 } 616 } 617 618 __kmp_printf("%s", buffer->str); 619 620 } // __kmp_i18n_dump_catalog 621 622 // ----------------------------------------------------------------------------- 623 kmp_msg_t __kmp_msg_format(unsigned id_arg, ...) { 624 625 kmp_msg_t msg; 626 va_list args; 627 kmp_str_buf_t buffer; 628 __kmp_str_buf_init(&buffer); 629 630 va_start(args, id_arg); 631 632 // We use unsigned for the ID argument and explicitly cast it here to the 633 // right enumerator because variadic functions are not compatible with 634 // default promotions. 635 kmp_i18n_id_t id = (kmp_i18n_id_t)id_arg; 636 637 #if KMP_OS_UNIX 638 // On Linux* OS and OS X*, printf() family functions process parameter 639 // numbers, for example: "%2$s %1$s". 640 __kmp_str_buf_vprint(&buffer, __kmp_i18n_catgets(id), args); 641 #elif KMP_OS_WINDOWS 642 // On Winodws, printf() family functions does not recognize GNU style 643 // parameter numbers, so we have to use FormatMessage() instead. It recognizes 644 // parameter numbers, e. g.: "%2!s! "%1!s!". 645 { 646 LPTSTR str = NULL; 647 int len; 648 FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, 649 __kmp_i18n_catgets(id), 0, 0, (LPTSTR)(&str), 0, &args); 650 len = ___strip_crs(str); 651 __kmp_str_buf_cat(&buffer, str, len); 652 LocalFree(str); 653 } 654 #else 655 #error 656 #endif 657 va_end(args); 658 __kmp_str_buf_detach(&buffer); 659 660 msg.type = (kmp_msg_type_t)(id >> 16); 661 msg.num = id & 0xFFFF; 662 msg.str = buffer.str; 663 msg.len = buffer.used; 664 665 return msg; 666 667 } // __kmp_msg_format 668 669 // ----------------------------------------------------------------------------- 670 static char *sys_error(int err) { 671 672 char *message = NULL; 673 674 #if KMP_OS_WINDOWS 675 676 LPVOID buffer = NULL; 677 int len; 678 DWORD rc; 679 rc = FormatMessage( 680 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 681 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language. 682 (LPTSTR)&buffer, 0, NULL); 683 if (rc > 0) { 684 // Message formatted. Copy it (so we can free it later with normal free(). 685 message = __kmp_str_format("%s", (char *)buffer); 686 len = ___strip_crs(message); // Delete carriage returns if any. 687 // Strip trailing newlines. 688 while (len > 0 && message[len - 1] == '\n') { 689 --len; 690 } 691 message[len] = 0; 692 } else { 693 // FormatMessage() failed to format system error message. GetLastError() 694 // would give us error code, which we would convert to message... this it 695 // dangerous recursion, which cannot clarify original error, so we will not 696 // even start it. 697 } 698 if (buffer != NULL) { 699 LocalFree(buffer); 700 } 701 702 #else // Non-Windows* OS: Linux* OS or OS X* 703 704 /* There are 2 incompatible versions of strerror_r: 705 706 char * strerror_r( int, char *, size_t ); // GNU version 707 int strerror_r( int, char *, size_t ); // XSI version 708 */ 709 710 #if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || \ 711 (defined(__BIONIC__) && defined(_GNU_SOURCE) && \ 712 __ANDROID_API__ >= __ANDROID_API_M__) 713 // GNU version of strerror_r. 714 715 char buffer[2048]; 716 char *const err_msg = strerror_r(err, buffer, sizeof(buffer)); 717 // Do not eliminate this assignment to temporary variable, otherwise compiler 718 // would not issue warning if strerror_r() returns `int' instead of expected 719 // `char *'. 720 message = __kmp_str_format("%s", err_msg); 721 722 #else // OS X*, FreeBSD* etc. 723 // XSI version of strerror_r. 724 int size = 2048; 725 char *buffer = (char *)KMP_INTERNAL_MALLOC(size); 726 int rc; 727 if (buffer == NULL) { 728 KMP_FATAL(MemoryAllocFailed); 729 } 730 rc = strerror_r(err, buffer, size); 731 if (rc == -1) { 732 rc = errno; // XSI version sets errno. 733 } 734 while (rc == ERANGE) { // ERANGE means the buffer is too small. 735 KMP_INTERNAL_FREE(buffer); 736 size *= 2; 737 buffer = (char *)KMP_INTERNAL_MALLOC(size); 738 if (buffer == NULL) { 739 KMP_FATAL(MemoryAllocFailed); 740 } 741 rc = strerror_r(err, buffer, size); 742 if (rc == -1) { 743 rc = errno; // XSI version sets errno. 744 } 745 } 746 if (rc == 0) { 747 message = buffer; 748 } else { // Buffer is unused. Free it. 749 KMP_INTERNAL_FREE(buffer); 750 } 751 752 #endif 753 754 #endif /* KMP_OS_WINDOWS */ 755 756 if (message == NULL) { 757 // TODO: I18n this message. 758 message = __kmp_str_format("%s", "(No system error message available)"); 759 } 760 return message; 761 } // sys_error 762 763 // ----------------------------------------------------------------------------- 764 kmp_msg_t __kmp_msg_error_code(int code) { 765 766 kmp_msg_t msg; 767 msg.type = kmp_mt_syserr; 768 msg.num = code; 769 msg.str = sys_error(code); 770 msg.len = KMP_STRLEN(msg.str); 771 return msg; 772 773 } // __kmp_msg_error_code 774 775 // ----------------------------------------------------------------------------- 776 kmp_msg_t __kmp_msg_error_mesg(char const *mesg) { 777 778 kmp_msg_t msg; 779 msg.type = kmp_mt_syserr; 780 msg.num = 0; 781 msg.str = __kmp_str_format("%s", mesg); 782 msg.len = KMP_STRLEN(msg.str); 783 return msg; 784 785 } // __kmp_msg_error_mesg 786 787 // ----------------------------------------------------------------------------- 788 void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, va_list args) { 789 kmp_i18n_id_t format; // format identifier 790 kmp_msg_t fmsg; // formatted message 791 kmp_str_buf_t buffer; 792 793 if (severity != kmp_ms_fatal && __kmp_generate_warnings == kmp_warnings_off) 794 return; // no reason to form a string in order to not print it 795 796 __kmp_str_buf_init(&buffer); 797 798 // Format the primary message. 799 switch (severity) { 800 case kmp_ms_inform: { 801 format = kmp_i18n_fmt_Info; 802 } break; 803 case kmp_ms_warning: { 804 format = kmp_i18n_fmt_Warning; 805 } break; 806 case kmp_ms_fatal: { 807 format = kmp_i18n_fmt_Fatal; 808 } break; 809 default: { KMP_DEBUG_ASSERT(0); } 810 } 811 fmsg = __kmp_msg_format(format, message.num, message.str); 812 __kmp_str_free(&message.str); 813 __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len); 814 __kmp_str_free(&fmsg.str); 815 816 // Format other messages. 817 for (;;) { 818 message = va_arg(args, kmp_msg_t); 819 if (message.type == kmp_mt_dummy && message.str == NULL) { 820 break; 821 } 822 switch (message.type) { 823 case kmp_mt_hint: { 824 format = kmp_i18n_fmt_Hint; 825 // we cannot skip %1$ and only use %2$ to print the message without the 826 // number 827 fmsg = __kmp_msg_format(format, message.str); 828 } break; 829 case kmp_mt_syserr: { 830 format = kmp_i18n_fmt_SysErr; 831 fmsg = __kmp_msg_format(format, message.num, message.str); 832 } break; 833 default: { KMP_DEBUG_ASSERT(0); } 834 } 835 __kmp_str_free(&message.str); 836 __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len); 837 __kmp_str_free(&fmsg.str); 838 } 839 840 // Print formatted messages. 841 // This lock prevents multiple fatal errors on the same problem. 842 // __kmp_acquire_bootstrap_lock( & lock ); // GEH - This lock causing tests 843 // to hang on OS X*. 844 __kmp_printf("%s", buffer.str); 845 __kmp_str_buf_free(&buffer); 846 847 // __kmp_release_bootstrap_lock( & lock ); // GEH - this lock causing tests 848 // to hang on OS X*. 849 850 } // __kmp_msg 851 852 void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, ...) { 853 va_list args; 854 va_start(args, message); 855 __kmp_msg(severity, message, args); 856 va_end(args); 857 } 858 859 void __kmp_fatal(kmp_msg_t message, ...) { 860 va_list args; 861 va_start(args, message); 862 __kmp_msg(kmp_ms_fatal, message, args); 863 va_end(args); 864 #if KMP_OS_WINDOWS 865 // Delay to give message a chance to appear before reaping 866 __kmp_thread_sleep(500); 867 #endif 868 __kmp_abort_process(); 869 } // __kmp_fatal 870 871 // end of file // 872