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