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