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 //
867