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