LLVM OpenMP* Runtime Library
kmp_i18n.cpp
1 /*
2  * kmp_i18n.cpp
3  */
4 
5 
6 //===----------------------------------------------------------------------===//
7 //
8 // The LLVM Compiler Infrastructure
9 //
10 // This file is dual licensed under the MIT and the University of Illinois Open
11 // Source Licenses. See LICENSE.txt for details.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 
16 #include "kmp_i18n.h"
17 
18 #include "kmp.h"
19 #include "kmp_debug.h"
20 #include "kmp_io.h" // __kmp_printf.
21 #include "kmp_lock.h"
22 #include "kmp_os.h"
23 
24 #include <errno.h>
25 #include <locale.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29 
30 #include "kmp_environment.h"
31 #include "kmp_i18n_default.inc"
32 #include "kmp_str.h"
33 
34 #undef KMP_I18N_OK
35 
36 #define get_section(id) ((id) >> 16)
37 #define get_number(id) ((id)&0xFFFF)
38 
39 kmp_msg_t __kmp_msg_empty = {kmp_mt_dummy, 0, "", 0};
40 kmp_msg_t __kmp_msg_null = {kmp_mt_dummy, 0, NULL, 0};
41 static char const *no_message_available = "(No message available)";
42 
43 enum kmp_i18n_cat_status {
44  KMP_I18N_CLOSED, // Not yet opened or closed.
45  KMP_I18N_OPENED, // Opened successfully, ready to use.
46  KMP_I18N_ABSENT // Opening failed, message catalog should not be used.
47 }; // enum kmp_i18n_cat_status
48 typedef enum kmp_i18n_cat_status kmp_i18n_cat_status_t;
49 static volatile kmp_i18n_cat_status_t status = KMP_I18N_CLOSED;
50 
51 /* Message catalog is opened at first usage, so we have to synchronize opening
52  to avoid race and multiple openings.
53 
54  Closing does not require synchronization, because catalog is closed very late
55  at library shutting down, when no other threads are alive. */
56 
57 static void __kmp_i18n_do_catopen();
58 static kmp_bootstrap_lock_t lock = KMP_BOOTSTRAP_LOCK_INITIALIZER(lock);
59 // `lock' variable may be placed into __kmp_i18n_catopen function because it is
60 // used only by that function. But we afraid a (buggy) compiler may treat it
61 // wrongly. So we put it outside of function just in case.
62 
63 void __kmp_i18n_catopen() {
64  if (status == KMP_I18N_CLOSED) {
65  __kmp_acquire_bootstrap_lock(&lock);
66  if (status == KMP_I18N_CLOSED) {
67  __kmp_i18n_do_catopen();
68  }; // if
69  __kmp_release_bootstrap_lock(&lock);
70  }; // if
71 } // func __kmp_i18n_catopen
72 
73 /* Linux* OS and OS X* part */
74 #if KMP_OS_UNIX
75 #define KMP_I18N_OK
76 
77 #include <nl_types.h>
78 
79 #define KMP_I18N_NULLCAT ((nl_catd)(-1))
80 static nl_catd cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?
81 static char const *name =
82  (KMP_VERSION_MAJOR == 4 ? "libguide.cat" : "libomp.cat");
83 
84 /* Useful links:
85 http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html#tag_08_02
86 http://www.opengroup.org/onlinepubs/000095399/functions/catopen.html
87 http://www.opengroup.org/onlinepubs/000095399/functions/setlocale.html
88 */
89 
90 void __kmp_i18n_do_catopen() {
91  int english = 0;
92  char *lang = __kmp_env_get("LANG");
93  // TODO: What about LC_ALL or LC_MESSAGES?
94 
95  KMP_DEBUG_ASSERT(status == KMP_I18N_CLOSED);
96  KMP_DEBUG_ASSERT(cat == KMP_I18N_NULLCAT);
97 
98  english = lang == NULL || // In all these cases English language is used.
99  strcmp(lang, "") == 0 || strcmp(lang, " ") == 0 ||
100  // Workaround for Fortran RTL bug DPD200137873 "Fortran runtime
101  // resets LANG env var to space if it is not set".
102  strcmp(lang, "C") == 0 || strcmp(lang, "POSIX") == 0;
103 
104  if (!english) { // English language is not yet detected, let us continue.
105  // Format of LANG is: [language[_territory][.codeset][@modifier]]
106  // Strip all parts except language.
107  char *tail = NULL;
108  __kmp_str_split(lang, '@', &lang, &tail);
109  __kmp_str_split(lang, '.', &lang, &tail);
110  __kmp_str_split(lang, '_', &lang, &tail);
111  english = (strcmp(lang, "en") == 0);
112  }; // if
113 
114  KMP_INTERNAL_FREE(lang);
115 
116  // Do not try to open English catalog because internal messages are
117  // exact copy of messages in English catalog.
118  if (english) {
119  status = KMP_I18N_ABSENT; // mark catalog as absent so it will not
120  // be re-opened.
121  return;
122  }
123 
124  cat = catopen(name, 0);
125  // TODO: Why do we pass 0 in flags?
126  status = (cat == KMP_I18N_NULLCAT ? KMP_I18N_ABSENT : KMP_I18N_OPENED);
127 
128  if (status == KMP_I18N_ABSENT) {
129  if (__kmp_generate_warnings > kmp_warnings_low) {
130  // AC: only issue warning in case explicitly asked to
131  int error = errno; // Save errno immediately.
132  char *nlspath = __kmp_env_get("NLSPATH");
133  char *lang = __kmp_env_get("LANG");
134 
135  // Infinite recursion will not occur -- status is KMP_I18N_ABSENT now, so
136  // __kmp_i18n_catgets() will not try to open catalog, but will return
137  // default message.
138  kmp_msg_t err_code = KMP_ERR(error);
139  __kmp_msg(kmp_ms_warning, KMP_MSG(CantOpenMessageCatalog, name), err_code,
140  KMP_HNT(CheckEnvVar, "NLSPATH", nlspath),
141  KMP_HNT(CheckEnvVar, "LANG", lang), __kmp_msg_null);
142  if (__kmp_generate_warnings == kmp_warnings_off) {
143  __kmp_str_free(&err_code.str);
144  }
145 
146  KMP_INFORM(WillUseDefaultMessages);
147  KMP_INTERNAL_FREE(nlspath);
148  KMP_INTERNAL_FREE(lang);
149  }
150  } else { // status == KMP_I18N_OPENED
151  int section = get_section(kmp_i18n_prp_Version);
152  int number = get_number(kmp_i18n_prp_Version);
153  char const *expected = __kmp_i18n_default_table.sect[section].str[number];
154  // Expected version of the catalog.
155  kmp_str_buf_t version; // Actual version of the catalog.
156  __kmp_str_buf_init(&version);
157  __kmp_str_buf_print(&version, "%s", catgets(cat, section, number, NULL));
158 
159  // String returned by catgets is invalid after closing catalog, so copy it.
160  if (strcmp(version.str, expected) != 0) {
161  __kmp_i18n_catclose(); // Close bad catalog.
162  status = KMP_I18N_ABSENT; // And mark it as absent.
163  if (__kmp_generate_warnings > kmp_warnings_low) {
164  // AC: only issue warning in case explicitly asked to
165  // And now print a warning using default messages.
166  char const *name = "NLSPATH";
167  char const *nlspath = __kmp_env_get(name);
168  __kmp_msg(kmp_ms_warning,
169  KMP_MSG(WrongMessageCatalog, name, version.str, expected),
170  KMP_HNT(CheckEnvVar, name, nlspath), __kmp_msg_null);
171  KMP_INFORM(WillUseDefaultMessages);
172  KMP_INTERNAL_FREE(CCAST(char *, nlspath));
173  } // __kmp_generate_warnings
174  }; // if
175  __kmp_str_buf_free(&version);
176  }; // if
177 } // func __kmp_i18n_do_catopen
178 
179 void __kmp_i18n_catclose() {
180  if (status == KMP_I18N_OPENED) {
181  KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
182  catclose(cat);
183  cat = KMP_I18N_NULLCAT;
184  }; // if
185  status = KMP_I18N_CLOSED;
186 } // func __kmp_i18n_catclose
187 
188 char const *__kmp_i18n_catgets(kmp_i18n_id_t id) {
189 
190  int section = get_section(id);
191  int number = get_number(id);
192  char const *message = NULL;
193 
194  if (1 <= section && section <= __kmp_i18n_default_table.size) {
195  if (1 <= number && number <= __kmp_i18n_default_table.sect[section].size) {
196  if (status == KMP_I18N_CLOSED) {
197  __kmp_i18n_catopen();
198  }; // if
199  if (status == KMP_I18N_OPENED) {
200  message = catgets(cat, section, number,
201  __kmp_i18n_default_table.sect[section].str[number]);
202  }; // if
203  if (message == NULL) {
204  message = __kmp_i18n_default_table.sect[section].str[number];
205  }; // if
206  }; // if
207  }; // if
208  if (message == NULL) {
209  message = no_message_available;
210  }; // if
211  return message;
212 
213 } // func __kmp_i18n_catgets
214 
215 #endif // KMP_OS_UNIX
216 
217 /* Windows* OS part. */
218 
219 #if KMP_OS_WINDOWS
220 #define KMP_I18N_OK
221 
222 #include "kmp_environment.h"
223 #include <windows.h>
224 
225 #define KMP_I18N_NULLCAT NULL
226 static HMODULE cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?
227 static char const *name =
228  (KMP_VERSION_MAJOR == 4 ? "libguide40ui.dll" : "libompui.dll");
229 
230 static kmp_i18n_table_t table = {0, NULL};
231 // Messages formatted by FormatMessage() should be freed, but catgets()
232 // interface assumes user will not free messages. So we cache all the retrieved
233 // messages in the table, which are freed at catclose().
234 static UINT const default_code_page = CP_OEMCP;
235 static UINT code_page = default_code_page;
236 
237 static char const *___catgets(kmp_i18n_id_t id);
238 static UINT get_code_page();
239 static void kmp_i18n_table_free(kmp_i18n_table_t *table);
240 
241 static UINT get_code_page() {
242 
243  UINT cp = default_code_page;
244  char const *value = __kmp_env_get("KMP_CODEPAGE");
245  if (value != NULL) {
246  if (_stricmp(value, "ANSI") == 0) {
247  cp = CP_ACP;
248  } else if (_stricmp(value, "OEM") == 0) {
249  cp = CP_OEMCP;
250  } else if (_stricmp(value, "UTF-8") == 0 || _stricmp(value, "UTF8") == 0) {
251  cp = CP_UTF8;
252  } else if (_stricmp(value, "UTF-7") == 0 || _stricmp(value, "UTF7") == 0) {
253  cp = CP_UTF7;
254  } else {
255  // !!! TODO: Issue a warning?
256  }; // if
257  }; // if
258  KMP_INTERNAL_FREE((void *)value);
259  return cp;
260 
261 } // func get_code_page
262 
263 static void kmp_i18n_table_free(kmp_i18n_table_t *table) {
264  int s;
265  int m;
266  for (s = 0; s < table->size; ++s) {
267  for (m = 0; m < table->sect[s].size; ++m) {
268  // Free message.
269  KMP_INTERNAL_FREE((void *)table->sect[s].str[m]);
270  table->sect[s].str[m] = NULL;
271  }; // for m
272  table->sect[s].size = 0;
273  // Free section itself.
274  KMP_INTERNAL_FREE((void *)table->sect[s].str);
275  table->sect[s].str = NULL;
276  }; // for s
277  table->size = 0;
278  KMP_INTERNAL_FREE((void *)table->sect);
279  table->sect = NULL;
280 } // kmp_i18n_table_free
281 
282 void __kmp_i18n_do_catopen() {
283 
284  LCID locale_id = GetThreadLocale();
285  WORD lang_id = LANGIDFROMLCID(locale_id);
286  WORD primary_lang_id = PRIMARYLANGID(lang_id);
287  kmp_str_buf_t path;
288 
289  KMP_DEBUG_ASSERT(status == KMP_I18N_CLOSED);
290  KMP_DEBUG_ASSERT(cat == KMP_I18N_NULLCAT);
291 
292  __kmp_str_buf_init(&path);
293 
294  // Do not try to open English catalog because internal messages are exact copy
295  // of messages in English catalog.
296  if (primary_lang_id == LANG_ENGLISH) {
297  status = KMP_I18N_ABSENT; // mark catalog as absent so it will not
298  // be re-opened.
299  goto end;
300  }; // if
301 
302  // Construct resource DLL name.
303  /* Simple LoadLibrary( name ) is not suitable due to security issue (see
304  http://www.microsoft.com/technet/security/advisory/2269637.mspx). We have
305  to specify full path to the message catalog. */
306  {
307  // Get handle of our DLL first.
308  HMODULE handle;
309  BOOL brc = GetModuleHandleEx(
310  GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
311  GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
312  reinterpret_cast<LPCSTR>(&__kmp_i18n_do_catopen), &handle);
313  if (!brc) { // Error occurred.
314  status = KMP_I18N_ABSENT; // mark catalog as absent so it will not be
315  // re-opened.
316  goto end;
317  // TODO: Enable multiple messages (KMP_MSG) to be passed to __kmp_msg; and
318  // print a proper warning.
319  }; // if
320 
321  // Now get path to the our DLL.
322  for (;;) {
323  DWORD drc = GetModuleFileName(handle, path.str, path.size);
324  if (drc == 0) { // Error occurred.
325  status = KMP_I18N_ABSENT;
326  goto end;
327  }; // if
328  if (drc < path.size) {
329  path.used = drc;
330  break;
331  }; // if
332  __kmp_str_buf_reserve(&path, path.size * 2);
333  }; // forever
334 
335  // Now construct the name of message catalog.
336  kmp_str_fname fname;
337  __kmp_str_fname_init(&fname, path.str);
338  __kmp_str_buf_clear(&path);
339  __kmp_str_buf_print(&path, "%s%lu/%s", fname.dir,
340  (unsigned long)(locale_id), name);
341  __kmp_str_fname_free(&fname);
342  }
343 
344  // For security reasons, use LoadLibraryEx() and load message catalog as a
345  // data file.
346  cat = LoadLibraryEx(path.str, NULL, LOAD_LIBRARY_AS_DATAFILE);
347  status = (cat == KMP_I18N_NULLCAT ? KMP_I18N_ABSENT : KMP_I18N_OPENED);
348 
349  if (status == KMP_I18N_ABSENT) {
350  if (__kmp_generate_warnings > kmp_warnings_low) {
351  // AC: only issue warning in case explicitly asked to
352  DWORD error = GetLastError();
353  // Infinite recursion will not occur -- status is KMP_I18N_ABSENT now, so
354  // __kmp_i18n_catgets() will not try to open catalog but will return
355  // default message.
356  /* If message catalog for another architecture found (e.g. OpenMP RTL for
357  IA-32 architecture opens libompui.dll for Intel(R) 64) Windows* OS
358  returns error 193 (ERROR_BAD_EXE_FORMAT). However, FormatMessage fails
359  to return a message for this error, so user will see:
360 
361  OMP: Warning #2: Cannot open message catalog "1041\libompui.dll":
362  OMP: System error #193: (No system error message available)
363  OMP: Info #3: Default messages will be used.
364 
365  Issue hint in this case so cause of trouble is more understandable. */
366  kmp_msg_t err_code = KMP_SYSERRCODE(error);
367  __kmp_msg(kmp_ms_warning, KMP_MSG(CantOpenMessageCatalog, path.str),
368  err_code, (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  }; // if
398  __kmp_str_buf_free(&version);
399 
400  }; // if
401  code_page = get_code_page();
402 
403 end:
404  __kmp_str_buf_free(&path);
405  return;
406 } // func __kmp_i18n_do_catopen
407 
408 void __kmp_i18n_catclose() {
409  if (status == KMP_I18N_OPENED) {
410  KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
411  kmp_i18n_table_free(&table);
412  FreeLibrary(cat);
413  cat = KMP_I18N_NULLCAT;
414  }; // if
415  code_page = default_code_page;
416  status = KMP_I18N_CLOSED;
417 } // func __kmp_i18n_catclose
418 
419 /* We use FormatMessage() to get strings from catalog, get system error
420  messages, etc. FormatMessage() tends to return Windows* OS-style
421  end-of-lines, "\r\n". When string is printed, printf() also replaces all the
422  occurrences of "\n" with "\r\n" (again!), so sequences like "\r\r\r\n"
423  appear in output. It is not too good.
424 
425  Additional mess comes from message catalog: Our catalog source en_US.mc file
426  (generated by message-converter.pl) contains only "\n" characters, but
427  en_US_msg_1033.bin file (produced by mc.exe) may contain "\r\n" or just "\n".
428  This mess goes from en_US_msg_1033.bin file to message catalog,
429  libompui.dll. For example, message
430 
431  Error
432 
433  (there is "\n" at the end) is compiled by mc.exe to "Error\r\n", while
434 
435  OMP: Error %1!d!: %2!s!\n
436 
437  (there is "\n" at the end as well) is compiled to "OMP: Error %1!d!:
438  %2!s!\r\n\n".
439 
440  Thus, stripping all "\r" normalizes string and returns it to canonical form,
441  so printf() will produce correct end-of-line sequences.
442 
443  ___strip_crs() serves for this purpose: it removes all the occurrences of
444  "\r" in-place and returns new length of string. */
445 static int ___strip_crs(char *str) {
446  int in = 0; // Input character index.
447  int out = 0; // Output character index.
448  for (;;) {
449  if (str[in] != '\r') {
450  str[out] = str[in];
451  ++out;
452  }; // if
453  if (str[in] == 0) {
454  break;
455  }; // if
456  ++in;
457  }; // forever
458  return out - 1;
459 } // func __strip_crs
460 
461 static char const *___catgets(kmp_i18n_id_t id) {
462 
463  char *result = NULL;
464  PVOID addr = NULL;
465  wchar_t *wmsg = NULL;
466  DWORD wlen = 0;
467  char *msg = NULL;
468  int len = 0;
469  int rc;
470 
471  KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
472  wlen = // wlen does *not* include terminating null.
473  FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
474  FORMAT_MESSAGE_FROM_HMODULE |
475  FORMAT_MESSAGE_IGNORE_INSERTS,
476  cat, id,
477  0, // LangId
478  (LPWSTR)&addr,
479  0, // Size in elements, not in bytes.
480  NULL);
481  if (wlen <= 0) {
482  goto end;
483  }; // if
484  wmsg = (wchar_t *)addr; // Warning: wmsg may be not nul-terminated!
485 
486  // Calculate length of multibyte message.
487  // Since wlen does not include terminating null, len does not include it also.
488  len = WideCharToMultiByte(code_page,
489  0, // Flags.
490  wmsg, wlen, // Wide buffer and size.
491  NULL, 0, // Buffer and size.
492  NULL, NULL // Default char and used default char.
493  );
494  if (len <= 0) {
495  goto end;
496  }; // if
497 
498  // Allocate memory.
499  msg = (char *)KMP_INTERNAL_MALLOC(len + 1);
500 
501  // Convert wide message to multibyte one.
502  rc = WideCharToMultiByte(code_page,
503  0, // Flags.
504  wmsg, wlen, // Wide buffer and size.
505  msg, len, // Buffer and size.
506  NULL, NULL // Default char and used default char.
507  );
508  if (rc <= 0 || rc > len) {
509  goto end;
510  }; // if
511  KMP_DEBUG_ASSERT(rc == len);
512  len = rc;
513  msg[len] = 0; // Put terminating null to the end.
514 
515  // Stripping all "\r" before stripping last end-of-line simplifies the task.
516  len = ___strip_crs(msg);
517 
518  // Every message in catalog is terminated with "\n". Strip it.
519  if (len >= 1 && msg[len - 1] == '\n') {
520  --len;
521  msg[len] = 0;
522  }; // if
523 
524  // Everything looks ok.
525  result = msg;
526  msg = NULL;
527 
528 end:
529 
530  if (msg != NULL) {
531  KMP_INTERNAL_FREE(msg);
532  }; // if
533  if (wmsg != NULL) {
534  LocalFree(wmsg);
535  }; // if
536 
537  return result;
538 
539 } // ___catgets
540 
541 char const *__kmp_i18n_catgets(kmp_i18n_id_t id) {
542 
543  int section = get_section(id);
544  int number = get_number(id);
545  char const *message = NULL;
546 
547  if (1 <= section && section <= __kmp_i18n_default_table.size) {
548  if (1 <= number && number <= __kmp_i18n_default_table.sect[section].size) {
549  if (status == KMP_I18N_CLOSED) {
550  __kmp_i18n_catopen();
551  }; // if
552  if (cat != KMP_I18N_NULLCAT) {
553  if (table.size == 0) {
554  table.sect = (kmp_i18n_section_t *)KMP_INTERNAL_CALLOC(
555  (__kmp_i18n_default_table.size + 2), sizeof(kmp_i18n_section_t));
556  table.size = __kmp_i18n_default_table.size;
557  }; // if
558  if (table.sect[section].size == 0) {
559  table.sect[section].str = (const char **)KMP_INTERNAL_CALLOC(
560  __kmp_i18n_default_table.sect[section].size + 2,
561  sizeof(char const *));
562  table.sect[section].size =
563  __kmp_i18n_default_table.sect[section].size;
564  }; // if
565  if (table.sect[section].str[number] == NULL) {
566  table.sect[section].str[number] = ___catgets(id);
567  }; // if
568  message = table.sect[section].str[number];
569  }; // if
570  if (message == NULL) {
571  // Catalog is not opened or message is not found, return default
572  // message.
573  message = __kmp_i18n_default_table.sect[section].str[number];
574  }; // if
575  }; // if
576  }; // if
577  if (message == NULL) {
578  message = no_message_available;
579  }; // if
580  return message;
581 
582 } // func __kmp_i18n_catgets
583 
584 #endif // KMP_OS_WINDOWS
585 
586 // -----------------------------------------------------------------------------
587 
588 #ifndef KMP_I18N_OK
589 #error I18n support is not implemented for this OS.
590 #endif // KMP_I18N_OK
591 
592 // -----------------------------------------------------------------------------
593 
594 void __kmp_i18n_dump_catalog(kmp_str_buf_t *buffer) {
595 
596  struct kmp_i18n_id_range_t {
597  kmp_i18n_id_t first;
598  kmp_i18n_id_t last;
599  }; // struct kmp_i18n_id_range_t
600 
601  static struct kmp_i18n_id_range_t ranges[] = {
602  {kmp_i18n_prp_first, kmp_i18n_prp_last},
603  {kmp_i18n_str_first, kmp_i18n_str_last},
604  {kmp_i18n_fmt_first, kmp_i18n_fmt_last},
605  {kmp_i18n_msg_first, kmp_i18n_msg_last},
606  {kmp_i18n_hnt_first, kmp_i18n_hnt_last}}; // ranges
607 
608  int num_of_ranges = sizeof(ranges) / sizeof(struct kmp_i18n_id_range_t);
609  int range;
610  kmp_i18n_id_t id;
611 
612  for (range = 0; range < num_of_ranges; ++range) {
613  __kmp_str_buf_print(buffer, "*** Set #%d ***\n", range + 1);
614  for (id = (kmp_i18n_id_t)(ranges[range].first + 1); id < ranges[range].last;
615  id = (kmp_i18n_id_t)(id + 1)) {
616  __kmp_str_buf_print(buffer, "%d: <<%s>>\n", id, __kmp_i18n_catgets(id));
617  }; // for id
618  }; // for range
619 
620  __kmp_printf("%s", buffer->str);
621 
622 } // __kmp_i18n_dump_catalog
623 
624 // -----------------------------------------------------------------------------
625 kmp_msg_t __kmp_msg_format(unsigned id_arg, ...) {
626 
627  kmp_msg_t msg;
628  va_list args;
629  kmp_str_buf_t buffer;
630  __kmp_str_buf_init(&buffer);
631 
632  va_start(args, id_arg);
633 
634  // We use unsigned for the ID argument and explicitly cast it here to the
635  // right enumerator because variadic functions are not compatible with
636  // default promotions.
637  kmp_i18n_id_t id = (kmp_i18n_id_t)id_arg;
638 
639 #if KMP_OS_UNIX
640  // On Linux* OS and OS X*, printf() family functions process parameter
641  // numbers, for example: "%2$s %1$s".
642  __kmp_str_buf_vprint(&buffer, __kmp_i18n_catgets(id), args);
643 #elif KMP_OS_WINDOWS
644  // On Winodws, printf() family functions does not recognize GNU style
645  // parameter numbers, so we have to use FormatMessage() instead. It recognizes
646  // parameter numbers, e. g.: "%2!s! "%1!s!".
647  {
648  LPTSTR str = NULL;
649  int len;
650  FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
651  __kmp_i18n_catgets(id), 0, 0, (LPTSTR)(&str), 0, &args);
652  len = ___strip_crs(str);
653  __kmp_str_buf_cat(&buffer, str, len);
654  LocalFree(str);
655  }
656 #else
657 #error
658 #endif
659  va_end(args);
660  __kmp_str_buf_detach(&buffer);
661 
662  msg.type = (kmp_msg_type_t)(id >> 16);
663  msg.num = id & 0xFFFF;
664  msg.str = buffer.str;
665  msg.len = buffer.used;
666 
667  return msg;
668 
669 } // __kmp_msg_format
670 
671 // -----------------------------------------------------------------------------
672 static char *sys_error(int err) {
673 
674  char *message = NULL;
675 
676 #if KMP_OS_WINDOWS
677 
678  LPVOID buffer = NULL;
679  int len;
680  DWORD rc;
681  rc = FormatMessage(
682  FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
683  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
684  (LPTSTR)&buffer, 0, NULL);
685  if (rc > 0) {
686  // Message formatted. Copy it (so we can free it later with normal free().
687  message = __kmp_str_format("%s", (char *)buffer);
688  len = ___strip_crs(message); // Delete carriage returns if any.
689  // Strip trailing newlines.
690  while (len > 0 && message[len - 1] == '\n') {
691  --len;
692  }; // while
693  message[len] = 0;
694  } else {
695  // FormatMessage() failed to format system error message. GetLastError()
696  // would give us error code, which we would convert to message... this it
697  // dangerous recursion, which cannot clarify original error, so we will not
698  // even start it.
699  }; // if
700  if (buffer != NULL) {
701  LocalFree(buffer);
702  }; // if
703 
704 #else // Non-Windows* OS: Linux* OS or OS X*
705 
706 /* There are 2 incompatible versions of strerror_r:
707 
708  char * strerror_r( int, char *, size_t ); // GNU version
709  int strerror_r( int, char *, size_t ); // XSI version
710 */
711 
712 #if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || \
713  (defined(__BIONIC__) && defined(_GNU_SOURCE) && \
714  __ANDROID_API__ >= __ANDROID_API_M__)
715  // GNU version of strerror_r.
716 
717  char buffer[2048];
718  char *const err_msg = strerror_r(err, buffer, sizeof(buffer));
719  // Do not eliminate this assignment to temporary variable, otherwise compiler
720  // would not issue warning if strerror_r() returns `int' instead of expected
721  // `char *'.
722  message = __kmp_str_format("%s", err_msg);
723 
724 #else // OS X*, FreeBSD* etc.
725  // XSI version of strerror_r.
726  int size = 2048;
727  char *buffer = (char *)KMP_INTERNAL_MALLOC(size);
728  int rc;
729  if (buffer == NULL) {
730  KMP_FATAL(MemoryAllocFailed);
731  }
732  rc = strerror_r(err, buffer, size);
733  if (rc == -1) {
734  rc = errno; // XSI version sets errno.
735  }; // if
736  while (rc == ERANGE) { // ERANGE means the buffer is too small.
737  KMP_INTERNAL_FREE(buffer);
738  size *= 2;
739  buffer = (char *)KMP_INTERNAL_MALLOC(size);
740  if (buffer == NULL) {
741  KMP_FATAL(MemoryAllocFailed);
742  }
743  rc = strerror_r(err, buffer, size);
744  if (rc == -1) {
745  rc = errno; // XSI version sets errno.
746  }; // if
747  }; // while
748  if (rc == 0) {
749  message = buffer;
750  } else { // Buffer is unused. Free it.
751  KMP_INTERNAL_FREE(buffer);
752  }; // if
753 
754 #endif
755 
756 #endif /* KMP_OS_WINDOWS */
757 
758  if (message == NULL) {
759  // TODO: I18n this message.
760  message = __kmp_str_format("%s", "(No system error message available)");
761  }; // if
762  return message;
763 } // sys_error
764 
765 // -----------------------------------------------------------------------------
766 kmp_msg_t __kmp_msg_error_code(int code) {
767 
768  kmp_msg_t msg;
769  msg.type = kmp_mt_syserr;
770  msg.num = code;
771  msg.str = sys_error(code);
772  msg.len = KMP_STRLEN(msg.str);
773  return msg;
774 
775 } // __kmp_msg_error_code
776 
777 // -----------------------------------------------------------------------------
778 kmp_msg_t __kmp_msg_error_mesg(char const *mesg) {
779 
780  kmp_msg_t msg;
781  msg.type = kmp_mt_syserr;
782  msg.num = 0;
783  msg.str = __kmp_str_format("%s", mesg);
784  msg.len = KMP_STRLEN(msg.str);
785  return msg;
786 
787 } // __kmp_msg_error_mesg
788 
789 // -----------------------------------------------------------------------------
790 void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, ...) {
791 
792  va_list args;
793  kmp_i18n_id_t format; // format identifier
794  kmp_msg_t fmsg; // formatted message
795  kmp_str_buf_t buffer;
796 
797  if (severity != kmp_ms_fatal && __kmp_generate_warnings == kmp_warnings_off)
798  return; // no reason to form a string in order to not print it
799 
800  __kmp_str_buf_init(&buffer);
801 
802  // Format the primary message.
803  switch (severity) {
804  case kmp_ms_inform: {
805  format = kmp_i18n_fmt_Info;
806  } break;
807  case kmp_ms_warning: {
808  format = kmp_i18n_fmt_Warning;
809  } break;
810  case kmp_ms_fatal: {
811  format = kmp_i18n_fmt_Fatal;
812  } break;
813  default: { KMP_DEBUG_ASSERT(0); };
814  }; // switch
815  fmsg = __kmp_msg_format(format, message.num, message.str);
816  __kmp_str_free(&message.str);
817  __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len);
818  __kmp_str_free(&fmsg.str);
819 
820  // Format other messages.
821  va_start(args, message);
822  for (;;) {
823  message = va_arg(args, kmp_msg_t);
824  if (message.type == kmp_mt_dummy && message.str == NULL) {
825  break;
826  }; // if
827  if (message.type == kmp_mt_dummy && message.str == __kmp_msg_empty.str) {
828  continue;
829  }; // if
830  switch (message.type) {
831  case kmp_mt_hint: {
832  format = kmp_i18n_fmt_Hint;
833  } break;
834  case kmp_mt_syserr: {
835  format = kmp_i18n_fmt_SysErr;
836  } break;
837  default: { KMP_DEBUG_ASSERT(0); };
838  }; // switch
839  fmsg = __kmp_msg_format(format, message.num, message.str);
840  __kmp_str_free(&message.str);
841  __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len);
842  __kmp_str_free(&fmsg.str);
843  }; // forever
844  va_end(args);
845 
846  // Print formatted messages.
847  // This lock prevents multiple fatal errors on the same problem.
848  // __kmp_acquire_bootstrap_lock( & lock ); // GEH - This lock causing tests
849  // to hang on OS X*.
850  __kmp_printf("%s", buffer.str);
851  __kmp_str_buf_free(&buffer);
852 
853  if (severity == kmp_ms_fatal) {
854 #if KMP_OS_WINDOWS
855  __kmp_thread_sleep(
856  500); /* Delay to give message a chance to appear before reaping */
857 #endif
858  __kmp_abort_process();
859  }; // if
860 
861  // __kmp_release_bootstrap_lock( & lock ); // GEH - this lock causing tests
862  // to hang on OS X*.
863 
864 } // __kmp_msg
865 
866 // end of file //