brintos

brintos / llvm-project-archived public Read only

0
0
Text · 27.8 KiB · f93e2b9 Raw
888 lines · cpp
1/*2 * kmp_i18n.cpp3 */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-exception10//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_OK32 33#define get_section(id) ((id) >> 16)34#define get_number(id) ((id)&0xFFFF)35 36kmp_msg_t __kmp_msg_null = {kmp_mt_dummy, 0, NULL, 0};37static char const *no_message_available = "(No message available)";38 39static void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message,40                      va_list ap);41 42enum 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_status47typedef enum kmp_i18n_cat_status kmp_i18n_cat_status_t;48static volatile kmp_i18n_cat_status_t status = KMP_I18N_CLOSED;49 50/* Message catalog is opened at first usage, so we have to synchronize opening51   to avoid race and multiple openings.52 53   Closing does not require synchronization, because catalog is closed very late54   at library shutting down, when no other threads are alive.  */55 56static void __kmp_i18n_do_catopen();57static kmp_bootstrap_lock_t lock = KMP_BOOTSTRAP_LOCK_INITIALIZER(lock);58// `lock' variable may be placed into __kmp_i18n_catopen function because it is59// used only by that function. But we afraid a (buggy) compiler may treat it60// wrongly. So we put it outside of function just in case.61 62void __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_catopen71 72/* Linux* OS and OS X* part */73#if KMP_OS_UNIX74#define KMP_I18N_OK75 76#include <nl_types.h>77 78#define KMP_I18N_NULLCAT ((nl_catd)(-1))79static nl_catd cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?80static char const *name =81    (KMP_VERSION_MAJOR == 4 ? "libguide.cat" : "libomp.cat");82 83/* Useful links:84http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html#tag_08_0285http://www.opengroup.org/onlinepubs/000095399/functions/catopen.html86http://www.opengroup.org/onlinepubs/000095399/functions/setlocale.html87*/88 89void __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 runtime100            // 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 are116  // exact copy of messages in English catalog.117  if (english) {118    status = KMP_I18N_ABSENT; // mark catalog as absent so it will not119    // 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 to130      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, so135      // __kmp_i18n_catgets() will not try to open catalog, but will return136      // 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_OPENED150    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 to164        // 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_warnings173    }174    __kmp_str_buf_free(&version);175  }176} // func __kmp_i18n_do_catopen177 178void __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_catclose186 187char 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_catgets213 214#endif // KMP_OS_UNIX215 216/* Windows* OS part. */217 218#if KMP_OS_WINDOWS219#define KMP_I18N_OK220 221#include "kmp_environment.h"222#include <windows.h>223 224#define KMP_I18N_NULLCAT NULL225static HMODULE cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?226static char const *name =227    (KMP_VERSION_MAJOR == 4 ? "libguide40ui.dll" : "libompui.dll");228 229static 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 retrieved232// messages in the table, which are freed at catclose().233static UINT const default_code_page = CP_OEMCP;234static UINT code_page = default_code_page;235 236static char const *___catgets(kmp_i18n_id_t id);237static UINT get_code_page();238static void kmp_i18n_table_free(kmp_i18n_table_t *table);239 240static 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_page261 262static 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_free280 281void __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 copy294  // of messages in English catalog.295  if (primary_lang_id == LANG_ENGLISH) {296    status = KMP_I18N_ABSENT; // mark catalog as absent so it will not297    // be re-opened.298    goto end;299  }300 301  // Construct resource DLL name.302  /* Simple LoadLibrary( name ) is not suitable due to security issue (see303     http://www.microsoft.com/technet/security/advisory/2269637.mspx). We have304     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 be314      // re-opened.315      goto end;316      // TODO: Enable multiple messages (KMP_MSG) to be passed to __kmp_msg; and317      // 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 a344  // 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 to351      DWORD error = GetLastError();352      // Infinite recursion will not occur -- status is KMP_I18N_ABSENT now, so353      // __kmp_i18n_catgets() will not try to open catalog but will return354      // default message.355      /* If message catalog for another architecture found (e.g. OpenMP RTL for356         IA-32 architecture opens libompui.dll for Intel(R) 64) Windows* OS357         returns error 193 (ERROR_BAD_EXE_FORMAT). However, FormatMessage fails358         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_FORMAT369                     ? 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_OPENED378 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_warnings397    }398    __kmp_str_buf_free(&version);399  }400  code_page = get_code_page();401 402end:403  __kmp_str_buf_free(&path);404  return;405} // func __kmp_i18n_do_catopen406 407void __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_catclose417 418/* We use FormatMessage() to get strings from catalog, get system error419   messages, etc. FormatMessage() tends to return Windows* OS-style420   end-of-lines, "\r\n". When string is printed, printf() also replaces all the421   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 file425   (generated by message-converter.pl) contains only "\n" characters, but426   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, message429 430   Error431 432   (there is "\n" at the end) is compiled by mc.exe to "Error\r\n", while433 434   OMP: Error %1!d!: %2!s!\n435 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 of443   "\r" in-place and returns new length of string.  */444static 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_crs459 460static 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, // LangId477                     (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 527end: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} // ___catgets539 540char 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 default571        // 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_catgets582 583#endif // KMP_OS_WINDOWS584 585// -----------------------------------------------------------------------------586 587#ifndef KMP_I18N_OK588#error I18n support is not implemented for this OS.589#endif // KMP_I18N_OK590 591// -----------------------------------------------------------------------------592 593void __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_t599 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}}; // ranges606 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_catalog622 623// -----------------------------------------------------------------------------624kmp_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 the634  // right enumerator because variadic functions are not compatible with635  // default promotions.636  kmp_i18n_id_t id = (kmp_i18n_id_t)id_arg;637 638#if KMP_OS_UNIX639  // On Linux* OS and OS X*, printf() family functions process parameter640  // numbers, for example:  "%2$s %1$s".641  __kmp_str_buf_vprint(&buffer, __kmp_i18n_catgets(id), args);642#elif KMP_OS_WINDOWS643  // On Windows, printf() family functions does not recognize GNU style644  // parameter numbers, so we have to use FormatMessage() instead. It recognizes645  // 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#else656#error657#endif658  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_format669 670// -----------------------------------------------------------------------------671static char *sys_error(int err) {672 673  char *message = NULL;674 675#if KMP_OS_WINDOWS676 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 it696    // dangerous recursion, which cannot clarify original error, so we will not697    // 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 version708     int    strerror_r( int, char *, size_t );  // XSI version709  */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 compiler719  // would not issue warning if strerror_r() returns `int' instead of expected720  // `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#endif754 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_error763 764// -----------------------------------------------------------------------------765kmp_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_code775 776// -----------------------------------------------------------------------------777kmp_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_mesg787 788// -----------------------------------------------------------------------------789void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, va_list args) {790  kmp_i18n_id_t format; // format identifier791  kmp_msg_t fmsg; // formatted message792  kmp_str_buf_t buffer;793 794  if (severity != kmp_ms_fatal && __kmp_generate_warnings == kmp_warnings_off) {795    // Have to free all possible pre-allocated messages796    // sent in through message and args797    __kmp_str_free(&message.str);798    for (;;) {799      message = va_arg(args, kmp_msg_t);800      if (message.type == kmp_mt_dummy && message.str == NULL) {801        break;802      }803      __kmp_str_free(&message.str);804    }805    return; // no reason to form a string in order to not print it806  }807 808  __kmp_str_buf_init(&buffer);809 810  // Format the primary message.811  switch (severity) {812  case kmp_ms_inform: {813    format = kmp_i18n_fmt_Info;814  } break;815  case kmp_ms_warning: {816    format = kmp_i18n_fmt_Warning;817  } break;818  case kmp_ms_fatal: {819    format = kmp_i18n_fmt_Fatal;820  } break;821  default: {822    KMP_DEBUG_ASSERT(0);823  }824  }825  fmsg = __kmp_msg_format(format, message.num, message.str);826  __kmp_str_free(&message.str);827  __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len);828  __kmp_str_free(&fmsg.str);829 830  // Format other messages.831  for (;;) {832    message = va_arg(args, kmp_msg_t);833    if (message.type == kmp_mt_dummy && message.str == NULL) {834      break;835    }836    switch (message.type) {837    case kmp_mt_hint: {838      format = kmp_i18n_fmt_Hint;839      // we cannot skip %1$ and only use %2$ to print the message without the840      // number841      fmsg = __kmp_msg_format(format, message.str);842    } break;843    case kmp_mt_syserr: {844      format = kmp_i18n_fmt_SysErr;845      fmsg = __kmp_msg_format(format, message.num, message.str);846    } break;847    default: {848      KMP_DEBUG_ASSERT(0);849    }850    }851    __kmp_str_free(&message.str);852    __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len);853    __kmp_str_free(&fmsg.str);854  }855 856  // Print formatted messages.857  // This lock prevents multiple fatal errors on the same problem.858  // __kmp_acquire_bootstrap_lock( & lock );    // GEH - This lock causing tests859  // to hang on OS X*.860  __kmp_printf("%s", buffer.str);861  __kmp_str_buf_free(&buffer);862 863  // __kmp_release_bootstrap_lock( & lock );  // GEH - this lock causing tests864  // to hang on OS X*.865 866} // __kmp_msg867 868void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, ...) {869  va_list args;870  va_start(args, message);871  __kmp_msg(severity, message, args);872  va_end(args);873}874 875void __kmp_fatal(kmp_msg_t message, ...) {876  va_list args;877  va_start(args, message);878  __kmp_msg(kmp_ms_fatal, message, args);879  va_end(args);880#if KMP_OS_WINDOWS881  // Delay to give message a chance to appear before reaping882  __kmp_thread_sleep(500);883#endif884  __kmp_abort_process();885} // __kmp_fatal886 887// end of file //888