xref: /illumos-gate/usr/src/lib/libc/port/gen/gettxt.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
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 #pragma weak _gettxt = gettxt
33 
34 #include "lint.h"
35 #include "libc.h"
36 #include <mtlib.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <locale.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
42 #include <sys/file.h>
43 #include <sys/mman.h>
44 #include <sys/stat.h>
45 #include <pfmt.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <limits.h>
49 #include <thread.h>
50 #include "../i18n/_locale.h"
51 #include "../i18n/_loc_path.h"
52 
53 #define	MESSAGES 	"/LC_MESSAGES/"
54 #define	DB_NAME_LEN	15
55 
56 #define	handle_return(s)	\
57 	((char *)((s) != NULL && *(s) != '\0' ? (s) : not_found))
58 
59 extern char cur_cat[];
60 extern rwlock_t _rw_cur_cat;
61 
62 static mutex_t	gettxt_lock = DEFAULTMUTEX;
63 static const char	*not_found = "Message not found!!\n";
64 static const char	*loc_C = "C";
65 
66 struct db_list {
67 	char	db_name[DB_NAME_LEN];	/* name of the message file */
68 	uintptr_t	addr;		/* virtual memory address */
69 	struct db_list	*next;
70 };
71 
72 struct db_cache {
73 	char	*loc;
74 	struct db_list	*info;
75 	struct db_cache	*next;
76 };
77 
78 static struct db_cache	*db_cache;
79 
80 char *
81 gettxt(const char *msg_id, const char *dflt_str)
82 {
83 	struct db_cache	*dbc;
84 	struct db_list	*dbl;
85 	char 	msgfile[DB_NAME_LEN];	/* name of static shared library */
86 	int	msgnum;			/* message number */
87 	char	pathname[PATH_MAX];	/* full pathname to message file */
88 	int	fd;
89 	struct stat64	sb;
90 	void	*addr;
91 	char	*tokp;
92 	size_t	name_len;
93 	char	*curloc;
94 
95 	if ((msg_id == NULL) || (*msg_id == '\0')) {
96 		return (handle_return(dflt_str));
97 	}
98 
99 	/* parse msg_id */
100 	if (((tokp = strchr(msg_id, ':')) == NULL) || *(tokp+1) == '\0')
101 		return (handle_return(dflt_str));
102 	if ((name_len = (tokp - msg_id)) >= DB_NAME_LEN)
103 		return (handle_return(dflt_str));
104 	if (name_len > 0) {
105 		(void) strncpy(msgfile, msg_id, name_len);
106 		msgfile[name_len] = '\0';
107 	} else {
108 		lrw_rdlock(&_rw_cur_cat);
109 		if (cur_cat == NULL || *cur_cat == '\0') {
110 			lrw_unlock(&_rw_cur_cat);
111 			return (handle_return(dflt_str));
112 		}
113 		/*
114 		 * We know the following strcpy is safe.
115 		 */
116 		(void) strcpy(msgfile, cur_cat);
117 		lrw_unlock(&_rw_cur_cat);
118 	}
119 	while (*++tokp) {
120 		if (!isdigit((unsigned char)*tokp))
121 			return (handle_return(dflt_str));
122 	}
123 	msgnum = atoi(msg_id + name_len + 1);
124 	curloc = setlocale(LC_MESSAGES, NULL);
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