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
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_status
47typedef 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 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
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 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
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_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))
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_02
85http://www.opengroup.org/onlinepubs/000095399/functions/catopen.html
86http://www.opengroup.org/onlinepubs/000095399/functions/setlocale.html
87*/
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 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
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_catclose
186
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_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
225static 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 retrieved
232// 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_page
261
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_free
280
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 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
401end:
402  __kmp_str_buf_free(&path);
403  return;
404} // func __kmp_i18n_do_catopen
405
406void __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.  */
443static 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
459static 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
526end:
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
539char 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
592void __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// -----------------------------------------------------------------------------
623kmp_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// -----------------------------------------------------------------------------
670static 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// -----------------------------------------------------------------------------
764kmp_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// -----------------------------------------------------------------------------
776kmp_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// -----------------------------------------------------------------------------
788void __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
852void __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
859void __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