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