944 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			944 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*  Part of SWI-Prolog
 | |
| 
 | |
|     Author:        Jan Wielemaker
 | |
|     E-mail:        J.Wielemaker@vu.nl
 | |
|     WWW:           http://www.swi-prolog.org
 | |
|     Copyright (C): 2013, VU University Amsterdam
 | |
| 
 | |
|     This library is free software; you can redistribute it and/or
 | |
|     modify it under the terms of the GNU Lesser General Public
 | |
|     License as published by the Free Software Foundation; either
 | |
|     version 2.1 of the License, or (at your option) any later version.
 | |
| 
 | |
|     This library is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|     Lesser General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public
 | |
|     License along with this library; if not, write to the Free Software
 | |
|     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 | |
| */
 | |
| 
 | |
| /** @defgroup SetLocale Localization Support
 | |
|   * @ingroup InputOutput
 | |
|   * @{
 | |
|   *
 | |
|   * This code includes support for localization, that is, the ability to support
 | |
|   * different languages and representation formats.
 | |
|   *
 | |
|   */
 | |
| #include "pl-incl.h"
 | |
| #include "pl-locale.h"
 | |
| 
 | |
| #if defined(__sun) || __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070
 | |
| #undef HAVE_WCSDUP			/* No prototype, so better use our own */
 | |
| #endif
 | |
| 
 | |
| #ifdef O_LOCALE
 | |
| 
 | |
| #include <locale.h>
 | |
| 
 | |
| #define LOCK()   PL_LOCK(L_LOCALE)	/* MT locking */
 | |
| #define UNLOCK() PL_UNLOCK(L_LOCALE)
 | |
| 
 | |
| #undef LD				/* fetch LD once per function */
 | |
| #define LD LOCAL_LD
 | |
| 
 | |
| #define LSTR_MAX 16
 | |
| 
 | |
| #ifndef HAVE_LOCALECONV
 | |
| typedef struct
 | |
| { char *decimal_point;
 | |
|   char *thousands_sep;
 | |
|   char *grouping;
 | |
| } lconv;
 | |
| 
 | |
| static struct lconv defl =
 | |
|   { ".",
 | |
|     ",",
 | |
|     "\003\003"
 | |
|   };
 | |
| 
 | |
| struct lconv *
 | |
| localeconv(void)
 | |
| {
 | |
|   return &defl;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #ifndef HAVE_WCSDUP
 | |
| static wchar_t *
 | |
| my_wcsdup(const wchar_t *in)
 | |
| { wchar_t *copy = malloc((wcslen(in)+1)*sizeof(wchar_t));
 | |
| 
 | |
|   if ( copy )
 | |
|     return wcscpy(copy, in);
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| #define wcsdup(ws) my_wcsdup(ws)
 | |
| #endif
 | |
| 
 | |
| 
 | |
| static wchar_t *
 | |
| ls_to_wcs(const char *in, const wchar_t *on_error)
 | |
| { wchar_t buf[LSTR_MAX];
 | |
|   mbstate_t state;
 | |
| 
 | |
|   memset(&state, 0, sizeof(state));
 | |
|   mbsrtowcs(buf, &in, LSTR_MAX, &state);
 | |
|   if ( in == NULL )
 | |
|   { return wcsdup(buf);
 | |
|   } else
 | |
|   { Sdprintf("Illegal locale string: %s\n", in);
 | |
|     return wcsdup(on_error);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| init_locale_strings(PL_locale *l, struct lconv *conv)
 | |
| { if ( conv )
 | |
|   { l->decimal_point = ls_to_wcs(conv->decimal_point, L".");
 | |
|     l->thousands_sep = ls_to_wcs(conv->thousands_sep, L",");
 | |
|     l->grouping      = strdup(conv->grouping);
 | |
| 
 | |
|     return TRUE;
 | |
|   } else
 | |
|   { l->decimal_point = wcsdup(L".");
 | |
|     l->thousands_sep = wcsdup(L",");
 | |
|     l->grouping      = strdup("\003");
 | |
| 
 | |
|     return FALSE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static PL_locale *
 | |
| new_locale(PL_locale *proto)
 | |
| { PL_locale *new = PL_malloc(sizeof(*new));
 | |
| 
 | |
|   if ( new )
 | |
|   { memset(new, 0, sizeof(*new));
 | |
|     new->magic = LOCALE_MAGIC;
 | |
| 
 | |
|     if ( proto )
 | |
|     { new->decimal_point = wcsdup(proto->decimal_point);
 | |
|       new->thousands_sep = wcsdup(proto->thousands_sep);
 | |
|       new->grouping      = strdup(proto->grouping);
 | |
|     } else
 | |
|     { init_locale_strings(new, localeconv());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return new;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| free_locale_strings(PL_locale *l)
 | |
| { free(l->decimal_point);
 | |
|   free(l->thousands_sep);
 | |
|   free(l->grouping);
 | |
| }
 | |
| 
 | |
| static void
 | |
| free_locale(PL_locale *l)
 | |
| { if ( l )
 | |
|   { free_locale_strings(l);
 | |
| 
 | |
|     if ( l->alias )
 | |
|       PL_unregister_atom(l->alias);
 | |
| 
 | |
|     PL_free(l);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| update_locale(PL_locale *l, int category, const char *locale)
 | |
| { free_locale_strings(l);
 | |
|   init_locale_strings(l, localeconv());
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| alias_locale(PL_locale *l, atom_t alias)
 | |
| { int rc;
 | |
| 
 | |
|   LOCK();
 | |
| 
 | |
|   if ( !GD->locale.localeTable )
 | |
|     GD->locale.localeTable = newHTable(16);
 | |
| 
 | |
|   if ( addHTable(GD->locale.localeTable, (void*)alias, l) )
 | |
|   { l->alias = alias;
 | |
|     PL_register_atom(alias);
 | |
|     rc = TRUE;
 | |
|   } else
 | |
|   { GET_LD
 | |
|     term_t obj = PL_new_term_ref();
 | |
| 
 | |
|     PL_put_atom(obj, alias);
 | |
|     rc = PL_error("locale_create", 2, "Alias name already taken",
 | |
| 		  ERR_PERMISSION, ATOM_create, ATOM_locale, obj);
 | |
|   }
 | |
|   UNLOCK();
 | |
| 
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	  LOCALE BLOB		*
 | |
| 		 *******************************/
 | |
| 
 | |
| typedef struct locale_ref
 | |
| { PL_locale	*data;
 | |
| } locale_ref;
 | |
| 
 | |
| 
 | |
| static int
 | |
| write_locale_ref(IOSTREAM *s, atom_t aref, int flags)
 | |
| { locale_ref *ref = PL_blob_data(aref, NULL, NULL);
 | |
|   (void)flags;
 | |
| 
 | |
|   Sfprintf(s, "<locale>(%p)", ref->data);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| acquire_locale_ref(atom_t aref)
 | |
| { locale_ref *ref = PL_blob_data(aref, NULL, NULL);
 | |
| 
 | |
|   (void)ref;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| release_locale_ref(atom_t aref)
 | |
| { locale_ref *ref = PL_blob_data(aref, NULL, NULL);
 | |
| 
 | |
|   LOCK();
 | |
|   if ( ref->data->references == 0 )
 | |
|     free_locale(ref->data);
 | |
|   else
 | |
|     ref->data->symbol = 0;
 | |
|   UNLOCK();
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| save_locale_ref(atom_t aref, IOSTREAM *fd)
 | |
| { locale_ref *ref = PL_blob_data(aref, NULL, NULL);
 | |
|   (void)fd;
 | |
| 
 | |
|   return PL_warning("Cannot save reference to <locale>(%p)", ref->data);
 | |
| }
 | |
| 
 | |
| 
 | |
| static atom_t
 | |
| load_locale_ref(IOSTREAM *fd)
 | |
| { (void)fd;
 | |
| 
 | |
|   return PL_new_atom("<saved-locale-ref>");
 | |
| }
 | |
| 
 | |
| 
 | |
| static PL_blob_t locale_blob =
 | |
| { PL_BLOB_MAGIC,
 | |
|   PL_BLOB_UNIQUE,
 | |
|   "locale",
 | |
|   release_locale_ref,
 | |
|   NULL,
 | |
|   write_locale_ref,
 | |
|   acquire_locale_ref,
 | |
|   save_locale_ref,
 | |
|   load_locale_ref
 | |
| };
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	   PROLOG HANDLE	*
 | |
| 		 *******************************/
 | |
| 
 | |
| int
 | |
| unifyLocale(term_t t, PL_locale *l, int alias)
 | |
| { GET_LD
 | |
|   term_t b;
 | |
| 
 | |
|   if ( l->alias && alias )
 | |
|     return PL_unify_atom(t, l->alias);
 | |
| 
 | |
|   if ( l->symbol )
 | |
|     return PL_unify_atom(t, l->symbol);
 | |
| 
 | |
|   if ( (b=PL_new_term_ref()) &&
 | |
|        PL_put_blob(b, &l, sizeof(l), &locale_blob) )
 | |
|   { PL_get_atom(b, &l->symbol);
 | |
|     assert(l->symbol);
 | |
|     return PL_unify(t, b);
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| getLocale(term_t t, PL_locale **lp)
 | |
| { GET_LD
 | |
|   atom_t a;
 | |
| 
 | |
|   if ( PL_get_atom(t, &a) )
 | |
|   { PL_locale *l = NULL;
 | |
|     PL_blob_t *bt;
 | |
|     locale_ref *ref;
 | |
| 
 | |
|     if ( a == ATOM_current_locale )
 | |
|     { GET_LD
 | |
| 
 | |
|       l = LD->locale.current;
 | |
|     } else if ( (ref=PL_blob_data(a, NULL, &bt)) && bt == &locale_blob )
 | |
|     { l = ref->data;
 | |
|     } else if ( GD->locale.localeTable )
 | |
|     { Symbol s;
 | |
| 
 | |
|       if ( (s=lookupHTable(GD->locale.localeTable, (void*)a)) )
 | |
| 	l = s->value;
 | |
|     }
 | |
| 
 | |
|     if ( l )
 | |
|     { assert(l->magic == LOCALE_MAGIC);
 | |
|       *lp = acquireLocale(l);
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| getLocaleEx(term_t t, PL_locale **lp)
 | |
| { GET_LD
 | |
| 
 | |
|   if ( getLocale(t, lp) )
 | |
|     return TRUE;
 | |
| 
 | |
|   if ( PL_is_atom(t) )
 | |
|     return PL_existence_error("locale", t);
 | |
|   else
 | |
|     return PL_type_error("locale", t);
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	 PROLOG BINDING		*
 | |
| 		 *******************************/
 | |
| 
 | |
| static int		/* locale_property(Mutex, alias(Name)) */
 | |
| locale_alias_property(PL_locale *l, term_t prop ARG_LD)
 | |
| { if ( l->alias )
 | |
|     return PL_unify_atom(prop, l->alias);
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static int		/* locale_property(Locale, decimal_point(Atom)) */
 | |
| locale_decimal_point_property(PL_locale *l, term_t prop ARG_LD)
 | |
| { if ( l->decimal_point && l->decimal_point[0] )
 | |
|     return PL_unify_wchars(prop, PL_ATOM, (size_t)-1, l->decimal_point);
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static int		/* locale_property(Locale, thousands_sep(Atom)) */
 | |
| locale_thousands_sep_property(PL_locale *l, term_t prop ARG_LD)
 | |
| { if ( l->thousands_sep && l->thousands_sep[0] )
 | |
|     return PL_unify_wchars(prop, PL_ATOM, (size_t)-1, l->thousands_sep);
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static int		/* locale_property(Locale, grouping(List)) */
 | |
| locale_grouping_property(PL_locale *l, term_t prop ARG_LD)
 | |
| { if ( l->grouping && l->grouping[0] )
 | |
|   { term_t tail = PL_copy_term_ref(prop);
 | |
|     term_t head = PL_new_term_ref();
 | |
|     char *s;
 | |
| 
 | |
|     for(s=l->grouping; ; s++)
 | |
|     { if ( !PL_unify_list(tail, head, tail) )
 | |
| 	return FALSE;
 | |
|       if ( s[1] == 0 || (s[1] == s[0] && s[2] == 0) )
 | |
| 	return ( PL_unify_term(head, PL_FUNCTOR, FUNCTOR_repeat1,
 | |
| 			       PL_INT, (int)s[0]) &&
 | |
| 		 PL_unify_nil(tail)
 | |
| 	       );
 | |
|       if ( s[0] == CHAR_MAX )
 | |
| 	return PL_unify_nil(tail);
 | |
|       if ( !PL_unify_integer(head, s[0]) )
 | |
| 	return FALSE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| typedef struct
 | |
| { functor_t functor;			/* functor of property */
 | |
|    int (*function)();			/* function to generate */
 | |
| } lprop;
 | |
| 
 | |
| static const lprop lprop_list [] =
 | |
| { { FUNCTOR_alias1,	    locale_alias_property },
 | |
|   { FUNCTOR_decimal_point1, locale_decimal_point_property },
 | |
|   { FUNCTOR_thousands_sep1, locale_thousands_sep_property },
 | |
|   { FUNCTOR_grouping1,      locale_grouping_property },
 | |
|   { 0,			    NULL }
 | |
| };
 | |
| 
 | |
| typedef struct
 | |
| { TableEnum e;				/* Enumerator on mutex-table */
 | |
|   PL_locale *l;				/* current locale */
 | |
|   const lprop *p;			/* Pointer in properties */
 | |
|   int enum_properties;			/* Enumerate the properties */
 | |
| } lprop_enum;
 | |
| 
 | |
| 
 | |
| static int
 | |
| get_prop_def(term_t t, atom_t expected, const lprop *list, const lprop **def)
 | |
| { GET_LD
 | |
|   functor_t f;
 | |
| 
 | |
|   if ( PL_get_functor(t, &f) )
 | |
|   { const lprop *p = list;
 | |
| 
 | |
|     for( ; p->functor; p++ )
 | |
|     { if ( f == p->functor )
 | |
|       { *def = p;
 | |
|         return TRUE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     PL_error(NULL, 0, NULL, ERR_DOMAIN, expected, t);
 | |
|     return -1;
 | |
|   }
 | |
| 
 | |
|   if ( PL_is_variable(t) )
 | |
|     return 0;
 | |
| 
 | |
|   PL_error(NULL, 0, NULL, ERR_TYPE, expected, t);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| advance_lstate(lprop_enum *state)
 | |
| { if ( state->enum_properties )
 | |
|   { state->p++;
 | |
|     if ( state->p->functor )
 | |
|       return TRUE;
 | |
| 
 | |
|     state->p = lprop_list;
 | |
|   }
 | |
|   if ( state->e )
 | |
|   { Symbol s;
 | |
| 
 | |
|     if ( (s = advanceTableEnum(state->e)) )
 | |
|     { state->l = s->value;
 | |
| 
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| free_lstate(lprop_enum *state)
 | |
| { if ( state->e )
 | |
|     freeTableEnum(state->e);
 | |
|   else if ( state->l )
 | |
|     releaseLocale(state->l);
 | |
| 
 | |
|   freeForeignState(state, sizeof(*state));
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| get_atom_arg(term_t t, atom_t *a)
 | |
| { GET_LD
 | |
|   term_t t2 = PL_new_term_ref();
 | |
| 
 | |
|   return PL_get_arg(1, t, t2) && PL_get_atom(t2, a);
 | |
| }
 | |
| 
 | |
| 
 | |
| /** locale_property(?Locale, ?Property) is nondet.
 | |
| */
 | |
| /// @memberof locale_property/2
 | |
| static
 | |
| PRED_IMPL("locale_property", 2, locale_property, PL_FA_NONDETERMINISTIC)
 | |
| { PRED_LD
 | |
|   term_t locale = A1;
 | |
|   term_t property = A2;
 | |
|   lprop_enum statebuf;
 | |
|   lprop_enum *state = NULL;
 | |
| 
 | |
|   switch( CTX_CNTRL )
 | |
|   { case FRG_FIRST_CALL:
 | |
|     { memset(&statebuf, 0, sizeof(statebuf));
 | |
|       state = &statebuf;
 | |
| 
 | |
|       if ( PL_is_variable(locale) )
 | |
|       { switch( get_prop_def(property, ATOM_locale_property,
 | |
| 			     lprop_list, &state->p) )
 | |
| 	{ case 1:
 | |
| 	  { atom_t alias;
 | |
| 
 | |
| 	    if ( state->p->functor == FUNCTOR_alias1 &&
 | |
| 		 get_atom_arg(property, &alias) )
 | |
| 	    { Symbol s;
 | |
| 
 | |
| 	      if ( (s=lookupHTable(GD->locale.localeTable, (void*)alias)) )
 | |
| 		return unifyLocale(locale, s->value, FALSE);
 | |
| 	      else
 | |
| 		return FALSE;
 | |
| 	    }
 | |
| 	    state->e = newTableEnum(GD->locale.localeTable);
 | |
| 	    goto enumerate;
 | |
| 	  }
 | |
| 	  case 0:
 | |
| 	    state->e = newTableEnum(GD->locale.localeTable);
 | |
| 	    state->p = lprop_list;
 | |
| 	    state->enum_properties = TRUE;
 | |
| 	    goto enumerate;
 | |
| 	  case -1:
 | |
| 	    return FALSE;
 | |
| 	}
 | |
|       } else if ( getLocale(locale, &state->l) )
 | |
|       { switch( get_prop_def(property, ATOM_locale_property,
 | |
| 			     lprop_list, &state->p) )
 | |
| 	{ case 1:
 | |
| 	    goto enumerate;
 | |
| 	  case 0:
 | |
| 	    state->p = lprop_list;
 | |
| 	    state->enum_properties = TRUE;
 | |
| 	    goto enumerate;
 | |
| 	  case -1:
 | |
| 	    return FALSE;
 | |
| 	}
 | |
|       } else
 | |
|       { return FALSE;
 | |
|       }
 | |
|     }
 | |
|     case FRG_REDO:
 | |
|       state = CTX_PTR;
 | |
|       break;
 | |
|     case FRG_CUTTED:
 | |
|       state = CTX_PTR;
 | |
|       free_lstate(state);
 | |
|       succeed;
 | |
|     default:
 | |
|       assert(0);
 | |
|   }
 | |
| 
 | |
| enumerate:
 | |
|   if ( !state->l )			/* first time, enumerating locales */
 | |
|   { Symbol s;
 | |
| 
 | |
|     assert(state->e);
 | |
|     if ( (s=advanceTableEnum(state->e)) )
 | |
|     { state->l = s->value;
 | |
|     } else
 | |
|     { freeTableEnum(state->e);
 | |
|       assert(state != &statebuf);
 | |
|       return FALSE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   { term_t arg = PL_new_term_ref();
 | |
| 
 | |
|     if ( !state->enum_properties )
 | |
|       _PL_get_arg(1, property, arg);
 | |
| 
 | |
|     for(;;)
 | |
|     { if ( (*state->p->function)(state->l, arg PASS_LD) )
 | |
|       { if ( state->enum_properties )
 | |
| 	{ if ( !PL_unify_term(property,
 | |
| 			      PL_FUNCTOR, state->p->functor,
 | |
| 			        PL_TERM, arg) )
 | |
| 	    goto error;
 | |
| 	}
 | |
| 	if ( state->e )
 | |
| 	{ if ( !unifyLocale(locale, state->l, TRUE) )
 | |
| 	    goto error;
 | |
| 	}
 | |
| 
 | |
| 	if ( advance_lstate(state) )
 | |
| 	{ if ( state == &statebuf )
 | |
| 	  { lprop_enum *copy = allocForeignState(sizeof(*copy));
 | |
| 
 | |
| 	    *copy = *state;
 | |
| 	    state = copy;
 | |
| 	  }
 | |
| 
 | |
| 	  ForeignRedoPtr(state);
 | |
| 	}
 | |
| 
 | |
| 	if ( state != &statebuf )
 | |
| 	  free_lstate(state);
 | |
| 	return TRUE;
 | |
|       }
 | |
| 
 | |
|       if ( !advance_lstate(state) )
 | |
|       { error:
 | |
| 	if ( state != &statebuf )
 | |
| 	  free_lstate(state);
 | |
| 	return FALSE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| set_chars(term_t t, wchar_t **valp)
 | |
| { wchar_t *s;
 | |
| 
 | |
|   if ( PL_get_wchars(t, NULL, &s, CVT_ATOM|CVT_EXCEPTION) )
 | |
|   { free(*valp);
 | |
|     if ( (*valp = wcsdup(s)) )
 | |
|       return TRUE;
 | |
|     return PL_no_memory();
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| #define MAX_GROUPING 10
 | |
| 
 | |
| static int
 | |
| get_group_size_ex(term_t t, int *s)
 | |
| { int i;
 | |
| 
 | |
|   if ( PL_get_integer_ex(t, &i) )
 | |
|   { if ( i > 0 && i < CHAR_MAX )
 | |
|     { *s = i;
 | |
|       return TRUE;
 | |
|     }
 | |
|     return PL_domain_error("digit_group_size", t);
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| set_grouping(term_t t, char **valp)
 | |
| { GET_LD
 | |
|   char s[MAX_GROUPING];
 | |
|   term_t tail = PL_copy_term_ref(t);
 | |
|   term_t head = PL_new_term_ref();
 | |
|   char *o = s;
 | |
| 
 | |
|   while(PL_get_list_ex(tail, head, tail))
 | |
|   { int g;
 | |
| 
 | |
|     if ( o-s+2 >= MAX_GROUPING )
 | |
|       return PL_representation_error("digit_groups");
 | |
| 
 | |
|     if ( PL_is_functor(head, FUNCTOR_repeat1) )
 | |
|     { if ( !PL_get_nil_ex(tail) )
 | |
| 	return FALSE;
 | |
| 
 | |
|       _PL_get_arg(1, head, head);
 | |
|       if ( get_group_size_ex(head, &g) )
 | |
|       { *o++ = g;
 | |
| 	goto end;
 | |
|       }
 | |
|       return FALSE;
 | |
|     }
 | |
|     if ( get_group_size_ex(head, &g) )
 | |
|     { *o++ = g;
 | |
|     } else
 | |
|       return FALSE;
 | |
|   }
 | |
| 
 | |
|   if ( PL_get_nil_ex(tail) )
 | |
|   { *o++ = CHAR_MAX;				/* no more grouping */
 | |
|   end:
 | |
|     *o++ = '\0';
 | |
|     free(*valp);
 | |
|     if ( (*valp = strdup(s)) )
 | |
|       return TRUE;
 | |
|     return PL_no_memory();
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** locale_create(-Locale, +Default, +Options) is det.
 | |
| */
 | |
| /// @memberof locale_create/3
 | |
| static
 | |
| PRED_IMPL("locale_create", 3, locale_create, 0)
 | |
| { PRED_LD
 | |
|   PL_locale *def, *new;
 | |
|   char *lname;
 | |
| 
 | |
|   if ( PL_get_chars(A2, &lname, CVT_LIST|CVT_STRING|REP_MB) )
 | |
|   { const char *old;
 | |
| 
 | |
|     LOCK();
 | |
|     if ( (old=setlocale(LC_NUMERIC, lname)) )
 | |
|     { new = new_locale(NULL);
 | |
|       setlocale(LC_NUMERIC, old);
 | |
|     } else
 | |
|     { assert(0);				/* keep compiler happy */
 | |
|       return FALSE;
 | |
|     }
 | |
|     UNLOCK();
 | |
|     if ( !old )
 | |
|     { if ( errno == ENOENT )
 | |
| 	return PL_existence_error("locale", A2);
 | |
|       else
 | |
| 	return PL_error(NULL, 0, MSG_ERRNO, ERR_SYSCALL, "setlocale");
 | |
|     }
 | |
|   } else
 | |
|   { if ( !getLocaleEx(A2, &def) )
 | |
|       return FALSE;
 | |
|     new = new_locale(def);
 | |
|     releaseLocale(def);
 | |
|   }
 | |
| 
 | |
|   if ( new )
 | |
|   { atom_t alias = 0;
 | |
|     term_t tail = PL_copy_term_ref(A3);
 | |
|     term_t head = PL_new_term_ref();
 | |
|     term_t arg  = PL_new_term_ref();
 | |
| 
 | |
|     while(PL_get_list_ex(tail, head, tail))
 | |
|     { atom_t pname;
 | |
|       int parity;
 | |
| 
 | |
|       if ( !PL_get_name_arity(head, &pname, &parity) ||
 | |
| 	   parity != 1 ||
 | |
| 	   !PL_get_arg(1, head, arg) )
 | |
|       { PL_type_error("locale_property", head);
 | |
| 	goto error;
 | |
|       }
 | |
|       if ( pname == ATOM_alias )
 | |
|       { if ( !PL_get_atom_ex(arg, &alias) )
 | |
| 	  goto error;
 | |
|       } else if ( pname == ATOM_decimal_point )
 | |
|       { if ( !set_chars(arg, &new->decimal_point) )
 | |
| 	  goto error;
 | |
|       } else if ( pname == ATOM_thousands_sep )
 | |
|       { if ( !set_chars(arg, &new->thousands_sep) )
 | |
| 	  goto error;
 | |
|       } else if ( pname == ATOM_grouping )
 | |
|       { if ( !set_grouping(arg, &new->grouping) )
 | |
| 	  goto error;
 | |
|       }
 | |
|     }
 | |
|     if ( !PL_get_nil_ex(tail) )
 | |
|     {
 | |
|     error:
 | |
|       free_locale(new);
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|     if ( alias && !alias_locale(new, alias) )
 | |
|       goto error;
 | |
| 
 | |
|     return unifyLocale(A1, new, TRUE);
 | |
|   } else
 | |
|   { return PL_no_memory();
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /** locale_destroy(+Locale) is det.
 | |
| */
 | |
| /// @memberof locale_destroy/1
 | |
| static
 | |
| PRED_IMPL("locale_destroy", 1, locale_destroy, 0)
 | |
| { PL_locale *l;
 | |
| 
 | |
|   if ( getLocaleEx(A1, &l) )
 | |
|   { if ( l->alias )
 | |
|     { Symbol s;
 | |
|       atom_t alias = l->alias;
 | |
| 
 | |
|       LOCK();
 | |
|       if ( (s=lookupHTable(GD->locale.localeTable, (void*)alias)) )
 | |
| 	deleteSymbolHTable(GD->locale.localeTable, s);
 | |
|       l->alias = 0;
 | |
|       PL_unregister_atom(alias);
 | |
|       UNLOCK();
 | |
|     }
 | |
| 
 | |
|     releaseLocale(l);
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** set_locale(+Locale) is det.
 | |
| */
 | |
| /// @memberof set_locale/1
 | |
| static
 | |
| PRED_IMPL("set_locale", 1, set_locale, 0)
 | |
| { PRED_LD
 | |
|   PL_locale *l = NULL;
 | |
| 
 | |
|   if ( getLocaleEx(A1, &l) )
 | |
|   { PL_locale *ol = LD->locale.current;
 | |
| 
 | |
|     if ( l != ol )
 | |
|     { IOSTREAM **sp;
 | |
| 
 | |
|       LD->locale.current = l;		/* already acquired */
 | |
|       if ( ol )
 | |
| 	releaseLocale(ol);
 | |
| 
 | |
|       if ( (sp=_PL_streams()) )		/* set locale of standard streams */
 | |
|       { int i;
 | |
| 
 | |
| 	for(i=0; i<5; i++)
 | |
| 	  Ssetlocale(sp[i], l, NULL);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** current_locale(-Locale) is det.
 | |
| */
 | |
| /// @memberof current_locale/1
 | |
| static
 | |
| PRED_IMPL("current_locale", 1, current_locale, 0)
 | |
| { PRED_LD
 | |
| 
 | |
|   if ( LD->locale.current )
 | |
|     return unifyLocale(A1, LD->locale.current, TRUE);
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	     C INTERFACE	*
 | |
| 		 *******************************/
 | |
| 
 | |
| 
 | |
| static void
 | |
| initDefaultsStreamsLocale(PL_locale *l)
 | |
| { IOSTREAM *s = S__getiob();
 | |
|   int i;
 | |
| 
 | |
|   for(i=0; i<2; i++, s++)
 | |
|   { if ( !s->locale )
 | |
|       s->locale = acquireLocale(l);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| initLocale(void)
 | |
| { GET_LD
 | |
|   PL_locale *def;
 | |
| 
 | |
|   if ( !setlocale(LC_NUMERIC, "") )
 | |
|   { DEBUG(0, Sdprintf("Failed to set LC_NUMERIC locale\n"));
 | |
|   }
 | |
| 
 | |
|   if ( (def = new_locale(NULL)) )
 | |
|   { alias_locale(def, ATOM_default);
 | |
|     def->references++;
 | |
|     GD->locale.default_locale = def;
 | |
|     LD->locale.current = acquireLocale(def);
 | |
| 
 | |
|     initDefaultsStreamsLocale(def);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| updateLocale(int category, const char *locale)
 | |
| { update_locale(GD->locale.default_locale, category, locale);
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| initStreamLocale(IOSTREAM *s)
 | |
| { GET_LD
 | |
|   PL_locale *l;
 | |
| 
 | |
|   if ( LD )					/* a Prolog thread */
 | |
|     l = LD->locale.current;
 | |
|   else
 | |
|     l = GD->locale.default_locale;
 | |
| 
 | |
|   if ( l )
 | |
|     s->locale = acquireLocale(l);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| PL_locale *
 | |
| acquireLocale(PL_locale *l)
 | |
| { LOCK();
 | |
|   l->references++;
 | |
|   UNLOCK();
 | |
| 
 | |
|   return l;
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| releaseLocale(PL_locale *l)
 | |
| { LOCK();
 | |
|   if ( --l->references == 0 && l->symbol == 0 && l->alias == 0 )
 | |
|     free_locale(l);
 | |
|   UNLOCK();
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *      PUBLISH PREDICATES	*
 | |
| 		 *******************************/
 | |
| 
 | |
| BeginPredDefs(locale)
 | |
|   PRED_DEF("locale_property", 2, locale_property, PL_FA_NONDETERMINISTIC)
 | |
|   PRED_DEF("locale_create",   3, locale_create,   0)
 | |
|   PRED_DEF("locale_destroy",  1, locale_destroy,  0)
 | |
|   PRED_DEF("set_locale",      1, set_locale,      0)
 | |
|   PRED_DEF("current_locale",  1, current_locale,  0)
 | |
| EndPredDefs
 | |
| 
 | |
| #endif /*O_LOCALE*/
 | |
| 
 | |
| /// @}
 |