LLVM OpenMP* Runtime Library
kmp_i18n.cpp
1 /*
2  * kmp_i18n.cpp
3  */
4 
5 //===----------------------------------------------------------------------===//
6 //
7 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
8 // See https://llvm.org/LICENSE.txt for license information.
9 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "kmp_i18n.h"
14 
15 #include "kmp.h"
16 #include "kmp_debug.h"
17 #include "kmp_io.h" // __kmp_printf.
18 #include "kmp_lock.h"
19 #include "kmp_os.h"
20 
21 #include <errno.h>
22 #include <locale.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26 
27 #include "kmp_environment.h"
28 #include "kmp_i18n_default.inc"
29 #include "kmp_str.h"
30 
31 #undef KMP_I18N_OK
32 
33 #define get_section(id) ((id) >> 16)
34 #define get_number(id) ((id)&0xFFFF)
35 
36 kmp_msg_t __kmp_msg_null = {kmp_mt_dummy, 0, NULL, 0};
37 static char const *no_message_available = "(No message available)";
38 
39 static void __kmp_msg(kmp_msg_severity_t severity, kmp_msg_t message,
40  va_list ap);
41 
42 enum kmp_i18n_cat_status {
43  KMP_I18N_CLOSED, // Not yet opened or closed.
44  KMP_I18N_OPENED, // Opened successfully, ready to use.
45  KMP_I18N_ABSENT // Opening failed, message catalog should not be used.
46 }; // enum kmp_i18n_cat_status
47 typedef enum kmp_i18n_cat_status kmp_i18n_cat_status_t;
48 static volatile kmp_i18n_cat_status_t status = KMP_I18N_CLOSED;
49 
50 /* Message catalog is opened at first usage, so we have to synchronize opening
51  to avoid race and multiple openings.
52 
53  Closing does not require synchronization, because catalog is closed very late
54  at library shutting down, when no other threads are alive. */
55 
56 static void __kmp_i18n_do_catopen();
57 static kmp_bootstrap_lock_t lock = KMP_BOOTSTRAP_LOCK_INITIALIZER(lock);
58 // `lock' variable may be placed into __kmp_i18n_catopen function because it is
59 // used only by that function. But we afraid a (buggy) compiler may treat it
60 // wrongly. So we put it outside of function just in case.
61 
62 void __kmp_i18n_catopen() {
63  if (status == KMP_I18N_CLOSED) {
64  __kmp_acquire_bootstrap_lock(&lock);
65  if (status == KMP_I18N_CLOSED) {
66  __kmp_i18n_do_catopen();
67  }
68  __kmp_release_bootstrap_lock(&lock);
69  }
70 } // func __kmp_i18n_catopen
71 
72 /* Linux* OS and OS X* part */
73 #if KMP_OS_UNIX
74 #define KMP_I18N_OK
75 
76 #include <nl_types.h>
77 
78 #define KMP_I18N_NULLCAT ((nl_catd)(-1))
79 static nl_catd cat = KMP_I18N_NULLCAT; // !!! Shall it be volatile?
80 static char const *name =
81  (KMP_VERSION_MAJOR == 4 ? "libguide.cat" : "libomp.cat");
82 
83 /* Useful links:
84 http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html#tag_08_02
85 http://www.opengroup.org/onlinepubs/000095399/functions/catopen.html
86 http://www.opengroup.org/onlinepubs/000095399/functions/setlocale.html
87 */
88 
89 void __kmp_i18n_do_catopen() {
90  int english = 0;
91  char *lang = __kmp_env_get("LANG");
92  // TODO: What about LC_ALL or LC_MESSAGES?
93 
94  KMP_DEBUG_ASSERT(status == KMP_I18N_CLOSED);
95  KMP_DEBUG_ASSERT(cat == KMP_I18N_NULLCAT);
96 
97  english = lang == NULL || // In all these cases English language is used.
98  strcmp(lang, "") == 0 || strcmp(lang, " ") == 0 ||
99  // Workaround for Fortran RTL bug DPD200137873 "Fortran runtime
100  // resets LANG env var to space if it is not set".
101  strcmp(lang, "C") == 0 || strcmp(lang, "POSIX") == 0;
102 
103  if (!english) { // English language is not yet detected, let us continue.
104  // Format of LANG is: [language[_territory][.codeset][@modifier]]
105  // Strip all parts except language.
106  char *tail = NULL;
107  __kmp_str_split(lang, '@', &lang, &tail);
108  __kmp_str_split(lang, '.', &lang, &tail);
109  __kmp_str_split(lang, '_', &lang, &tail);
110  english = (strcmp(lang, "en") == 0);
111  }
112 
113  KMP_INTERNAL_FREE(lang);
114 
115  // Do not try to open English catalog because internal messages are
116  // exact copy of messages in English catalog.
117  if (english) {
118  status = KMP_I18N_ABSENT; // mark catalog as absent so it will not
119  // be re-opened.
120  return;
121  }
122 
123  cat = catopen(name, 0);
124  // TODO: Why do we pass 0 in flags?
125  status = (cat == KMP_I18N_NULLCAT ? KMP_I18N_ABSENT : KMP_I18N_OPENED);
126 
127  if (status == KMP_I18N_ABSENT) {
128  if (__kmp_generate_warnings > kmp_warnings_low) {
129  // AC: only issue warning in case explicitly asked to
130  int error = errno; // Save errno immediately.
131  char *nlspath = __kmp_env_get("NLSPATH");
132  char *lang = __kmp_env_get("LANG");
133 
134  // Infinite recursion will not occur -- status is KMP_I18N_ABSENT now, so
135  // __kmp_i18n_catgets() will not try to open catalog, but will return
136  // default message.
137  kmp_msg_t err_code = KMP_ERR(error);
138  __kmp_msg(kmp_ms_warning, KMP_MSG(CantOpenMessageCatalog, name), err_code,
139  KMP_HNT(CheckEnvVar, "NLSPATH", nlspath),
140  KMP_HNT(CheckEnvVar, "LANG", lang), __kmp_msg_null);
141  if (__kmp_generate_warnings == kmp_warnings_off) {
142  __kmp_str_free(&err_code.str);
143  }
144 
145  KMP_INFORM(WillUseDefaultMessages);
146  KMP_INTERNAL_FREE(nlspath);
147  KMP_INTERNAL_FREE(lang);
148  }
149  } else { // status == KMP_I18N_OPENED
150  int section = get_section(kmp_i18n_prp_Version);
151  int number = get_number(kmp_i18n_prp_Version);
152  char const *expected = __kmp_i18n_default_table.sect[section].str[number];
153  // Expected version of the catalog.
154  kmp_str_buf_t version; // Actual version of the catalog.
155  __kmp_str_buf_init(&version);
156  __kmp_str_buf_print(&version, "%s", catgets(cat, section, number, NULL));
157 
158  // String returned by catgets is invalid after closing catalog, so copy it.
159  if (strcmp(version.str, expected) != 0) {
160  __kmp_i18n_catclose(); // Close bad catalog.
161  status = KMP_I18N_ABSENT; // And mark it as absent.
162  if (__kmp_generate_warnings > kmp_warnings_low) {
163  // AC: only issue warning in case explicitly asked to
164  // And now print a warning using default messages.
165  char const *name = "NLSPATH";
166  char const *nlspath = __kmp_env_get(name);
167  __kmp_msg(kmp_ms_warning,
168  KMP_MSG(WrongMessageCatalog, name, version.str, expected),
169  KMP_HNT(CheckEnvVar, name, nlspath), __kmp_msg_null);
170  KMP_INFORM(WillUseDefaultMessages);
171  KMP_INTERNAL_FREE(CCAST(char *, nlspath));
172  } // __kmp_generate_warnings
173  }
174  __kmp_str_buf_free(&version);
175  }
176 } // func __kmp_i18n_do_catopen
177 
178 void __kmp_i18n_catclose() {
179  if (status == KMP_I18N_OPENED) {
180  KMP_DEBUG_ASSERT(cat != KMP_I18N_NULLCAT);
181  catclose(cat);
182  cat = KMP_I18N_NULLCAT;
183  }
184  status = KMP_I18N_CLOSED;
185 } // func __kmp_i18n_catclose
186 
187 char const *__kmp_i18n_catgets(kmp_i18n_id_t id) {
188 
189  int section = get_section(id);
190  int number = get_number(id);
191  char const *message = NULL;
192 
193  if (1 <= section && section <= __kmp_i18n_default_table.size) {
194  if (1 <= number && number <= __kmp_i18n_default_table.sect[section].size) {
195  if (status == KMP_I18N_CLOSED) {
196  __kmp_i18n_catopen();
197  }
198  if (status == KMP_I18N_OPENED) {
199  message = catgets(cat, section, number,
200  __kmp_i18n_default_table.sect[section].str[number]);
201  }
202  if (message == NULL) {
203  message = __kmp_i18n_default_table.sect[section].str[number];
204  }
205  }
206  }
207  if (message == NULL) {
208  message = no_message_available;
209  }
210  return message;
211 
212 } // func __kmp_i18n_catgets
213 
214 #endif // KMP_OS_UNIX
215 
216 /* Windows* OS part. */
217 
218 #if KMP_OS_WINDOWS
219