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 #pragma ident "%Z%%M% %I% %E% SMI"
31
32 /* __gtxt(): Common part to gettxt() and pfmt() */
33
34 #pragma weak _setcat = setcat
35
36 #include "lint.h"
37 #include "libc.h"
38 #include <mtlib.h>
39 #include <sys/types.h>
40 #include <string.h>
41 #include <locale.h>
42 #include <fcntl.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/mman.h>
46 #include <stdlib.h>
47 #include <synch.h>
48 #include <pfmt.h>
49 #include <thread.h>
50 #include <unistd.h>
51 #include <errno.h>
52 #include <limits.h>
53 #include "../i18n/_locale.h"
54 #include "../i18n/_loc_path.h"
55
56 #define MESSAGES "/LC_MESSAGES/"
57 static const char *def_locale = "C";
58 static const char *not_found = "Message not found!!\n";
59 static struct db_info *db_info;
60 static int db_count, maxdb;
61
62 struct db_info {
63 char db_name[DB_NAME_LEN]; /* Name of the message file */
64 uintptr_t addr; /* Virtual memory address */
65 size_t length;
66 char *saved_locale;
67 char flag;
68 };
69
70 #define DB_EXIST 1 /* The catalogue exists */
71 #define DB_OPEN 2 /* Already tried to open */
72
73 /* Minimum number of open catalogues */
74 #define MINDB 3
75
76 char cur_cat[DB_NAME_LEN];
77 rwlock_t _rw_cur_cat = DEFAULTRWLOCK;
78
79
80 /*
81 * setcat(cat): Specify the default catalogue.
82 * Return a pointer to the local copy of the default catalogue
83 */
84 const char *
setcat(const char * cat)85 setcat(const char *cat)
86 {
87 lrw_wrlock(&_rw_cur_cat);
88 if (cat) {
89 if (((strchr(cat, '/') != NULL)) ||
90 ((strchr(cat, ':') != NULL))) {
91 cur_cat[0] = '\0';
92 goto out;
93 }
94 (void) strncpy(cur_cat, cat, sizeof (cur_cat) - 1);
95 cur_cat[sizeof (cur_cat) - 1] = '\0';
96 }
97 out:
98 lrw_unlock(&_rw_cur_cat);
99 return (cur_cat[0] ? cur_cat : NULL);
100 }
101
102 /*
103 * load a message catalog which specified with current locale,
104 * and catalog name.
105 */
106 static struct db_info *
load_db(const char * curloc,const char * catname,int * err)107 load_db(const char *curloc, const char *catname, int *err)
108 {
109 char pathname[PATH_MAX];
110 struct stat64 sb;
111 caddr_t addr;
112 struct db_info *db;
113 int fd;
114 int i;
115
116 *err = 0;
117
118 /* First time called, allocate space */
119 if (!db_info) {
120 if ((db_info =
121 libc_malloc(MINDB * sizeof (struct db_info))) == NULL) {
122 *err = 1;
123 return (NULL);
124 }
125 maxdb = MINDB;
126 }
127
128 for (i = 0; i < db_count; i++) {
129 if (db_info[i].flag == 0)
130 break;
131 }
132 /* New catalogue */
133 if (i == db_count) {
134 if (db_count == maxdb) {
135 if ((db = libc_realloc(db_info,
136 ++maxdb * sizeof (struct db_info))) == NULL) {
137 *err = 1;
138 return (NULL);
139 }
140 db_info = db;
141 }
142 db_count++;
143 }
144 db = &db_info[i];
145 db->flag = 0;
146 (void) strcpy(db->db_name, catname);
147 db->saved_locale = libc_strdup(curloc);
148 if (db->saved_locale == NULL) {
149 *err = 1;
150 return (NULL);
151 }
152 db->flag = DB_OPEN;
153 if (snprintf(pathname, sizeof (pathname),
154 _DFLT_LOC_PATH "%s" MESSAGES "%s",
155 db->saved_locale, db->db_name) >= sizeof (pathname)) {
156 /*
157 * We won't set err here, because an invalid locale is not
158 * the fatal condition, but we can fall back to "C"
159 * locale.
160 */
161 return (NULL);
162 }
163 if ((fd = open(pathname, O_RDONLY)) != -1 &&
164 fstat64(fd, &sb) != -1 &&
165 (addr = mmap(0, (size_t)sb.st_size, PROT_READ, MAP_SHARED,
166 fd, 0)) != MAP_FAILED) {
167 db->flag |= DB_EXIST;
168 db->addr = (uintptr_t)addr;
169 db->length = (size_t)sb.st_size;
170 }
171 if (fd != -1)
172 (void) close(fd);
173 return (db);
174 }
175
176 /*
177 * unmap the message catalog, and release the db_info slot.
178 */
179 static void
unload_db(struct db_info * db)180 unload_db(struct db_info *db)
181 {
182 if ((db->flag & (DB_OPEN|DB_EXIST)) ==
183 (DB_OPEN|DB_EXIST)) {
184 (void) munmap((caddr_t)db->addr, db->length);
185 }
186 db->flag = 0;
187 if (db->saved_locale)
188 libc_free(db->saved_locale);
189 db->saved_locale = NULL;
190 }
191
192 /*
193 * go through the db_info, and find out a db_info slot regarding
194 * the given current locale and catalog name.
195 * If db is not NULL, then search will start from top of the array,
196 * otherwise it will start from the next of given db.
197 * If curloc is set to NULL, then return a cache without regards of
198 * locale.
199 */
200 static struct db_info *
lookup_cache(struct db_info * db,const char * curloc,const char * catname)201 lookup_cache(struct db_info *db, const char *curloc, const char *catname)
202 {
203 if (db_info == NULL)
204 return (NULL);
205
206 if (db == NULL)
207 db = db_info;
208 else
209 db++;
210
211 for (; db < &db_info[db_count]; db++) {
212 if (db->flag == 0)
213 continue;
214 if (strcmp(db->db_name, catname) == 0) {
215 if (curloc == NULL ||
216 (db->saved_locale != NULL &&
217 strcmp(db->saved_locale, curloc) == 0)) {
218 return (db);
219 }
220 }
221 }
222 return (NULL);
223 }
224
225 static int
valid_msg(struct db_info * db,int id)226 valid_msg(struct db_info *db, int id)
227 {
228 if (db == NULL || (db->flag & DB_EXIST) == 0)
229 return (0);
230
231 /* catalog has been loaded */
232 if (id != 0 && id <= *(int *)(db->addr))
233 return (1);
234
235 /* not a valid id */
236 return (0);
237 }
238
239 static char *
msg(struct db_info * db,int id)240 msg(struct db_info *db, int id)
241 {
242 return ((char *)(db->addr + *(int *)(db->addr +
243 id * sizeof (int))));
244 }
245
246 /*
247 * __gtxt(catname, id, dflt): Return a pointer to a message.
248 * catname is the name of the catalog. If null, the default catalog is
249 * used.
250 * id is the numeric id of the message in the catalogue
251 * dflt is the default message.
252 *
253 * Information about non-existent catalogues is kept in db_info, in
254 * such a way that subsequent calls with the same catalogue do not
255 * try to open the catalogue again.
256 */
257 const char *
__gtxt(const char * catname,int id,const char * dflt)258 __gtxt(const char *catname, int id, const char *dflt)
259 {
260 char *curloc;
261 struct db_info *db;
262 int err;
263
264 /* Check for invalid message id */
265 if (id < 0)
266 return (not_found);
267 if (id == 0)
268 return ((dflt && *dflt) ? dflt : not_found);
269
270 /*
271 * If catalogue is unspecified, use default catalogue.
272 * No catalogue at all is an error
273 */
274 if (!catname || !*catname) {
275 lrw_rdlock(&_rw_cur_cat);
276 if (cur_cat == NULL || !*cur_cat) {
277 lrw_unlock(&_rw_cur_cat);
278 return (not_found);
279 }
280 catname = cur_cat;
281 lrw_unlock(&_rw_cur_cat);
282 }
283
284 curloc = setlocale(LC_MESSAGES, NULL);
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