]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/openmp/runtime/src/kmp_i18n.cpp
Merge ^/vendor/clang/dist up to its last change, and resolve conflicts.
[FreeBSD/FreeBSD.git] / contrib / llvm-project / openmp / runtime / src / 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 //