Intel® OpenMP* Runtime Library
 All Classes Functions Variables Typedefs Enumerations Enumerator Groups Pages
kmp_i18n.c
1 /*
2  * kmp_i18n.c
3  * $Revision: 42181 $
4  * $Date: 2013-03-26 15:04:45 -0500 (Tue, 26 Mar 2013) $
5  */
6 
7 /* <copyright>
8  Copyright (c) 2007-2013 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 occured.
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 occured.
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 occurences 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 occurences 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 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( 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 ); id < ranges[ range ].last; 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*
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 = 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 = 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 //