| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 | /* setlocale.c * Load LC_CTYPE and LC_COLLATE locale only special for uclibc * * Written by Vladimir Oleynik (c) vodz@usa.net * * This file is part of the uClibc C library and is distributed * under the GNU Library General Public License. * used ideas is part of the GNU C Library. */#include <locale.h>#include <stdio.h>      /* NULL, fopen */#include <stdlib.h>     /* malloc */#include <string.h>#include <limits.h>     /* PATH_MAX */#include <errno.h>      /* EINVAL */#include <unistd.h>     /* get(e)[u|g]id */#include "_locale.h"static char C_LOCALE_NAME    []="C";static char POSIX_LOCALE_NAME[]="POSIX";static char composite_name_C []="LC_CTYPE=C;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=C;LC_MONETARY=C;LC_MESSAGES=C";#ifdef __UCLIBC_HAS_LOCALE__#ifdef TEST_LOCALEstatic const char PATH_LOCALE[]="./";#elsestatic const char PATH_LOCALE[]=__UCLIBC_LOCALE_DIR;#endifstruct SAV_LOADED_LOCALE {	int category;	char *locale;	const unsigned char *buf;	struct SAV_LOADED_LOCALE *next;};static struct SAV_LOADED_LOCALE sll_C_LC_MESSAGES = {  LC_MESSAGES, C_LOCALE_NAME,  0, 0};static struct SAV_LOADED_LOCALE sll_C_LC_MONETARY = {  LC_MONETARY, C_LOCALE_NAME, 0, &sll_C_LC_MESSAGES};static struct SAV_LOADED_LOCALE sll_C_LC_COLLATE = {  LC_COLLATE,  C_LOCALE_NAME, 0, &sll_C_LC_MONETARY};static struct SAV_LOADED_LOCALE sll_C_LC_TIME = {  LC_TIME,     C_LOCALE_NAME, 0, &sll_C_LC_COLLATE};static struct SAV_LOADED_LOCALE sll_C_LC_NUMERIC = {  LC_NUMERIC,  C_LOCALE_NAME, 0, &sll_C_LC_TIME};static struct SAV_LOADED_LOCALE sll_C_LC_CTYPE = {  LC_CTYPE,    C_LOCALE_NAME, _uc_ctype_b_C, &sll_C_LC_NUMERIC};static struct SAV_LOADED_LOCALE *sll = &sll_C_LC_CTYPE;#endif /* __UCLIBC_HAS_LOCALE__ */static char *nl_current[LC_ALL+1] = {	C_LOCALE_NAME, C_LOCALE_NAME, C_LOCALE_NAME,	C_LOCALE_NAME, C_LOCALE_NAME, C_LOCALE_NAME,	composite_name_C};static const char * const LC_strs[LC_ALL+1] = {	"/LC_CTYPE",	"/LC_NUMERIC",	"/LC_TIME",	"/LC_COLLATE",	"/LC_MONETARY",	"/LC_MESSAGES",	"/LC_ALL"};static char *find_locale(int c, const char **plocale){#ifdef __UCLIBC_HAS_LOCALE__	struct SAV_LOADED_LOCALE *cur;#endif	const char *name = *plocale;	if (name[0] == '\0') {	/* The user decides which locale to use by setting environment								variables. */		name = getenv (&LC_strs[LC_ALL][1]);		if (name == NULL || name[0] == '\0')			name = getenv (&LC_strs[c][1]);		if (name == NULL || name[0] == '\0')			name = getenv ("LANG");		if (name == NULL || name[0] == '\0')			name = C_LOCALE_NAME;	}	if (strcmp (name, C_LOCALE_NAME) == 0 ||				strcmp (name, POSIX_LOCALE_NAME) == 0 ||		/* TODO! */     (c!=LC_CTYPE && c!=LC_COLLATE))	    name = C_LOCALE_NAME;	*plocale = name;#ifdef __UCLIBC_HAS_LOCALE__	for(cur = sll; cur; cur = cur->next)		if(cur->category == c && strcmp(cur->locale, name)==0)			return cur->locale;#else	if(name == C_LOCALE_NAME)		return C_LOCALE_NAME;#endif	return NULL;}#ifdef __UCLIBC_HAS_LOCALE__static char *load_locale(int category, const char *locale){	FILE * fl;	char   full_path[PATH_MAX];	char * buf = 0;	struct SAV_LOADED_LOCALE *cur;	struct SAV_LOADED_LOCALE *bottom;	int    bufsize;	int    l = strlen(locale);	if((l+sizeof(PATH_LOCALE)+strlen(LC_strs[category]))>=PATH_MAX)		return NULL;	/* Not allow acces suid/sgid binaries to outside PATH_LOCALE */	if((geteuid()!=getuid() || getegid()!=getgid()) &&		strchr(locale, '/')!=NULL)			return NULL;	strcpy(full_path, PATH_LOCALE);	strcat(full_path, locale);	strcat(full_path, LC_strs[category]);	fl = fopen(full_path, "r");	if(fl==0)		return NULL;	switch(category) {		case LC_CTYPE:			bufsize = LOCALE_BUF_SIZE;			break;		case LC_COLLATE:			bufsize = 256;			break;		default:        /* TODO */			bufsize = 0;			break;	}	cur = malloc(sizeof(struct SAV_LOADED_LOCALE)+bufsize+l+2);	if(cur) {		buf = (char *)(cur+1);		if(bufsize!=0 && fread(buf, 1, bufsize+1, fl)!=(bufsize)) {			/* broken locale file */			free(cur);			buf = 0;#ifdef TEST_LOCALE			fprintf(stderr, "\nbroken locale file\n");#endif		}	}	fclose(fl);	if(cur==0)      /* not enough memory */		return NULL;	if(buf==0) {    /* broken locale file, set to "C" */		return C_LOCALE_NAME;	}	cur->next     = 0;	cur->buf      = buf;	cur->category = category;	cur->locale   = buf+bufsize;	strcpy(cur->locale, locale);	bottom = sll;	while(bottom->next!=0)		bottom = bottom->next;	bottom->next = cur;	return cur->locale;}static char *set_composite(int category, char *locale){	int i, l;	char *old_composite_name = nl_current[LC_ALL];	char *new_composite_name;	struct SAV_LOADED_LOCALE *cur;	for(l=i=0; i<LC_ALL; i++) {		new_composite_name = i == category ? locale : nl_current[i];						/* '=' + ';' or '\0' */		l += strlen(&LC_strs[i][1])+strlen(new_composite_name)+2;	}	new_composite_name = malloc(l);	if(new_composite_name==NULL)		return NULL;	if(old_composite_name!=composite_name_C)		free(old_composite_name);	nl_current[category] = locale;  /* change after malloc */	*new_composite_name = 0;	for(i=0; i<LC_ALL; i++) {		if(i)			strcat(new_composite_name, ";");		strcat(new_composite_name, &LC_strs[i][1]);		strcat(new_composite_name, "=");		strcat(new_composite_name, nl_current[i]);	}	nl_current[LC_ALL] = new_composite_name;	/* set locale data for ctype and strcollate functions */	for(cur = sll; ; cur = cur->next)		if(cur->category == category && cur->locale == locale)			break;	switch(category) {		case LC_CTYPE:			_uc_ctype_b     = cur->buf;			_uc_ctype_trans = cur->buf+LOCALE_BUF_SIZE/2;			break;		case LC_COLLATE:			_uc_collate_b   = cur->buf;			break;		default:        /* TODO */			break;	}	return locale;}#endif /* __UCLIBC_HAS_LOCALE__ */char *setlocale(int category, const char *locale){	char * tl;#ifdef __UCLIBC_HAS_LOCALE__	int    i;#endif	if (category < 0 || category > LC_ALL) {#ifdef __UCLIBC_HAS_LOCALE__einval:#endif		errno = EINVAL;		return NULL;	}	if(locale==NULL)		return nl_current[category];	if(category!=LC_ALL) {		tl = find_locale(category, &locale);#ifdef __UCLIBC_HAS_LOCALE__		if(tl==NULL)			tl = load_locale(category, locale);		if(tl) {			if(nl_current[category] != tl)				tl = set_composite(category, tl);		}#endif		return tl;	}	/* LC_ALL */#ifdef __UCLIBC_HAS_LOCALE__	/* The user wants to set all categories.  The desired locales	 for the individual categories can be selected by using a	 composite locale name.  This is a semi-colon separated list	 of entries of the form `CATEGORY=VALUE'.  */	tl = strchr(locale, ';');	if(tl==NULL) {	/* This is not a composite name. Load the data for each category. */		for(i=0; i<LC_ALL; i++)			setlocale(i, locale);   /* recursive */	} else {	/* This is a composite name.  Make a copy and split it up. */	  const char *newnames[LC_ALL];	  char *np;	  char *cp;	  i  = strlen(locale);	  np = alloca (i);	  if(np)		strcpy(np, locale);	  else		return NULL;	  for (i = 0; i < LC_ALL; ++i)		newnames[i] = 0;	  while ((cp = strchr (np, '=')) != NULL) {	      for (i = 0; i < LC_ALL; ++i)		if ((size_t) (cp - np) == strlen(&LC_strs[i][1])		    && memcmp (np, &LC_strs[i][1], (cp - np)) == 0)		  break;	      if (i == LC_ALL)		/* Bogus category name.  */		goto einval;	      /* Found the category this clause sets.  */	      newnames[i] = ++cp;	      cp = strchr (cp, ';');	      if (cp != NULL) {		  /* Examine the next clause.  */		  *cp = '\0';		  np = cp + 1;	      } else		/* This was the last clause.  We are done.  */		break;	  }	  for (i = 0; i < LC_ALL; ++i)		setlocale(i, newnames[i]);   /* recursive */	}#endif	return nl_current[LC_ALL];}
 |