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