1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1988 AT&T */
28 /* All Rights Reserved */
29
30 /* __gtxt(): Common part to gettxt() and pfmt() */
31
32 #pragma weak _setcat = setcat
33
34 #include "lint.h"
35 #include "libc.h"
36 #include <mtlib.h>
37 #include <sys/types.h>
38 #include <string.h>
39 #include <locale.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/mman.h>
44 #include <stdlib.h>
45 #include <synch.h>
46 #include <pfmt.h>
47 #include <thread.h>
48 #include <unistd.h>
49 #include <errno.h>
50 #include <limits.h>
51 #include "../i18n/_locale.h"
52 #include "../i18n/_loc_path.h"
53
54 #define MESSAGES "/LC_MESSAGES/"
55 static const char *def_locale = "C";
56 static const char *not_found = "Message not found!!\n";
57 static struct db_info *db_info;
58 static int db_count, maxdb;
59
60 struct db_info {
61 char db_name[DB_NAME_LEN]; /* Name of the message file */
62 uintptr_t addr; /* Virtual memory address */
63 size_t length;
64 char *saved_locale;
65 char flag;
66 };
67
68 #define DB_EXIST 1 /* The catalogue exists */
69 #define DB_OPEN 2 /* Already tried to open */
70
71 /* Minimum number of open catalogues */
72 #define MINDB 3
73
74 char cur_cat[DB_NAME_LEN];
75 rwlock_t _rw_cur_cat = DEFAULTRWLOCK;
76
77
78 /*
79 * setcat(cat): Specify the default catalogue.
80 * Return a pointer to the local copy of the default catalogue
81 */
82 const char *
setcat(const char * cat)83 setcat(const char *cat)
84 {
85 lrw_wrlock(&_rw_cur_cat);
86 if (cat) {
87 if (((strchr(cat, '/') != NULL)) ||
88 ((strchr(cat, ':') != NULL))) {
89 cur_cat[0] = '\0';
90 goto out;
91 }
92 (void) strncpy(cur_cat, cat, sizeof (cur_cat) - 1);
93 cur_cat[sizeof (cur_cat) - 1] = '\0';
94 }
95 out:
96 lrw_unlock(&_rw_cur_cat);
97 return (cur_cat[0] ? cur_cat : NULL);
98 }
99
100 /*
101 * load a message catalog which specified with current locale,
102 * and catalog name.
103 */
104 static struct db_info *
load_db(const char * curloc,const char * catname,int * err)105 load_db(const char *curloc, const char *catname, int *err)
106 {
107 char pathname[PATH_MAX];
108 struct stat64 sb;
109 caddr_t addr;
110 struct db_info *db;
111 int fd;
112 int i;
113
114 *err = 0;
115
116 /* First time called, allocate space */
117 if (!db_info) {
118 if ((db_info =
119 libc_malloc(MINDB * sizeof (struct db_info))) == NULL) {
120 *err = 1;
121 return (NULL);
122 }
123 maxdb = MINDB;
124 }
125
126 for (i = 0; i < db_count; i++) {
127 if (db_info[i].flag == 0)
128 break;
129 }
130 /* New catalogue */
131 if (i == db_count) {
132 if (db_count == maxdb) {
133 if ((db = libc_realloc(db_info,
134 ++maxdb * sizeof (struct db_info))) == NULL) {
135 *err = 1;
136 return (NULL);
137 }
138 db_info = db;
139 }
140 db_count++;
141 }
142 db = &db_info[i];
143 db->flag = 0;
144 (void) strcpy(db->db_name, catname);
145 db->saved_locale = libc_strdup(curloc);
146 if (db->saved_locale == NULL) {
147 *err = 1;
148 return (NULL);
149 }
150 db->flag = DB_OPEN;
151 if (snprintf(pathname, sizeof (pathname),
152 _DFLT_LOC_PATH "%s" MESSAGES "%s",
153 db->saved_locale, db->db_name) >= sizeof (pathname)) {
154 /*
155 * We won't set err here, because an invalid locale is not
156 * the fatal condition, but we can fall back to "C"
157 * locale.
158 */
159 return (NULL);
160 }
161 if ((fd = open(pathname, O_RDONLY)) != -1 &&
162 fstat64(fd, &sb) != -1 &&
163 (addr = mmap(0, (size_t)sb.st_size, PROT_READ, MAP_SHARED,
164 fd, 0)) != MAP_FAILED) {
165 db->flag |= DB_EXIST;
166 db->addr = (uintptr_t)addr;
167 db->length = (size_t)sb.st_size;
168 }
169 if (fd != -1)
170 (void) close(fd);
171 return (db);
172 }
173
174 /*
175 * unmap the message catalog, and release the db_info slot.
176 */
177 static void
unload_db(struct db_info * db)178 unload_db(struct db_info *db)
179 {
180 if ((db->flag & (DB_OPEN|DB_EXIST)) ==
181 (DB_OPEN|DB_EXIST)) {
182 (void) munmap((caddr_t)db->addr, db->length);
183 }
184 db->flag = 0;
185 if (db->saved_locale)
186 libc_free(db->saved_locale);
187 db->saved_locale = NULL;
188 }
189
190 /*
191 * go through the db_info, and find out a db_info slot regarding
192 * the given current locale and catalog name.
193 * If db is not NULL, then search will start from top of the array,
194 * otherwise it will start from the next of given db.
195 * If curloc is set to NULL, then return a cache without regards of
196 * locale.
197 */
198 static struct db_info *
lookup_cache(struct db_info * db,const char * curloc,const char * catname)199 lookup_cache(struct db_info *db, const char *curloc, const char *catname)
200 {
201 if (db_info == NULL)
202 return (NULL);
203
204 if (db == NULL)
205 db = db_info;
206 else
207 db++;
208
209 for (; db < &db_info[db_count]; db++) {
210 if (db->flag == 0)
211 continue;
212 if (strcmp(db->db_name, catname) == 0) {
213 if (curloc == NULL ||
214 (db->saved_locale != NULL &&
215 strcmp(db->saved_locale, curloc) == 0)) {
216 return (db);
217 }
218 }
219 }
220 return (NULL);
221 }
222
223 static int
valid_msg(struct db_info * db,int id)224 valid_msg(struct db_info *db, int id)
225 {
226 if (db == NULL || (db->flag & DB_EXIST) == 0)
227 return (0);
228
229 /* catalog has been loaded */
230 if (id != 0 && id <= *(int *)(db->addr))
231 return (1);
232
233 /* not a valid id */
234 return (0);
235 }
236
237 static char *
msg(struct db_info * db,int id)238 msg(struct db_info *db, int id)
239 {
240 return ((char *)(db->addr + *(int *)(db->addr +
241 id * sizeof (int))));
242 }
243
244 /*
245 * __gtxt(catname, id, dflt): Return a pointer to a message.
246 * catname is the name of the catalog. If null, the default catalog is
247 * used.
248 * id is the numeric id of the message in the catalogue
249 * dflt is the default message.
250 *
251 * Information about non-existent catalogues is kept in db_info, in
252 * such a way that subsequent calls with the same catalogue do not
253 * try to open the catalogue again.
254 */
255 const char *
__gtxt(const char * catname,int id,const char * dflt)256 __gtxt(const char *catname, int id, const char *dflt)
257 {
258 char *curloc;
259 struct db_info *db;
260 int err;
261 locale_t loc;
262
263 /* Check for invalid message id */
264 if (id < 0)
265 return (not_found);
266 if (id == 0)
267 return ((dflt && *dflt) ? dflt : not_found);
268
269 /*
270 * If catalogue is unspecified, use default catalogue.
271 * No catalogue at all is an error
272 */
273 if (!catname || !*catname) {
274 lrw_rdlock(&_rw_cur_cat);
275 if (cur_cat == NULL || !*cur_cat) {
276 lrw_unlock(&_rw_cur_cat);
277 return (not_found);
278 }
279 catname = cur_cat;
280 lrw_unlock(&_rw_cur_cat);
281 }
282
283 loc = uselocale(NULL);
284 curloc = current_locale(loc, LC_MESSAGES);
285
286 /* First look up the cache */
287 db = lookup_cache(NULL, curloc, catname);
288 if (db != NULL) {
289 /*
290 * The catalog has been loaded, and if id seems valid,
291 * then just return.
292 */
293 if (valid_msg(db, id))
294 return (msg(db, id));
295
296 /*
297 * seems given id is out of bound or does not exist. In this
298 * case, we need to look up a message for the "C" locale as
299 * documented in the man page.
300 */
301 db = lookup_cache(NULL, def_locale, catname);
302 if (db == NULL) {
303 /*
304 * Even the message catalog for the "C" has not been
305 * loaded.
306 */
307 db = load_db(def_locale, catname, &err);
308 if (err)
309 return (not_found);
310 }
311 if (valid_msg(db, id))
312 return (msg(db, id));
313 /* no message found */
314 return ((dflt && *dflt) ? dflt : not_found);
315 }
316
317 /*
318 * The catalog has not been loaded or even has not
319 * attempted to be loaded, invalidate all caches related to
320 * the catname for possibly different locale.
321 */
322 db = NULL;
323 while ((db = lookup_cache(db, NULL, catname)) != NULL)
324 unload_db(db);
325
326 /*
327 * load a message catalog for the requested locale.
328 */
329 db = load_db(curloc, catname, &err);
330 if (err)
331 return (not_found);
332 if (valid_msg(db, id))
333 return (msg(db, id));
334
335 /*
336 * If the requested catalog is either not exist or message
337 * id is invalid, then try to load from "C" locale.
338 */
339 db = load_db(def_locale, catname, &err);
340 if (err)
341 return (not_found);
342
343 if (valid_msg(db, id))
344 return (msg(db, id));
345
346 /* no message found */
347 return ((dflt && *dflt) ? dflt : not_found);
348 }
349