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 static void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message,
42                       va_list ap);
43 
44 enum kmp_i18n_cat_status {
45   KMP_I18N_CLOSED, // Not yet opened or closed.
46   KMP_I18N_OPENED, // Opened successfully, ready to use.
47   KMP_I18N_ABSENT // Opening failed, message catalog should not be used.
48 }; // enum kmp_i18n_cat_status
49 typedef enum kmp_i18n_cat_status kmp_i18n_cat_status_t;
50 static volatile kmp_i18n_cat_status_t status = KMP_I18N_CLOSED;
51 
52 /* Message catalog is opened at first usage, so we have to synchronize opening
53    to avoid race and multiple openings.
54 
55    Closing does not require synchronization, because catalog is closed very late
56    at library shutting down, when no other threads are alive.  */
57 
58 static void __kmp_i18n_do_catopen();
59 static kmp_bootstrap_lock_t lock = KMP_BOOTSTRAP_LOCK_INITIALIZER(lock);
60 // `lock' variable may be placed into __kmp_i18n_catopen function because it is
61 // used only by that function. But we afraid a (buggy) compiler may treat it
62 // wrongly. So we put it outside of function just in case.
63 
64 void __kmp_i18n_catopen() {
65   if (status == KMP_I18N_CLOSED) {
66     __kmp_acquire_bootstrap_lock(&lock);
67     if (status == KMP_I18N_CLOSED) {
68       __kmp_i18n_do_catopen();
69     }
70     __kmp_release_bootstrap_lock(&lock);
71   }
72 } // func __kmp_i18n_catopen
73 
74 /* Linux* OS and OS X* part */
75 #if KMP_OS_UNIX
76 #define KMP_I18N_OK
77 
78 #include <nl_types.h>
79 
80 #define KMP_I18N_NULLCAT ((nl_catd)(-1))
81 static nl_catd cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?
82 static char const *name =
83     (KMP_VERSION_MAJOR == 4 ? "libguide.cat" : "libomp.cat");
84 
85 /* Useful links:
86 http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html#tag_08_02
87 http://www.opengroup.org/onlinepubs/000095399/functions/catopen.html
88 http://www.opengroup.org/onlinepubs/000095399/functions/setlocale.html
89 */
90 
91 void __kmp_i18n_do_catopen() {
92   int english = 0;
93   char *lang = __kmp_env_get("LANG");
94   // TODO: What about LC_ALL or LC_MESSAGES?
95 
96   KMP_DEBUG_ASSERT(status == KMP_I18N_CLOSED);
97   KMP_DEBUG_ASSERT(cat == KMP_I18N_NULLCAT);
98 
99   english = lang == NULL || // In all these cases English language is used.
100             strcmp(lang, "") == 0 || strcmp(lang, " ") == 0 ||
101             // Workaround for Fortran RTL bug DPD200137873 "Fortran runtime
102             // resets LANG env var to space if it is not set".
103             strcmp(lang, "C") == 0 || strcmp(lang, "POSIX") == 0;
104 
105   if (!english) { // English language is not yet detected, let us continue.
106     // Format of LANG is: [language[_territory][.codeset][@modifier]]
107     // Strip all parts except language.
108     char *tail = NULL;
109     __kmp_str_split(lang, '@', &lang, &tail);
110     __kmp_str_split(lang, '.', &lang, &tail);
111     __kmp_str_split(lang, '_', &lang, &tail);
112     english = (strcmp(lang, "en") == 0);
113   }
114 
115   KMP_INTERNAL_FREE(lang);
116 
117   // Do not try to open English catalog because internal messages are
118   // exact copy of messages in English catalog.
119   if (english) {
120     status = KMP_I18N_ABSENT; // mark catalog as absent so it will not
121     // be re-opened.
122     return;
123   }
124 
125   cat = catopen(name, 0);
126   // TODO: Why do we pass 0 in flags?
127   status = (cat == KMP_I18N_NULLCAT ? KMP_I18N_ABSENT : KMP_I18N_OPENED);
128 
129   if (status == KMP_I18N_ABSENT) {
130     if (__kmp_generate_warnings > kmp_warnings_low) {
131       // AC: only issue warning in case explicitly asked to
132       int error = errno; // Save errno immediately.
133       char *nlspath = __kmp_env_get("NLSPATH");
134       char *lang = __kmp_env_get("LANG");
135 
136       // Infinite recursion will not occur -- status is KMP_I18N_ABSENT now, so
137       // __kmp_i18n_catgets() will not try to open catalog, but will return
138       // default message.
139       kmp_msg_t err_code = KMP_ERR(error);
140       __kmp_msg(kmp_ms_warning, KMP_MSG(CantOpenMessageCatalog, name), err_code,
141                 KMP_HNT(CheckEnvVar, "NLSPATH", nlspath),
142                 KMP_HNT(CheckEnvVar, "LANG", lang), __kmp_msg_null);
143       if (__kmp_generate_warnings == kmp_warnings_off) {
144         __kmp_str_free(&err_code.str);
145       }
146 
147       KMP_INFORM(WillUseDefaultMessages);
148       KMP_INTERNAL_FREE(nlspath);
149       KMP_INTERNAL_FREE(lang);
150     }
151   } else { // status == KMP_I18N_OPENED
152     int section = get_section(kmp_i18n_prp_Version);
153     int number = get_number(kmp_i18n_prp_Version);
154     char const *expected = __kmp_i18n_default_table.sect[section].str[number];
155     // Expected version of the catalog.
156     kmp_str_buf_t version; // Actual version of the catalog.
157     __kmp_str_buf_init(&version);
158     __kmp_str_buf_print(&version, "%s", catgets(cat, section, number, NULL));
159 
160     // String returned by catgets is invalid after closing catalog, so copy it.
161     if (strcmp(version.str, expected) != 0) {
162       __kmp_i18n_catclose(); // Close bad catalog.
163       status = KMP_I18N_ABSENT; // And mark it as absent.
164       if (__kmp_generate_warnings > kmp_warnings_low) {
165         // AC: only issue warning in case explicitly asked to
166         // And now print a warning using default messages.
167         char const *name = "NLSPATH";
168         char const *nlspath = __kmp_env_get(name);
169         __kmp_msg(kmp_ms_warning,
170                   KMP_MSG(WrongMessageCatalog, name, version.str, expected),
171                   KMP_HNT(CheckEnvVar, name, nlspath), __kmp_msg_null);
172         KMP_INFORM(WillUseDefaultMessages);
173         KMP_INTERNAL_FREE(CCAST(char *, nlspath));
174       } // __kmp_generate_warnings
175     }
176     __kmp_str_buf_free(&version);
177   }
178 } // func __kmp_i18n_do_catopen
179 
180 void __kmp_i18n_catclose() {
181   if (status == KMP_I18N_OPENED) {
182     KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
183     catclose(cat);
184     cat = KMP_I18N_NULLCAT;
185   }
186   status = KMP_I18N_CLOSED;
187 } // func __kmp_i18n_catclose
188 
189 char const *__kmp_i18n_catgets(kmp_i18n_id_t id) {
190 
191   int section = get_section(id);
192   int number = get_number(id);
193   char const *message = NULL;
194 
195   if (1 <= section && section <= __kmp_i18n_default_table.size) {
196     if (1 <= number && number <= __kmp_i18n_default_table.sect[section].size) {
197       if (status == KMP_I18N_CLOSED) {
198         __kmp_i18n_catopen();
199       }
200       if (status == KMP_I18N_OPENED) {
201         message = catgets(cat, section, number,
202                           __kmp_i18n_default_table.sect[section].str[number]);
203       }
204       if (message == NULL) {
205         message = __kmp_i18n_default_table.sect[section].str[number];
206       }
207     }
208   }
209   if (message == NULL) {
210     message = no_message_available;
211   }
212   return message;
213 
214 } // func __kmp_i18n_catgets
215 
216 #endif // KMP_OS_UNIX
217 
218 /* Windows* OS part. */
219 
220 #if KMP_OS_WINDOWS
221 #define KMP_I18N_OK
222 
223 #include "kmp_environment.h"
224 #include <windows.h>
225 
226 #define KMP_I18N_NULLCAT NULL
227 static HMODULE cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?
228 static char const *name =
229     (KMP_VERSION_MAJOR == 4 ? "libguide40ui.dll" : "libompui.dll");
230 
231 static kmp_i18n_table_t table = {0, NULL};
232 // Messages formatted by FormatMessage() should be freed, but catgets()
233 // interface assumes user will not free messages. So we cache all the retrieved
234 // messages in the table, which are freed at catclose().
235 static UINT const default_code_page = CP_OEMCP;
236 static UINT code_page = default_code_page;
237 
238 static char const *___catgets(kmp_i18n_id_t id);
239 static UINT get_code_page();
240 static void kmp_i18n_table_free(kmp_i18n_table_t *table);
241 
242 static UINT get_code_page() {
243 
244   UINT cp = default_code_page;
245   char const *value = __kmp_env_get("KMP_CODEPAGE");
246   if (value != NULL) {
247     if (_stricmp(value, "ANSI") == 0) {
248       cp = CP_ACP;
249     } else if (_stricmp(value, "OEM") == 0) {
250       cp = CP_OEMCP;
251     } else if (_stricmp(value, "UTF-8") == 0 || _stricmp(value, "UTF8") == 0) {
252       cp = CP_UTF8;
253     } else if (_stricmp(value, "UTF-7") == 0 || _stricmp(value, "UTF7") == 0) {
254       cp = CP_UTF7;
255     } else {
256       // !!! TODO: Issue a warning?
257     }
258   }
259   KMP_INTERNAL_FREE((void *)value);
260   return cp;
261 
262 } // func get_code_page
263 
264 static void kmp_i18n_table_free(kmp_i18n_table_t *table) {
265   int s;
266   int m;
267   for (s = 0; s < table->size; ++s) {
268     for (m = 0; m < table->sect[s].size; ++m) {
269       // Free message.
270       KMP_INTERNAL_FREE((void *)table->sect[s].str[m]);
271       table->sect[s].str[m] = NULL;
272     }
273     table->sect[s].size = 0;
274     // Free section itself.
275     KMP_INTERNAL_FREE((void *)table->sect[s].str);
276     table->sect[s].str = NULL;
277   }
278   table->size = 0;
279   KMP_INTERNAL_FREE((void *)table->sect);
280   table->sect = NULL;
281 } // kmp_i18n_table_free
282 
283 void __kmp_i18n_do_catopen() {
284 
285   LCID locale_id = GetThreadLocale();
286   WORD lang_id = LANGIDFROMLCID(locale_id);
287   WORD primary_lang_id = PRIMARYLANGID(lang_id);
288   kmp_str_buf_t path;
289 
290   KMP_DEBUG_ASSERT(status == KMP_I18N_CLOSED);
291   KMP_DEBUG_ASSERT(cat == KMP_I18N_NULLCAT);
292 
293   __kmp_str_buf_init(&path);
294 
295   // Do not try to open English catalog because internal messages are exact copy
296   // of messages in English catalog.
297   if (primary_lang_id == LANG_ENGLISH) {
298     status = KMP_I18N_ABSENT; // mark catalog as absent so it will not
299     // be re-opened.
300     goto end;
301   }
302 
303   // Construct resource DLL name.
304   /* Simple LoadLibrary( name ) is not suitable due to security issue (see
305      http://www.microsoft.com/technet/security/advisory/2269637.mspx). We have
306      to specify full path to the message catalog.  */
307   {
308     // Get handle of our DLL first.
309     HMODULE handle;
310     BOOL brc = GetModuleHandleEx(
311         GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
312             GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
313         reinterpret_cast<LPCSTR>(&__kmp_i18n_do_catopen), &handle);
314     if (!brc) { // Error occurred.
315       status = KMP_I18N_ABSENT; // mark catalog as absent so it will not be
316       // re-opened.
317       goto end;
318       // TODO: Enable multiple messages (KMP_MSG) to be passed to __kmp_msg; and
319       // print a proper warning.
320     }
321 
322     // Now get path to the our DLL.
323     for (;;) {
324       DWORD drc = GetModuleFileName(handle, path.str, path.size);
325       if (drc == 0) { // Error occurred.
326         status = KMP_I18N_ABSENT;
327         goto end;
328       }
329       if (drc < path.size) {
330         path.used = drc;
331         break;
332       }
333       __kmp_str_buf_reserve(&path, path.size * 2);
334     }
335 
336     // Now construct the name of message catalog.
337     kmp_str_fname fname;
338     __kmp_str_fname_init(&fname, path.str);
339     __kmp_str_buf_clear(&path);
340     __kmp_str_buf_print(&path, "%s%lu/%s", fname.dir,
341                         (unsigned long)(locale_id), name);
342     __kmp_str_fname_free(&fname);
343   }
344 
345   // For security reasons, use LoadLibraryEx() and load message catalog as a
346   // data file.
347   cat = LoadLibraryEx(path.str, NULL, LOAD_LIBRARY_AS_DATAFILE);
348   status = (cat == KMP_I18N_NULLCAT ? KMP_I18N_ABSENT : KMP_I18N_OPENED);
349 
350   if (status == KMP_I18N_ABSENT) {
351     if (__kmp_generate_warnings > kmp_warnings_low) {
352       // AC: only issue warning in case explicitly asked to
353       DWORD error = GetLastError();
354       // Infinite recursion will not occur -- status is KMP_I18N_ABSENT now, so
355       // __kmp_i18n_catgets() will not try to open catalog but will return
356       // default message.
357       /* If message catalog for another architecture found (e.g. OpenMP RTL for
358          IA-32 architecture opens libompui.dll for Intel(R) 64) Windows* OS
359          returns error 193 (ERROR_BAD_EXE_FORMAT). However, FormatMessage fails
360          to return a message for this error, so user will see:
361 
362          OMP: Warning #2: Cannot open message catalog "1041\libompui.dll":
363          OMP: System error #193: (No system error message available)
364          OMP: Info #3: Default messages will be used.
365 
366          Issue hint in this case so cause of trouble is more understandable. */
367       kmp_msg_t err_code = KMP_SYSERRCODE(error);
368       __kmp_msg(kmp_ms_warning, KMP_MSG(CantOpenMessageCatalog, path.str),
369                 err_code, (error == ERROR_BAD_EXE_FORMAT
370                                ? KMP_HNT(BadExeFormat, path.str, KMP_ARCH_STR)
371                                : __kmp_msg_null),
372                 __kmp_msg_null);
373       if (__kmp_generate_warnings == kmp_warnings_off) {
374         __kmp_str_free(&err_code.str);
375       }
376       KMP_INFORM(WillUseDefaultMessages);
377     }
378   } else { // status == KMP_I18N_OPENED
379 
380     int section = get_section(kmp_i18n_prp_Version);
381     int number = get_number(kmp_i18n_prp_Version);
382     char const *expected = __kmp_i18n_default_table.sect[section].str[number];
383     kmp_str_buf_t version; // Actual version of the catalog.
384     __kmp_str_buf_init(&version);
385     __kmp_str_buf_print(&version, "%s", ___catgets(kmp_i18n_prp_Version));
386     // String returned by catgets is invalid after closing catalog, so copy it.
387     if (strcmp(version.str, expected) != 0) {
388       // Close bad catalog.
389       __kmp_i18n_catclose();
390       status = KMP_I18N_ABSENT; // And mark it as absent.
391       if (__kmp_generate_warnings > kmp_warnings_low) {
392         // And now print a warning using default messages.
393         __kmp_msg(kmp_ms_warning,
394                   KMP_MSG(WrongMessageCatalog, path.str, version.str, expected),
395                   __kmp_msg_null);
396         KMP_INFORM(WillUseDefaultMessages);
397       } // __kmp_generate_warnings
398     }
399     __kmp_str_buf_free(&version);
400   }
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   }
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     }
453     if (str[in] == 0) {
454       break;
455     }
456     ++in;
457   }
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   }
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   }
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   }
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   }
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   }
533   if (wmsg != NULL) {
534     LocalFree(wmsg);
535   }
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       }
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         }
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         }
565         if (table.sect[section].str[number] == NULL) {
566           table.sect[section].str[number] = ___catgets(id);
567         }
568         message = table.sect[section].str[number];
569       }
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       }
575     }
576   }
577   if (message == NULL) {
578     message = no_message_available;
579   }
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     }
618   }
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     }
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   }
700   if (buffer != NULL) {
701     LocalFree(buffer);
702   }
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   }
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     }
747   }
748   if (rc == 0) {
749     message = buffer;
750   } else { // Buffer is unused. Free it.
751     KMP_INTERNAL_FREE(buffer);
752   }
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   }
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, va_list args) {
791   kmp_i18n_id_t format; // format identifier
792   kmp_msg_t fmsg; // formatted message
793   kmp_str_buf_t buffer;
794 
795   if (severity != kmp_ms_fatal && __kmp_generate_warnings == kmp_warnings_off)
796     return; // no reason to form a string in order to not print it
797 
798   __kmp_str_buf_init(&buffer);
799 
800   // Format the primary message.
801   switch (severity) {
802   case kmp_ms_inform: {
803     format = kmp_i18n_fmt_Info;
804   } break;
805   case kmp_ms_warning: {
806     format = kmp_i18n_fmt_Warning;
807   } break;
808   case kmp_ms_fatal: {
809     format = kmp_i18n_fmt_Fatal;
810   } break;
811   default: { KMP_DEBUG_ASSERT(0); }
812   }
813   fmsg = __kmp_msg_format(format, message.num, message.str);
814   __kmp_str_free(&message.str);
815   __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len);
816   __kmp_str_free(&fmsg.str);
817 
818   // Format other messages.
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 
842   // Print formatted messages.
843   // This lock prevents multiple fatal errors on the same problem.
844   // __kmp_acquire_bootstrap_lock( & lock );    // GEH - This lock causing tests
845   // to hang on OS X*.
846   __kmp_printf("%s", buffer.str);
847   __kmp_str_buf_free(&buffer);
848 
849   // __kmp_release_bootstrap_lock( & lock );  // GEH - this lock causing tests
850   // to hang on OS X*.
851 
852 } // __kmp_msg
853 
854 void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, ...) {
855   va_list args;
856   va_start(args, message);
857   __kmp_msg(severity, message, args);
858   va_end(args);
859 }
860 
861 void __kmp_fatal(kmp_msg_t message, ...) {
862   va_list args;
863   va_start(args, message);
864   __kmp_msg(kmp_ms_fatal, message, args);
865   va_end(args);
866 #if KMP_OS_WINDOWS
867   // Delay to give message a chance to appear before reaping
868   __kmp_thread_sleep(500);
869 #endif
870   __kmp_abort_process();
871 } // __kmp_fatal
872 
873 // end of file //
874