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, (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 //