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