usr/src/lib/libc/port/gen/gettxt.c
author sp149894
Tue, 12 Jun 2007 02:38:27 -0700
changeset 4447 205a92ede54a
parent 1219 f89f56c2d9ac
child 6812 febeba71273d
permissions -rw-r--r--
6293320 gettxt() not MT-SAFE

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*	Copyright (c) 1988 AT&T	*/
/*	  All Rights Reserved  	*/


#pragma weak gettxt = _gettxt

#include "synonyms.h"
#include "libc.h"
#include <mtlib.h>
#include <ctype.h>
#include <string.h>
#include <locale.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pfmt.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <thread.h>
#include "../i18n/_locale.h"
#include "../i18n/_loc_path.h"

#define	MESSAGES 	"/LC_MESSAGES/"
#define	DB_NAME_LEN	15

#define	handle_return(s)	\
	((char *)((s) != NULL && *(s) != '\0' ? (s) : not_found))

extern char cur_cat[];
extern rwlock_t _rw_cur_cat;

static mutex_t	gettxt_lock = DEFAULTMUTEX;
static const char	*not_found = "Message not found!!\n";
static const char	*loc_C = "C";

struct db_list {
	char	db_name[DB_NAME_LEN];	/* name of the message file */
	uintptr_t	addr;		/* virtual memory address */
	struct db_list	*next;
};

struct db_cache {
	char	*loc;
	struct db_list	*info;
	struct db_cache	*next;
};

static struct db_cache	*db_cache;

char *
gettxt(const char *msg_id, const char *dflt_str)
{
	struct db_cache	*dbc;
	struct db_list	*dbl;
	char 	msgfile[DB_NAME_LEN];	/* name of static shared library */
	int	msgnum;			/* message number */
	char	pathname[PATH_MAX];	/* full pathname to message file */
	int	fd;
	struct stat64	sb;
	void	*addr;
	char	*tokp;
	size_t	name_len;
	char	*curloc;

	if ((msg_id == NULL) || (*msg_id == '\0')) {
		return (handle_return(dflt_str));
	}

	/* parse msg_id */
	if (((tokp = strchr(msg_id, ':')) == NULL) || *(tokp+1) == '\0')
		return (handle_return(dflt_str));
	if ((name_len = (tokp - msg_id)) >= DB_NAME_LEN)
		return (handle_return(dflt_str));
	if (name_len > 0) {
		(void) strncpy(msgfile, msg_id, name_len);
		msgfile[name_len] = '\0';
	} else {
		lrw_rdlock(&_rw_cur_cat);
		if (cur_cat == NULL || *cur_cat == '\0') {
			lrw_unlock(&_rw_cur_cat);
			return (handle_return(dflt_str));
		}
		/*
		 * We know the following strcpy is safe.
		 */
		(void) strcpy(msgfile, cur_cat);
		lrw_unlock(&_rw_cur_cat);
	}
	while (*++tokp) {
		if (!isdigit((unsigned char)*tokp))
			return (handle_return(dflt_str));
	}
	msgnum = atoi(msg_id + name_len + 1);
	curloc = setlocale(LC_MESSAGES, NULL);

	lmutex_lock(&gettxt_lock);

try_C:
	dbc = db_cache;
	while (dbc) {
		if (strcmp(curloc, dbc->loc) == 0) {
			dbl = dbc->info;
			while (dbl) {
				if (strcmp(msgfile, dbl->db_name) == 0) {
					/* msgfile found */
					lmutex_unlock(&gettxt_lock);
					goto msgfile_found;
				}
				dbl = dbl->next;
			}
			/* not found */
			break;
		}
		dbc = dbc->next;
	}
	if (dbc == NULL) {
		/* new locale */
		if ((dbc = lmalloc(sizeof (struct db_cache))) == NULL) {
			lmutex_unlock(&gettxt_lock);
			return (handle_return(dflt_str));
		}
		if ((dbc->loc = lmalloc(strlen(curloc) + 1)) == NULL) {
			lfree(dbc, sizeof (struct db_cache));
			lmutex_unlock(&gettxt_lock);
			return (handle_return(dflt_str));
		}
		dbc->info = NULL;
		(void) strcpy(dbc->loc, curloc);
		/* connect dbc to the dbc list */
		dbc->next = db_cache;
		db_cache = dbc;
	}
	if ((dbl = lmalloc(sizeof (struct db_list))) == NULL) {
		lmutex_unlock(&gettxt_lock);
		return (handle_return(dflt_str));
	}

	if (snprintf(pathname, sizeof (pathname),
	    _DFLT_LOC_PATH "%s" MESSAGES "%s", dbc->loc, msgfile) >=
	    sizeof (pathname)) {
		lfree(dbl, sizeof (struct db_list));
		lmutex_unlock(&gettxt_lock);
		return (handle_return(dflt_str));
	}
	if ((fd = open(pathname, O_RDONLY)) == -1 ||
	    fstat64(fd, &sb) == -1 ||
	    (addr = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_SHARED,
	    fd, 0L)) == MAP_FAILED) {
		if (fd != -1)
			(void) close(fd);
		lfree(dbl, sizeof (struct db_list));

		if (strcmp(dbc->loc, "C") == 0) {
			lmutex_unlock(&gettxt_lock);
			return (handle_return(dflt_str));
		}
		/* Change locale to C */
		curloc = (char *)loc_C;
		goto try_C;
	}
	(void) close(fd);

	/* save file name, memory address, fd and size */
	(void) strcpy(dbl->db_name, msgfile);
	dbl->addr = (uintptr_t)addr;

	/* connect dbl to the dbc->info list */
	dbl->next = dbc->info;
	dbc->info = dbl;

	lmutex_unlock(&gettxt_lock);

msgfile_found:
	/* check if msgnum out of domain */
	if (msgnum <= 0 || msgnum > *(int *)dbl->addr)
		return (handle_return(dflt_str));
	/* return pointer to message */
	return ((char *)(dbl->addr +
	    *(int *)(dbl->addr + msgnum * sizeof (int))));
}