/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
*/

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

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: setlocale.c 1.11 90/11/01 $@(#)libc-port:locale/setlocale.c 1.0 AT&T-UP"

/*
* setlocale - set and query function for all or parts of a program's locale.
*/
#include <stdio.h>
#include <locale.h>
#include "_locale.h"	/* internal to libc locale data structures */
#include <nl_types.h>
#include <langinfo.h>
#include "_loadlang.h"  /* internal to libc locale data structures */
#include <string.h>
#include <sys/types.h>
#include <sys/limits.h>
#include <sys/stat.h>
#include <fcntl.h>

extern char *getenv(), *_sl_nativeloc();;
extern void *malloc(), *memchr();

char *
setlocale(cat, loc)
int cat;
char *loc;
{
	char 		part[LC_NAMELEN], fullpath[18 + LC_NAMELEN];
	register 	char *p, locarr[LC_MAX+1][LC_NAMELEN];
	register int 	i, bad_subloc = 0;

	if (cat < LC_CTYPE || cat > LC_MAX)
		return (char *)0;
	
	if (loc == NULL) { 					/* Query */
		static char ans[LC_MAX * LC_NAMELEN + 1];
			
		if (cat != LC_ALL)
			(void)strcpy(ans, _cur_locale[cat]);
		else {
			register char *p, *q;
			register int i;
			register int flag = 0;

			/*
			* Generate composite locale description.
			*/
			p = ans;
			for (i = LC_CTYPE; i < LC_MAX; i++) {
				*p++ = '/';
				q = _cur_locale[i];
				(void)strcpy(p, q);
				p += strlen(q);
				if (!flag && i > LC_CTYPE)
					flag = strcmp(q ,_cur_locale[i-1]);
			}
			if (!flag)
				strcpy(ans,q);
		}
		return ans;
	}

	if (loc[0] == '\0') { 	       /* Set locs with environment variables. */
		if (cat == LC_ALL) {
			if ((loc = _sl_nativeloc(cat)) == NULL)
				return(NULL);

  			(void)loadlang(cat, loc);/* Xopen hook, loads _inmem */
				     	   	 /* for LC_ALL category (xopen)*/

					/* validate ALL sublocs FIRST - posix */
			for (cat = LC_CTYPE; cat < LC_MAX; ++cat) 
				if (_sl_nativeloc(cat) == NULL)
					return(NULL);
			
					/* change them, all sublocs are valid */
			for (bad_subloc=0, cat = LC_CTYPE; cat < LC_MAX; ++cat){
				if ((loc = _sl_nativeloc(cat)) == NULL)
					return(NULL);

				bad_subloc += set_subloc(cat, loc);
			}
			
			if (bad_subloc)	     /* bad if _ctype, _numeric tables */
				return NULL; /*    did not load correctly      */
			
			return (setlocale(LC_ALL, (char *)0));
		}
	
				     /* Set individual sublocale */
		if ((p = _sl_nativeloc(cat)) == NULL)
			return(NULL);

		if (set_subloc(cat, p))
			return NULL;
		
		return (setlocale(cat, (char *)0));
	}
	else {			/* New locale specified by loc */ 
		if (cat == LC_ALL)
		{
  			(void)loadlang(cat, loc);  /* Xopen hook, load _inmem  */
				     	   	   /* for xopen LC_ALL category*/

			cat = LC_CTYPE;
			bad_subloc = 0;

			if (loc[0] != '/') { 	   /* simple locale */

				(void)strncpy(part, loc, LC_NAMELEN - 1);
				part[LC_NAMELEN - 1] = '\0';

				if (!valid_loc(part)) 
					return (NULL);

					/* for each cat other than LC_ALL */
				while (cat < LC_MAX)
					bad_subloc += set_subloc(cat++, part);
	
				if (bad_subloc)
					return NULL;
			
				return (setlocale(LC_ALL, (char *)0));
			}
			else { 		/* composite locale for each cat other */
					/* than LC_ALL, check if valid */
				p = loc;
				for ( ; (cat < LC_MAX) && (p != NULL); ++cat) {
					i = strcspn(++p, "/");
					(void)strncpy(locarr[cat], p, i);
					locarr[cat][i] = '\0';
					p += i;
					if (!valid_loc(locarr[cat]))
						return (NULL);
				} 
				*locarr[cat] = NULL;

				for (cat = LC_CTYPE; (cat < LC_MAX)  
					        && (locarr[cat] != NULL); ++cat)
					bad_subloc +=set_subloc(cat,locarr[cat]);
				
				if (bad_subloc)
					return NULL;
				
				return (setlocale(LC_ALL, (char *)0));
			}
		}
					/* Individual sublocale */
		if (!valid_loc(loc))
			return (NULL);

		if (set_subloc(cat, loc))
			return NULL;
		else
			return (setlocale(cat, NULL));
	}
}



int
set_subloc(cat, part)
  int 	cat;
  char  *part;
{

	if (cat <= LC_NUMERIC) 
		if(strcmp(part, _cur_locale[cat]) && _set_tab(part, cat) != 0)
			return (-1);

	(void)loadlang(cat, part); /* Xopen hook - loads _inmem array */

	strcpy(_cur_locale[cat], part);

	return(0);
}




/*
 * Loadlang - loads LANGINFO file for X/open interface.
 *	      nl_langinfo() calls setlocale() which calls this function.
 */
static int loadlang(cat, locale)
  int 	cat;
  char 	*locale; 
{
	int 		fd;
	struct stat 	buf;
	char 		pathname[sizeof(P_locale)+MAXLENLOCALE+sizeof(DEF_LANGINFO)-1];
	char 		**temp, *p, *str;	
	int 		cnt;
	void		setup_locs();
	struct xo_clang *xocl_ptr, *check_if_loaded(), *init_xo_clang();

	
	switch(cat) {
		case LC_CTYPE:
		case LC_COLLATE:
		case LC_MESSAGES:
			return(1);	/* loadlang does not affect these */
	}

	if(_C_inmem[0] == NULL)
		for (cnt = 0; cnt < MAXSTRMSG+2; ++cnt)
			_C_inmem[cnt] = _inmem[cnt];

	(void)strcpy(pathname, P_locale);  /* Def as "/usr/lib/locale" in locale.h*/
	(void)strcpy(&pathname[16], locale);
	(void)strcat(pathname, DEF_LANGINFO);

	if ((xocl_ptr = check_if_loaded(pathname)) == (struct xo_clang *)NULL) {
		if ((fd = open(pathname,O_RDONLY)) == EOF)
			return(0);
	
		if( (fstat(fd, &buf)) != 0) {
			(void) close(fd);
			return(0);
		}
	
		if ( (str = (char *)malloc(buf.st_size + 2)) == NULL ){
			(void) close(fd);
			return(0);
		}
	
		if ( (read(fd, str, buf.st_size)) != buf.st_size) {
			free(str);
			(void) close(fd);
			return(0);
		}
		(void) close(fd); 	/* LANGINFO now in core */

					/* p will "walk thru" seperate strings
					 * contained in str 
					 */
		if ((temp = (char **)malloc((MAXSTRMSG+2)*sizeof(char *)))==NULL)
			return(0);

			/*
			 * Set string 0 to a blank line (as defined in the
			 * C locale.
			 */
		temp[0] = _C_inmem[0];
		p = &str[0]; 
		cnt = 1;
		while ((cnt <= MAXSTRMSG) && ((p - (&str[0])) < buf.st_size)){
			temp[cnt] = p;
			p += strlen(p)+1;
			cnt++;
		}
			/*
			 * Fill in missing entries with C locale strings.
			 * temp[1] is currently not updated with the C locale
			 * string because it is the beginning of the malloc'd
			 * space for the locale strings. free() is called
			 * with this address when the locale is unloaded.
			 */
		for (cnt = 2; cnt <= MAXSTRMSG; cnt++) { 
			if (temp[cnt] == NULL){
				temp[cnt] = _C_inmem[cnt];
			} else {
				if (*temp[cnt] == '\0') 
					temp[cnt] = _C_inmem[cnt];
			}
		}
		if ((xocl_ptr = init_xo_clang(temp, pathname)) == NULL)
			return(0);
	} 
	setup_locs(cat, xocl_ptr);
	
		/*
		 * Only collect after C locale 
		 */
	collect_empty_tabs(xo_langfo.forw);   

	return(1);
}

static void
setup_locs(cat, xocl_ptr)
  int 	 cat;
  struct xo_clang  *xocl_ptr;
{
	int 	i, *pos;

	switch(cat) {
		case LC_ALL:
			if (xocl_ptr != xo_locale_info.all_loc) {
				if (xo_locale_info.all_loc != NULL)
					--(xo_locale_info.all_loc->ref_cnt);

				xo_locale_info.all_loc = xocl_ptr;
				++(xo_locale_info.all_loc->ref_cnt); 
				pos = xo_locale_info.all_entry;
				for ( ; *pos != NULL; ++pos)
					_inmem[*pos]=xocl_ptr->langfo_ptr[*pos];
			}
			break;

		case LC_NUMERIC:
			if (xocl_ptr != xo_locale_info.numeric_loc) {
				if (xo_locale_info.numeric_loc != NULL)
					--(xo_locale_info.numeric_loc->ref_cnt);

				xo_locale_info.numeric_loc = xocl_ptr;
				++(xo_locale_info.numeric_loc->ref_cnt); 
				pos = xo_locale_info.numeric_entry;
				for ( ; *pos != NULL; ++pos)
					_inmem[*pos]=xocl_ptr->langfo_ptr[*pos];
			}
			break;

		case LC_MONETARY:
			if (xocl_ptr != xo_locale_info.monetary_loc) {
				if (xo_locale_info.monetary_loc != NULL)
					--(xo_locale_info.monetary_loc->ref_cnt);

				xo_locale_info.monetary_loc = xocl_ptr;
				++(xo_locale_info.monetary_loc->ref_cnt);
				pos = xo_locale_info.monetary_entry;
				for ( ; *pos != NULL; ++pos)
					_inmem[*pos]=xocl_ptr->langfo_ptr[*pos];
			}
			break;

		case LC_TIME:
			if (xocl_ptr != xo_locale_info.time_loc) {
				if (xo_locale_info.time_loc != NULL)
					--(xo_locale_info.time_loc->ref_cnt);

				xo_locale_info.time_loc = xocl_ptr;
				++(xo_locale_info.time_loc->ref_cnt);
				pos = xo_locale_info.time_entry;
				for ( ; *pos != NULL; ++pos)
					_inmem[*pos]=xocl_ptr->langfo_ptr[*pos];
			}
			break;

		default:
			return;
	}
}

static struct xo_clang *
init_xo_clang(temp, pathname)
  char *temp[];
  char pathname[];
{
	struct xo_clang *xocl_ptr, *tmptr;
	
	if ((xocl_ptr = (struct xo_clang *)malloc(sizeof(struct xo_clang))) == NULL)
		return(NULL);

	if ((xocl_ptr->name = (char *)malloc(strlen(pathname)+1)) == NULL)
		return(NULL);

	strcpy(xocl_ptr->name, pathname);
	xocl_ptr->ref_cnt = 0;
	xocl_ptr->langfo_ptr = temp;

	tmptr = &xo_langfo;
	while (tmptr->forw != NULL) 
		tmptr = tmptr->forw;
	
	tmptr->forw = xocl_ptr;
	xocl_ptr->back = tmptr;
	xocl_ptr->forw = NULL;

	return(xocl_ptr);
}

static struct xo_clang *
check_if_loaded(pathname)
  char 	pathname[];
{
	struct	xo_clang	*xocl_ptr;

	if (pathname == NULL)
		return((struct xo_clang *)NULL);

	xocl_ptr = &xo_langfo;
	do {
		if (xocl_ptr->name != NULL) 
			if (!strcmp(pathname, xocl_ptr->name))
				return(xocl_ptr);
		
	} while ((xocl_ptr = xocl_ptr->forw) != NULL);

	return((struct xo_clang*)NULL);
}
	

static collect_empty_tabs(xocl_ptr)
  struct xo_clang  *xocl_ptr;
{
	struct xo_clang	*tmptr;
	
	while (xocl_ptr != NULL) {
		tmptr = xocl_ptr->forw;
		if (xocl_ptr->ref_cnt == 0) {
			xocl_ptr->back->forw = xocl_ptr->forw;
			if (xocl_ptr->forw != NULL) 
				xocl_ptr->forw->back = xocl_ptr->back;

				/* Table loaded beginning at entry 1!!! */
			free(xocl_ptr->name); 
			free(xocl_ptr->langfo_ptr[1]); 
			free(xocl_ptr->langfo_ptr);
			free(xocl_ptr);
		}
		xocl_ptr = tmptr;
	}
}


