LLVM OpenMP* Runtime Library
kmp_i18n.cpp
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 //