LLVM OpenMP* Runtime Library
kmp_i18n.cpp
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 //