xref: /illumos-gate/usr/src/lib/libc/port/gen/gettxt.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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 weak _gettxt = gettxt
31 
32 #include "lint.h"
33 #include "libc.h"
34 #include <mtlib.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <locale.h>
38 #include <fcntl.h>
39 #include <sys/types.h>
40 #include <sys/file.h>
41 #include <sys/mman.h>
42 #include <sys/stat.h>
43 #include <pfmt.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <limits.h>
47 #include <thread.h>
48 #include "../i18n/_locale.h"
49 #include "../i18n/_loc_path.h"
50 
51 #define	MESSAGES 	"/LC_MESSAGES/"
52 #define	DB_NAME_LEN	15
53 
54 #define	handle_return(s)	\
55 	((char *)((s) != NULL && *(s) != '\0' ? (s) : not_found))
56 
57 extern char cur_cat[];
58 extern rwlock_t _rw_cur_cat;
59 
60 static mutex_t	gettxt_lock = DEFAULTMUTEX;
61 static const char	*not_found = "Message not found!!\n";
62 static const char	*loc_C = "C";
63 
64 struct db_list {
65 	char	db_name[DB_NAME_LEN];	/* name of the message file */
66 	uintptr_t	addr;		/* virtual memory address */
67 	struct db_list	*next;
68 };
69 
70 struct db_cache {
71 	char	*loc;
72 	struct db_list	*info;
73 	struct db_cache	*next;
74 };
75 
76 static struct db_cache	*db_cache;
77 
78 char *
79 gettxt(const char *msg_id, const char *dflt_str)
80 {
81 	struct db_cache	*dbc;
82 	struct db_list	*dbl;
83 	char 	msgfile[DB_NAME_LEN];	/* name of static shared library */
84 	int	msgnum;			/* message number */
85 	char	pathname[PATH_MAX];	/* full pathname to message file */
86 	int	fd;
87 	struct stat64	sb;
88 	void	*addr;
89 	char	*tokp;
90 	size_t	name_len;
91 	char	*curloc;
92 	locale_t	loc;
93 
94 	if ((msg_id == NULL) || (*msg_id == '\0')) {
95 		return (handle_return(dflt_str));
96 	}
97 
98 	/* parse msg_id */
99 	if (((tokp = strchr(msg_id, ':')) == NULL) || *(tokp+1) == '\0')
100 		return (handle_return(dflt_str));
101 	if ((name_len = (tokp - msg_id)) >= DB_NAME_LEN)
102 		return (handle_return(dflt_str));
103 	if (name_len > 0) {
104 		(void) strncpy(msgfile, msg_id, name_len);
105 		msgfile[name_len] = '\0';
106 	} else {
107 		lrw_rdlock(&_rw_cur_cat);
108 		if (cur_cat == NULL || *cur_cat == '\0') {
109 			lrw_unlock(&_rw_cur_cat);
110 			return (handle_return(dflt_str));
111 		}
112 		/*
113 		 * We know the following strcpy is safe.
114 		 */
115 		(void) strcpy(msgfile, cur_cat);
116 		lrw_unlock(&_rw_cur_cat);
117 	}
118 	while (*++tokp) {
119 		if (!isdigit((unsigned char)*tokp))
120 			return (handle_return(dflt_str));
121 	}
122 	msgnum = atoi(msg_id + name_len + 1);
123 	loc = uselocale(NULL);
124 	curloc = current_locale(loc, LC_MESSAGES);
125 
126 	lmutex_lock(&gettxt_lock);
127 
128 try_C:
129 	dbc = db_cache;
130 	while (dbc) {
131 		if (strcmp(curloc, dbc->loc) == 0) {
132 			dbl = dbc->info;
133 			while (dbl) {
134 				if (strcmp(msgfile, dbl->db_name) == 0) {
135 					/* msgfile found */
136 					lmutex_unlock(&gettxt_lock);
137 					goto msgfile_found;
138 				}
139 				dbl = dbl->next;
140 			}
141 			/* not found */
142 			break;
143 		}
144 		dbc = dbc->next;
145 	}
146 	if (dbc == NULL) {
147 		/* new locale */
148 		if ((dbc = lmalloc(sizeof (struct db_cache))) == NULL) {
149 			lmutex_unlock(&gettxt_lock);
150 			return (handle_return(dflt_str));
151 		}
152 		if ((dbc->loc = lmalloc(strlen(curloc) + 1)) == NULL) {
153 			lfree(dbc, sizeof (struct db_cache));
154 			lmutex_unlock(&gettxt_lock);
155 			return (handle_return(dflt_str));
156 		}
157 		dbc->info = NULL;
158 		(void) strcpy(dbc->loc, curloc);
159 		/* connect dbc to the dbc list */
160 		dbc->next = db_cache;
161 		db_cache = dbc;
162 	}
163 	if ((dbl = lmalloc(sizeof (struct db_list))) == NULL) {
164 		lmutex_unlock(&gettxt_lock);
165 		return (handle_return(dflt_str));
166 	}
167 
168 	if (snprintf(pathname, sizeof (pathname),
169 	    _DFLT_LOC_PATH "%s" MESSAGES "%s", dbc->loc, msgfile) >=
170 	    sizeof (pathname)) {
171 		lfree(dbl, sizeof (struct db_list));
172 		lmutex_unlock(&gettxt_lock);
173 		return (handle_return(dflt_str));
174 	}
175 	if ((fd = open(pathname, O_RDONLY)) == -1 ||
176 	    fstat64(fd, &sb) == -1 ||
177 	    (addr = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_SHARED,
178 	    fd, 0L)) == MAP_FAILED) {
179 		if (fd != -1)
180 			(void) close(fd);
181 		lfree(dbl, sizeof (struct db_list));
182 
183 		if (strcmp(dbc->loc, "C") == 0) {
184 			lmutex_unlock(&gettxt_lock);
185 			return (handle_return(dflt_str));
186 		}
187 		/* Change locale to C */
188 		curloc = (char *)loc_C;
189 		goto try_C;
190 	}
191 	(void) close(fd);
192 
193 	/* save file name, memory address, fd and size */
194 	(void) strcpy(dbl->db_name, msgfile);
195 	dbl->addr = (uintptr_t)addr;
196 
197 	/* connect dbl to the dbc->info list */
198 	dbl->next = dbc->info;
199 	dbc->info = dbl;
200 
201 	lmutex_unlock(&gettxt_lock);
202 
203 msgfile_found:
204 	/* check if msgnum out of domain */
205 	if (msgnum <= 0 || msgnum > *(int *)dbl->addr)
206 		return (handle_return(dflt_str));
207 	/* return pointer to message */
208 	return ((char *)(dbl->addr +
209 	    *(int *)(dbl->addr + msgnum * sizeof (int))));
210 }
211