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