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_null = {kmp_mt_dummy, 0, NULL, 0};
38 static char const *no_message_available = "(No message available)";
39 
40 static void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message,
41                       va_list ap);
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 
__kmp_i18n_catopen()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     }
69     __kmp_release_bootstrap_lock(&lock);
70   }
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 
__kmp_i18n_do_catopen()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   }
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     }
175     __kmp_str_buf_free(&version);
176   }
177 } // func __kmp_i18n_do_catopen
178 
__kmp_i18n_catclose()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   }
185   status = KMP_I18N_CLOSED;
186 } // func __kmp_i18n_catclose
187 
__kmp_i18n_catgets(kmp_i18n_id_t id)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       }
199       if (status == KMP_I18N_OPENED) {
200         message = catgets(cat, section, number,
201                           __kmp_i18n_default_table.sect[section].str[number]);
202       }
203       if (message == NULL) {
204         message = __kmp_i18n_default_table.sect[section].str[number];
205       }
206     }
207   }
208   if (message == NULL) {
209     message = no_message_available;
210   }
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 
get_code_page()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     }
257   }
258   KMP_INTERNAL_FREE((void *)value);
259   return cp;
260 
261 } // func get_code_page
262 
kmp_i18n_table_free(kmp_i18n_table_t * table)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     }
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   }
277   table->size = 0;
278   KMP_INTERNAL_FREE((void *)table->sect);
279   table->sect = NULL;
280 } // kmp_i18n_table_free
281 
__kmp_i18n_do_catopen()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   }
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     }
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       }
328       if (drc < path.size) {
329         path.used = drc;
330         break;
331       }
332       __kmp_str_buf_reserve(&path, path.size * 2);
333     }
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     }
398     __kmp_str_buf_free(&version);
399   }
400   code_page = get_code_page();
401 
402 end:
403   __kmp_str_buf_free(&path);
404   return;
405 } // func __kmp_i18n_do_catopen
406 
__kmp_i18n_catclose()407 void __kmp_i18n_catclose() {
408   if (status == KMP_I18N_OPENED) {
409     KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
410     kmp_i18n_table_free(&table);
411     FreeLibrary(cat);
412     cat = KMP_I18N_NULLCAT;
413   }
414   code_page = default_code_page;
415   status = KMP_I18N_CLOSED;
416 } // func __kmp_i18n_catclose
417 
418 /* We use FormatMessage() to get strings from catalog, get system error
419    messages, etc. FormatMessage() tends to return Windows* OS-style
420    end-of-lines, "\r\n". When string is printed, printf() also replaces all the
421    occurrences of "\n" with "\r\n" (again!), so sequences like "\r\r\r\n"
422    appear in output. It is not too good.
423 
424    Additional mess comes from message catalog: Our catalog source en_US.mc file
425    (generated by message-converter.pl) contains only "\n" characters, but
426    en_US_msg_1033.bin file (produced by mc.exe) may contain "\r\n" or just "\n".
427    This mess goes from en_US_msg_1033.bin file to message catalog,
428    libompui.dll. For example, message
429 
430    Error
431 
432    (there is "\n" at the end) is compiled by mc.exe to "Error\r\n", while
433 
434    OMP: Error %1!d!: %2!s!\n
435 
436    (there is "\n" at the end as well) is compiled to "OMP: Error %1!d!:
437    %2!s!\r\n\n".
438 
439    Thus, stripping all "\r" normalizes string and returns it to canonical form,
440    so printf() will produce correct end-of-line sequences.
441 
442    ___strip_crs() serves for this purpose: it removes all the occurrences of
443    "\r" in-place and returns new length of string.  */
___strip_crs(char * str)444 static int ___strip_crs(char *str) {
445   int in = 0; // Input character index.
446   int out = 0; // Output character index.
447   for (;;) {
448     if (str[in] != '\r') {
449       str[out] = str[in];
450       ++out;
451     }
452     if (str[in] == 0) {
453       break;
454     }
455     ++in;
456   }
457   return out - 1;
458 } // func __strip_crs
459 
___catgets(kmp_i18n_id_t id)460 static char const *___catgets(kmp_i18n_id_t id) {
461 
462   char *result = NULL;
463   PVOID addr = NULL;
464   wchar_t *wmsg = NULL;
465   DWORD wlen = 0;
466   char *msg = NULL;
467   int len = 0;
468   int rc;
469 
470   KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
471   wlen = // wlen does *not* include terminating null.
472       FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
473                          FORMAT_MESSAGE_FROM_HMODULE |
474                          FORMAT_MESSAGE_IGNORE_INSERTS,
475                      cat, id,
476                      0, // LangId
477                      (LPWSTR)&addr,
478                      0, // Size in elements, not in bytes.
479                      NULL);
480   if (wlen <= 0) {
481     goto end;
482   }
483   wmsg = (wchar_t *)addr; // Warning: wmsg may be not nul-terminated!
484 
485   // Calculate length of multibyte message.
486   // Since wlen does not include terminating null, len does not include it also.
487   len = WideCharToMultiByte(code_page,
488                             0, // Flags.
489                             wmsg, wlen, // Wide buffer and size.
490                             NULL, 0, // Buffer and size.
491                             NULL, NULL // Default char and used default char.
492                             );
493   if (len <= 0) {
494     goto end;
495   }
496 
497   // Allocate memory.
498   msg = (char *)KMP_INTERNAL_MALLOC(len + 1);
499 
500   // Convert wide message to multibyte one.
501   rc = WideCharToMultiByte(code_page,
502                            0, // Flags.
503                            wmsg, wlen, // Wide buffer and size.
504                            msg, len, // Buffer and size.
505                            NULL, NULL // Default char and used default char.
506                            );
507   if (rc <= 0 || rc > len) {
508     goto end;
509   }
510   KMP_DEBUG_ASSERT(rc == len);
511   len = rc;
512   msg[len] = 0; // Put terminating null to the end.
513 
514   // Stripping all "\r" before stripping last end-of-line simplifies the task.
515   len = ___strip_crs(msg);
516 
517   // Every message in catalog is terminated with "\n". Strip it.
518   if (len >= 1 && msg[len - 1] == '\n') {
519     --len;
520     msg[len] = 0;
521   }
522 
523   // Everything looks ok.
524   result = msg;
525   msg = NULL;
526 
527 end:
528 
529   if (msg != NULL) {
530     KMP_INTERNAL_FREE(msg);
531   }
532   if (wmsg != NULL) {
533     LocalFree(wmsg);
534   }
535 
536   return result;
537 
538 } // ___catgets
539 
__kmp_i18n_catgets(kmp_i18n_id_t id)540 char const *__kmp_i18n_catgets(kmp_i18n_id_t id) {
541 
542   int section = get_section(id);
543   int number = get_number(id);
544   char const *message = NULL;
545 
546   if (1 <= section && section <= __kmp_i18n_default_table.size) {
547     if (1 <= number && number <= __kmp_i18n_default_table.sect[section].size) {
548       if (status == KMP_I18N_CLOSED) {
549         __kmp_i18n_catopen();
550       }
551       if (cat != KMP_I18N_NULLCAT) {
552         if (table.size == 0) {
553           table.sect = (kmp_i18n_section_t *)KMP_INTERNAL_CALLOC(
554               (__kmp_i18n_default_table.size + 2), sizeof(kmp_i18n_section_t));
555           table.size = __kmp_i18n_default_table.size;
556         }
557         if (table.sect[section].size == 0) {
558           table.sect[section].str = (const char **)KMP_INTERNAL_CALLOC(
559               __kmp_i18n_default_table.sect[section].size + 2,
560               sizeof(char const *));
561           table.sect[section].size =
562               __kmp_i18n_default_table.sect[section].size;
563         }
564         if (table.sect[section].str[number] == NULL) {
565           table.sect[section].str[number] = ___catgets(id);
566         }
567         message = table.sect[section].str[number];
568       }
569       if (message == NULL) {
570         // Catalog is not opened or message is not found, return default
571         // message.
572         message = __kmp_i18n_default_table.sect[section].str[number];
573       }
574     }
575   }
576   if (message == NULL) {
577     message = no_message_available;
578   }
579   return message;
580 
581 } // func __kmp_i18n_catgets
582 
583 #endif // KMP_OS_WINDOWS
584 
585 // -----------------------------------------------------------------------------
586 
587 #ifndef KMP_I18N_OK
588 #error I18n support is not implemented for this OS.
589 #endif // KMP_I18N_OK
590 
591 // -----------------------------------------------------------------------------
592 
__kmp_i18n_dump_catalog(kmp_str_buf_t * buffer)593 void __kmp_i18n_dump_catalog(kmp_str_buf_t *buffer) {
594 
595   struct kmp_i18n_id_range_t {
596     kmp_i18n_id_t first;
597     kmp_i18n_id_t last;
598   }; // struct kmp_i18n_id_range_t
599 
600   static struct kmp_i18n_id_range_t ranges[] = {
601       {kmp_i18n_prp_first, kmp_i18n_prp_last},
602       {kmp_i18n_str_first, kmp_i18n_str_last},
603       {kmp_i18n_fmt_first, kmp_i18n_fmt_last},
604       {kmp_i18n_msg_first, kmp_i18n_msg_last},
605       {kmp_i18n_hnt_first, kmp_i18n_hnt_last}}; // ranges
606 
607   int num_of_ranges = sizeof(ranges) / sizeof(struct kmp_i18n_id_range_t);
608   int range;
609   kmp_i18n_id_t id;
610 
611   for (range = 0; range < num_of_ranges; ++range) {
612     __kmp_str_buf_print(buffer, "*** Set #%d ***\n", range + 1);
613     for (id = (kmp_i18n_id_t)(ranges[range].first + 1); id < ranges[range].last;
614          id = (kmp_i18n_id_t)(id + 1)) {
615       __kmp_str_buf_print(buffer, "%d: <<%s>>\n", id, __kmp_i18n_catgets(id));
616     }
617   }
618 
619   __kmp_printf("%s", buffer->str);
620 
621 } // __kmp_i18n_dump_catalog
622 
623 // -----------------------------------------------------------------------------
__kmp_msg_format(unsigned id_arg,...)624 kmp_msg_t __kmp_msg_format(unsigned id_arg, ...) {
625 
626   kmp_msg_t msg;
627   va_list args;
628   kmp_str_buf_t buffer;
629   __kmp_str_buf_init(&buffer);
630 
631   va_start(args, id_arg);
632 
633   // We use unsigned for the ID argument and explicitly cast it here to the
634   // right enumerator because variadic functions are not compatible with
635   // default promotions.
636   kmp_i18n_id_t id = (kmp_i18n_id_t)id_arg;
637 
638 #if KMP_OS_UNIX
639   // On Linux* OS and OS X*, printf() family functions process parameter
640   // numbers, for example:  "%2$s %1$s".
641   __kmp_str_buf_vprint(&buffer, __kmp_i18n_catgets(id), args);
642 #elif KMP_OS_WINDOWS
643   // On Winodws, printf() family functions does not recognize GNU style
644   // parameter numbers, so we have to use FormatMessage() instead. It recognizes
645   // parameter numbers, e. g.:  "%2!s! "%1!s!".
646   {
647     LPTSTR str = NULL;
648     int len;
649     FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
650                   __kmp_i18n_catgets(id), 0, 0, (LPTSTR)(&str), 0, &args);
651     len = ___strip_crs(str);
652     __kmp_str_buf_cat(&buffer, str, len);
653     LocalFree(str);
654   }
655 #else
656 #error
657 #endif
658   va_end(args);
659   __kmp_str_buf_detach(&buffer);
660 
661   msg.type = (kmp_msg_type_t)(id >> 16);
662   msg.num = id & 0xFFFF;
663   msg.str = buffer.str;
664   msg.len = buffer.used;
665 
666   return msg;
667 
668 } // __kmp_msg_format
669 
670 // -----------------------------------------------------------------------------
sys_error(int err)671 static char *sys_error(int err) {
672 
673   char *message = NULL;
674 
675 #if KMP_OS_WINDOWS
676 
677   LPVOID buffer = NULL;
678   int len;
679   DWORD rc;
680   rc = FormatMessage(
681       FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
682       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
683       (LPTSTR)&buffer, 0, NULL);
684   if (rc > 0) {
685     // Message formatted. Copy it (so we can free it later with normal free().
686     message = __kmp_str_format("%s", (char *)buffer);
687     len = ___strip_crs(message); // Delete carriage returns if any.
688     // Strip trailing newlines.
689     while (len > 0 && message[len - 1] == '\n') {
690       --len;
691     }
692     message[len] = 0;
693   } else {
694     // FormatMessage() failed to format system error message. GetLastError()
695     // would give us error code, which we would convert to message... this it
696     // dangerous recursion, which cannot clarify original error, so we will not
697     // even start it.
698   }
699   if (buffer != NULL) {
700     LocalFree(buffer);
701   }
702 
703 #else // Non-Windows* OS: Linux* OS or OS X*
704 
705 /* There are 2 incompatible versions of strerror_r:
706 
707    char * strerror_r( int, char *, size_t );  // GNU version
708    int    strerror_r( int, char *, size_t );  // XSI version
709 */
710 
711 #if (defined(__GLIBC__) && defined(_GNU_SOURCE)) ||                            \
712     (defined(__BIONIC__) && defined(_GNU_SOURCE) &&                            \
713      __ANDROID_API__ >= __ANDROID_API_M__)
714   // GNU version of strerror_r.
715 
716   char buffer[2048];
717   char *const err_msg = strerror_r(err, buffer, sizeof(buffer));
718   // Do not eliminate this assignment to temporary variable, otherwise compiler
719   // would not issue warning if strerror_r() returns `int' instead of expected
720   // `char *'.
721   message = __kmp_str_format("%s", err_msg);
722 
723 #else // OS X*, FreeBSD* etc.
724   // XSI version of strerror_r.
725   int size = 2048;
726   char *buffer = (char *)KMP_INTERNAL_MALLOC(size);
727   int rc;
728   if (buffer == NULL) {
729     KMP_FATAL(MemoryAllocFailed);
730   }
731   rc = strerror_r(err, buffer, size);
732   if (rc == -1) {
733     rc = errno; // XSI version sets errno.
734   }
735   while (rc == ERANGE) { // ERANGE means the buffer is too small.
736     KMP_INTERNAL_FREE(buffer);
737     size *= 2;
738     buffer = (char *)KMP_INTERNAL_MALLOC(size);
739     if (buffer == NULL) {
740       KMP_FATAL(MemoryAllocFailed);
741     }
742     rc = strerror_r(err, buffer, size);
743     if (rc == -1) {
744       rc = errno; // XSI version sets errno.
745     }
746   }
747   if (rc == 0) {
748     message = buffer;
749   } else { // Buffer is unused. Free it.
750     KMP_INTERNAL_FREE(buffer);
751   }
752 
753 #endif
754 
755 #endif /* KMP_OS_WINDOWS */
756 
757   if (message == NULL) {
758     // TODO: I18n this message.
759     message = __kmp_str_format("%s", "(No system error message available)");
760   }
761   return message;
762 } // sys_error
763 
764 // -----------------------------------------------------------------------------
__kmp_msg_error_code(int code)765 kmp_msg_t __kmp_msg_error_code(int code) {
766 
767   kmp_msg_t msg;
768   msg.type = kmp_mt_syserr;
769   msg.num = code;
770   msg.str = sys_error(code);
771   msg.len = KMP_STRLEN(msg.str);
772   return msg;
773 
774 } // __kmp_msg_error_code
775 
776 // -----------------------------------------------------------------------------
__kmp_msg_error_mesg(char const * mesg)777 kmp_msg_t __kmp_msg_error_mesg(char const *mesg) {
778 
779   kmp_msg_t msg;
780   msg.type = kmp_mt_syserr;
781   msg.num = 0;
782   msg.str = __kmp_str_format("%s", mesg);
783   msg.len = KMP_STRLEN(msg.str);
784   return msg;
785 
786 } // __kmp_msg_error_mesg
787 
788 // -----------------------------------------------------------------------------
__kmp_msg(kmp_msg_severity_t severity,kmp_msg_t message,va_list args)789 void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, 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   for (;;) {
819     message = va_arg(args, kmp_msg_t);
820     if (message.type == kmp_mt_dummy && message.str == NULL) {
821       break;
822     }
823     switch (message.type) {
824     case kmp_mt_hint: {
825       format = kmp_i18n_fmt_Hint;
826       // we cannot skip %1$ and only use %2$ to print the message without the
827       // number
828       fmsg = __kmp_msg_format(format, message.str);
829     } break;
830     case kmp_mt_syserr: {
831       format = kmp_i18n_fmt_SysErr;
832       fmsg = __kmp_msg_format(format, message.num, message.str);
833     } break;
834     default: { KMP_DEBUG_ASSERT(0); }
835     }
836     __kmp_str_free(&message.str);
837     __kmp_str_buf_cat(&buffer, fmsg.str, fmsg.len);
838     __kmp_str_free(&fmsg.str);
839   }
840 
841   // Print formatted messages.
842   // This lock prevents multiple fatal errors on the same problem.
843   // __kmp_acquire_bootstrap_lock( & lock );    // GEH - This lock causing tests
844   // to hang on OS X*.
845   __kmp_printf("%s", buffer.str);
846   __kmp_str_buf_free(&buffer);
847 
848   // __kmp_release_bootstrap_lock( & lock );  // GEH - this lock causing tests
849   // to hang on OS X*.
850 
851 } // __kmp_msg
852 
__kmp_msg(kmp_msg_severity_t severity,kmp_msg_t message,...)853 void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message, ...) {
854   va_list args;
855   va_start(args, message);
856   __kmp_msg(severity, message, args);
857   va_end(args);
858 }
859 
__kmp_fatal(kmp_msg_t message,...)860 void __kmp_fatal(kmp_msg_t message, ...) {
861   va_list args;
862   va_start(args, message);
863   __kmp_msg(kmp_ms_fatal, message, args);
864   va_end(args);
865 #if KMP_OS_WINDOWS
866   // Delay to give message a chance to appear before reaping
867   __kmp_thread_sleep(500);
868 #endif
869   __kmp_abort_process();
870 } // __kmp_fatal
871 
872 // end of file //
873