* Makefile.in (DLL_OFILES): Add nlsfunc.o and strfmon.o.

* autoload.cc (LocaleNameToLCID): Define.
	* cygwin.din (strfmon): Export.
	* nlsfuncs.cc: New file.  Define a lot of internal functions called
	from setlocale.
	(wcscoll): Implement locale-aware here, using CompareStringW function.
	(strcoll): Ditto.
	(wcsxfrm): Implement locale-aware here, usingLCMapStringW function.
	(strxfrm): Ditto.
	(__set_charset_from_locale): Replace __set_charset_from_codepage.
	Return Linux-compatible charset.
	* strfuncs.cc (__set_charset_from_codepage): Remove.
	* wchar.h (__set_charset_from_codepage): Drop definition.
	* wincap.h (wincaps::has_localenames): New element.
	* wincap.cc: Implement above element throughout.
	* libc/strfmon.c: New file.
	* libc/strptime.cc: Remove locale constant strings in favor of
	access to locale-specifc data.
	(strptime): Point _CurrentTimeLocale to locale-specific data.
	Throughout use correct locale-specific format fields for all
	locale-specific formats.
	* include/monetary.h: New file.
	* include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump.
This commit is contained in:
Corinna Vinschen 2010-01-22 22:31:31 +00:00
parent e894eef9f5
commit 326fb376dd
13 changed files with 1491 additions and 145 deletions

View File

@ -1,3 +1,29 @@
2010-01-22 Corinna Vinschen <corinna@vinschen.de>
* Makefile.in (DLL_OFILES): Add nlsfunc.o and strfmon.o.
* autoload.cc (LocaleNameToLCID): Define.
* cygwin.din (strfmon): Export.
* nlsfuncs.cc: New file. Define a lot of internal functions called
from setlocale.
(wcscoll): Implement locale-aware here, using CompareStringW function.
(strcoll): Ditto.
(wcsxfrm): Implement locale-aware here, usingLCMapStringW function.
(strxfrm): Ditto.
(__set_charset_from_locale): Replace __set_charset_from_codepage.
Return Linux-compatible charset.
* strfuncs.cc (__set_charset_from_codepage): Remove.
* wchar.h (__set_charset_from_codepage): Drop definition.
* wincap.h (wincaps::has_localenames): New element.
* wincap.cc: Implement above element throughout.
* libc/strfmon.c: New file.
* libc/strptime.cc: Remove locale constant strings in favor of
access to locale-specifc data.
(strptime): Point _CurrentTimeLocale to locale-specific data.
Throughout use correct locale-specific format fields for all
locale-specific formats.
* include/monetary.h: New file.
* include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump.
2010-01-18 Corinna Vinschen <corinna@vinschen.de>
* strfuncs.cc: Remove needless includes.

View File

@ -1,6 +1,6 @@
# Makefile.in for Cygwin.
# Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005, 2006, 2007, 2008, 2009 Red Hat, Inc.
# 2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
#
# This file is part of Cygwin.
#
@ -148,12 +148,12 @@ DLL_OFILES:=assert.o autoload.o bsdlib.o ctype.o cxx.o cygheap.o cygthread.o \
glob_pattern_p.o globals.o grp.o heap.o hookapi.o inet_addr.o inet_network.o \
init.o ioctl.o ipc.o kernel32.o libstdcxx_wrapper.o localtime.o lsearch.o \
malloc_wrapper.o minires-os-if.o minires.o miscfuncs.o mktemp.o mmap.o msg.o \
mount.o net.o netdb.o nfs.o nftw.o ntea.o passwd.o path.o pinfo.o pipe.o \
poll.o posix_ipc.o pthread.o random.o regcomp.o regerror.o regexec.o \
regfree.o registry.o resource.o rexec.o rcmd.o scandir.o sched.o \
sec_acl.o sec_auth.o sec_helper.o security.o select.o sem.o \
setlsapwd.o shared.o shm.o sigfe.o signal.o sigproc.o smallprint.o \
spawn.o strace.o strfuncs.o strptime.o strsep.o strsig.o sync.o \
mount.o net.o netdb.o nfs.o nftw.o nlsfuncs.o ntea.o passwd.o path.o \
pinfo.o pipe.o poll.o posix_ipc.o pthread.o random.o regcomp.o \
regerror.o regexec.o regfree.o registry.o resource.o rexec.o rcmd.o \
scandir.o sched.o sec_acl.o sec_auth.o sec_helper.o security.o select.o \
sem.o setlsapwd.o shared.o shm.o sigfe.o signal.o sigproc.o smallprint.o \
spawn.o strace.o strfmon.o strfuncs.o strptime.o strsep.o strsig.o sync.o \
syscalls.o sysconf.o syslog.o termios.o thread.o timer.o times.o \
tls_pbuf.o tty.o uinfo.o uname.o wait.o wincap.o window.o winf.o \
xsique.o \

View File

@ -1,6 +1,7 @@
/* autoload.cc: all dynamic load stuff.
Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.
Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
2009, 2010 Red Hat, Inc.
This file is part of Cygwin.
@ -422,6 +423,7 @@ LoadDLLfuncEx (GetSystemWindowsDirectoryW, 8, kernel32, 1)
LoadDLLfuncEx (GetVolumeNameForVolumeMountPointA, 12, kernel32, 1)
LoadDLLfuncEx (GetSystemDEPPolicy, 0, kernel32, 1)
LoadDLLfuncEx (GetProcessDEPPolicy, 12, kernel32, 1)
LoadDLLfunc (LocaleNameToLCID, 8, kernel32)
LoadDLLfuncEx (SetProcessDEPPolicy, 4, kernel32, 1)
LoadDLLfunc (SHGetDesktopFolder, 4, shell32)

View File

@ -1527,6 +1527,7 @@ strerror SIGFE
_strerror = strerror SIGFE
strerror_r SIGFE
_strerror_r = strerror_r SIGFE
strfmon SIGFE
strftime SIGFE
_strftime = strftime SIGFE
strlcat NOSIGFE

View File

@ -374,12 +374,13 @@ details. */
218: Export get_nprocs, get_nprocs_conf, get_phys_pages, get_avphys_pages.
219: Export dup3, pipe2, O_CLOEXEC, F_DUPFD_CLOEXEC.
220: Export accept4, SOCK_CLOEXEC, SOCK_NONBLOCK.
221: Export strfmon.
*/
/* Note that we forgot to bump the api for ualarm, strtoll, strtoull */
#define CYGWIN_VERSION_API_MAJOR 0
#define CYGWIN_VERSION_API_MINOR 220
#define CYGWIN_VERSION_API_MINOR 221
/* There is also a compatibity version number associated with the
shared memory regions. It is incremented when incompatible

View File

@ -0,0 +1,39 @@
/*-
* Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _MONETARY_H_
#define _MONETARY_H_
#include <sys/cdefs.h>
#include <sys/types.h>
__BEGIN_DECLS
ssize_t strfmon(char * __restrict, size_t, const char * __restrict, ...);
__END_DECLS
#endif /* !_MONETARY_H_ */

View File

@ -0,0 +1,621 @@
/*-
* Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
#if 0
__FBSDID("$FreeBSD: src/lib/libc/stdlib/strfmon.c,v 1.19 2008/04/24 07:49:00 ru Exp $");
#endif
#include "winsup.h"
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <monetary.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* internal flags */
#define NEED_GROUPING 0x01 /* print digits grouped (default) */
#define SIGN_POSN_USED 0x02 /* '+' or '(' usage flag */
#define LOCALE_POSN 0x04 /* use locale defined +/- (default) */
#define PARENTH_POSN 0x08 /* enclose negative amount in () */
#define SUPRESS_CURR_SYMBOL 0x10 /* supress the currency from output */
#define LEFT_JUSTIFY 0x20 /* left justify */
#define USE_INTL_CURRENCY 0x40 /* use international currency symbol */
#define IS_NEGATIVE 0x80 /* is argument value negative ? */
/* internal macros */
#define PRINT(CH) do { \
if (dst >= s + maxsize) \
goto e2big_error; \
*dst++ = CH; \
} while (0)
#define PRINTS(STR) do { \
char *tmps = STR; \
while (*tmps != '\0') \
PRINT(*tmps++); \
} while (0)
#define GET_NUMBER(VAR) do { \
VAR = 0; \
while (isdigit((unsigned char)*fmt)) { \
if (VAR > INT_MAX / 10) \
goto e2big_error; \
VAR *= 10; \
VAR += *fmt - '0'; \
if (VAR < 0) \
goto e2big_error; \
fmt++; \
} \
} while (0)
#define GRPCPY(howmany) do { \
int i = howmany; \
while (i-- > 0) { \
avalue_size--; \
*--bufend = *(avalue+avalue_size+padded); \
} \
} while (0)
#define GRPSEP do { \
*--bufend = thousands_sep; \
groups++; \
} while (0)
static void __setup_vars(int, char *, char *, char *, char **);
static int __calc_left_pad(int, char *);
static char *__format_grouped_double(double, int *, int, int, int);
ssize_t
strfmon(char * __restrict s, size_t maxsize, const char * __restrict format,
...)
{
va_list ap;
char *dst; /* output destination pointer */
const char *fmt; /* current format poistion pointer */
struct lconv *lc; /* pointer to lconv structure */
char *asciivalue; /* formatted double pointer */
int flags; /* formatting options */
int pad_char; /* padding character */
int pad_size; /* pad size */
int width; /* field width */
int left_prec; /* left precision */
int right_prec; /* right precision */
double value; /* just value */
char space_char = ' '; /* space after currency */
char cs_precedes, /* values gathered from struct lconv */
sep_by_space,
sign_posn,
*signstr,
*currency_symbol;
char *tmpptr; /* temporary vars */
int sverrno;
va_start(ap, format);
lc = localeconv();
dst = s;
fmt = format;
asciivalue = NULL;
currency_symbol = NULL;
pad_size = 0;
while (*fmt) {
/* pass nonformating characters AS IS */
if (*fmt != '%')
goto literal;
/* '%' found ! */
/* "%%" mean just '%' */
if (*(fmt+1) == '%') {
fmt++;
literal:
PRINT(*fmt++);
continue;
}
/* set up initial values */
flags = (NEED_GROUPING|LOCALE_POSN);
pad_char = ' '; /* padding character is "space" */
left_prec = -1; /* no left precision specified */
right_prec = -1; /* no right precision specified */
width = -1; /* no width specified */
value = 0; /* we have no value to print now */
/* Flags */
while (1) {
switch (*++fmt) {
case '=': /* fill character */
pad_char = *++fmt;
if (pad_char == '\0')
goto format_error;
continue;
case '^': /* not group currency */
flags &= ~(NEED_GROUPING);
continue;
case '+': /* use locale defined signs */
if (flags & SIGN_POSN_USED)
goto format_error;
flags |= (SIGN_POSN_USED|LOCALE_POSN);
continue;
case '(': /* enclose negatives with () */
if (flags & SIGN_POSN_USED)
goto format_error;
flags |= (SIGN_POSN_USED|PARENTH_POSN);
continue;
case '!': /* suppress currency symbol */
flags |= SUPRESS_CURR_SYMBOL;
continue;
case '-': /* alignment (left) */
flags |= LEFT_JUSTIFY;
continue;
default:
break;
}
break;
}
/* field Width */
if (isdigit((unsigned char)*fmt)) {
GET_NUMBER(width);
/* Do we have enough space to put number with
* required width ?
*/
if ((unsigned int)width >= maxsize - (dst - s))
goto e2big_error;
}
/* Left precision */
if (*fmt == '#') {
if (!isdigit((unsigned char)*++fmt))
goto format_error;
GET_NUMBER(left_prec);
if ((unsigned int)left_prec >= maxsize - (dst - s))
goto e2big_error;
}
/* Right precision */
if (*fmt == '.') {
if (!isdigit((unsigned char)*++fmt))
goto format_error;
GET_NUMBER(right_prec);
if ((unsigned int)right_prec >= maxsize - (dst - s) -
left_prec)
goto e2big_error;
}
/* Conversion Characters */
switch (*fmt++) {
case 'i': /* use internaltion currency format */
flags |= USE_INTL_CURRENCY;
break;
case 'n': /* use national currency format */
flags &= ~(USE_INTL_CURRENCY);
break;
default: /* required character is missing or
premature EOS */
goto format_error;
}
if (currency_symbol != NULL)
free(currency_symbol);
if (flags & USE_INTL_CURRENCY) {
currency_symbol = strdup(lc->int_curr_symbol);
if (currency_symbol != NULL) {
space_char = *(currency_symbol+3);
*(currency_symbol+3) = '\0';
}
} else
currency_symbol = strdup(lc->currency_symbol);
if (currency_symbol == NULL)
goto end_error; /* ENOMEM. */
/* value itself */
value = va_arg(ap, double);
/* detect sign */
if (value < 0) {
flags |= IS_NEGATIVE;
value = -value;
}
/* fill left_prec with amount of padding chars */
if (left_prec >= 0) {
pad_size = __calc_left_pad((flags ^ IS_NEGATIVE),
currency_symbol) -
__calc_left_pad(flags, currency_symbol);
if (pad_size < 0)
pad_size = 0;
}
if (asciivalue != NULL)
free(asciivalue);
asciivalue = __format_grouped_double(value, &flags,
left_prec, right_prec, pad_char);
if (asciivalue == NULL)
goto end_error; /* errno already set */
/* to ENOMEM by malloc() */
/* set some variables for later use */
__setup_vars(flags, &cs_precedes, &sep_by_space,
&sign_posn, &signstr);
/*
* Description of some LC_MONETARY's values:
*
* p_cs_precedes & n_cs_precedes
*
* = 1 - $currency_symbol precedes the value
* for a monetary quantity with a non-negative value
* = 0 - symbol succeeds the value
*
* p_sep_by_space & n_sep_by_space
*
* = 0 - no space separates $currency_symbol
* from the value for a monetary quantity with a
* non-negative value
* = 1 - space separates the symbol from the value
* = 2 - space separates the symbol and the sign string,
* if adjacent.
*
* p_sign_posn & n_sign_posn
*
* = 0 - parentheses enclose the quantity and the
* $currency_symbol
* = 1 - the sign string precedes the quantity and the
* $currency_symbol
* = 2 - the sign string succeeds the quantity and the
* $currency_symbol
* = 3 - the sign string precedes the $currency_symbol
* = 4 - the sign string succeeds the $currency_symbol
*
*/
tmpptr = dst;
while (pad_size-- > 0)
PRINT(' ');
if (sign_posn == 0 && (flags & IS_NEGATIVE))
PRINT('(');
if (cs_precedes == 1) {
if (sign_posn == 1 || sign_posn == 3) {
PRINTS(signstr);
if (sep_by_space == 2) /* XXX: ? */
PRINT(' ');
}
if (!(flags & SUPRESS_CURR_SYMBOL)) {
PRINTS(currency_symbol);
if (sign_posn == 4) {
if (sep_by_space == 2)
PRINT(space_char);
PRINTS(signstr);
if (sep_by_space == 1)
PRINT(' ');
} else if (sep_by_space == 1)
PRINT(space_char);
}
} else if (sign_posn == 1)
PRINTS(signstr);
PRINTS(asciivalue);
if (cs_precedes == 0) {
if (sign_posn == 3) {
if (sep_by_space == 1)
PRINT(' ');
PRINTS(signstr);
}
if (!(flags & SUPRESS_CURR_SYMBOL)) {
if ((sign_posn == 3 && sep_by_space == 2)
|| (sep_by_space == 1
&& (sign_posn == 0
|| sign_posn == 1
|| sign_posn == 2
|| sign_posn == 4)))
PRINT(space_char);
PRINTS(currency_symbol); /* XXX: len */
if (sign_posn == 4) {
if (sep_by_space == 2)
PRINT(' ');
PRINTS(signstr);
}
}
}
if (sign_posn == 2) {
if (sep_by_space == 2)
PRINT(' ');
PRINTS(signstr);
}
if (sign_posn == 0 && (flags & IS_NEGATIVE))
PRINT(')');
if (dst - tmpptr < width) {
if (flags & LEFT_JUSTIFY) {
while (dst - tmpptr < width)
PRINT(' ');
} else {
pad_size = dst-tmpptr;
memmove(tmpptr + width-pad_size, tmpptr,
pad_size);
memset(tmpptr, ' ', width-pad_size);
dst += width-pad_size;
}
}
}
PRINT('\0');
va_end(ap);
free(asciivalue);
free(currency_symbol);
return (dst - s - 1); /* return size of put data except trailing '\0' */
e2big_error:
errno = E2BIG;
goto end_error;
format_error:
errno = EINVAL;
end_error:
sverrno = errno;
if (asciivalue != NULL)
free(asciivalue);
if (currency_symbol != NULL)
free(currency_symbol);
errno = sverrno;
va_end(ap);
return (-1);
}
static void
__setup_vars(int flags, char *cs_precedes, char *sep_by_space,
char *sign_posn, char **signstr) {
struct lconv *lc = localeconv();
if ((flags & IS_NEGATIVE) && (flags & USE_INTL_CURRENCY)) {
*cs_precedes = lc->int_n_cs_precedes;
*sep_by_space = lc->int_n_sep_by_space;
*sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_n_sign_posn;
*signstr = (lc->negative_sign == '\0') ? "-"
: lc->negative_sign;
} else if (flags & USE_INTL_CURRENCY) {
*cs_precedes = lc->int_p_cs_precedes;
*sep_by_space = lc->int_p_sep_by_space;
*sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_p_sign_posn;
*signstr = lc->positive_sign;
} else if (flags & IS_NEGATIVE) {
*cs_precedes = lc->n_cs_precedes;
*sep_by_space = lc->n_sep_by_space;
*sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn;
*signstr = (lc->negative_sign == '\0') ? "-"
: lc->negative_sign;
} else {
*cs_precedes = lc->p_cs_precedes;
*sep_by_space = lc->p_sep_by_space;
*sign_posn = (flags & PARENTH_POSN) ? 0 : lc->p_sign_posn;
*signstr = lc->positive_sign;
}
/* Set defult values for unspecified information. */
if (*cs_precedes != 0)
*cs_precedes = 1;
if (*sep_by_space == CHAR_MAX)
*sep_by_space = 0;
if (*sign_posn == CHAR_MAX)
*sign_posn = 0;
}
static int
__calc_left_pad(int flags, char *cur_symb) {
char cs_precedes, sep_by_space, sign_posn, *signstr;
int left_chars = 0;
__setup_vars(flags, &cs_precedes, &sep_by_space, &sign_posn, &signstr);
if (cs_precedes != 0) {
left_chars += strlen(cur_symb);
if (sep_by_space != 0)
left_chars++;
}
switch (sign_posn) {
case 1:
left_chars += strlen(signstr);
break;
case 3:
case 4:
if (cs_precedes != 0)
left_chars += strlen(signstr);
}
return (left_chars);
}
static int
get_groups(int size, char *grouping) {
int chars = 0;
if (*grouping == CHAR_MAX || *grouping <= 0) /* no grouping ? */
return (0);
while (size > (int)*grouping) {
chars++;
size -= (int)*grouping++;
/* no more grouping ? */
if (*grouping == CHAR_MAX)
break;
/* rest grouping with same value ? */
if (*grouping == 0) {
chars += (size - 1) / *(grouping - 1);
break;
}
}
return (chars);
}
/* convert double to ASCII */
static char *
__format_grouped_double(double value, int *flags,
int left_prec, int right_prec, int pad_char) {
char *rslt;
char *avalue;
int avalue_size;
char fmt[32];
size_t bufsize;
char *bufend;
int padded;
struct lconv *lc = localeconv();
char *grouping;
char decimal_point;
char thousands_sep;
int groups = 0;
grouping = lc->mon_grouping;
decimal_point = *lc->mon_decimal_point;
if (decimal_point == '\0')
decimal_point = *lc->decimal_point;
thousands_sep = *lc->mon_thousands_sep;
if (thousands_sep == '\0')
thousands_sep = *lc->thousands_sep;
/* fill left_prec with default value */
if (left_prec == -1)
left_prec = 0;
/* fill right_prec with default value */
if (right_prec == -1) {
if (*flags & USE_INTL_CURRENCY)
right_prec = lc->int_frac_digits;
else
right_prec = lc->frac_digits;
if (right_prec == CHAR_MAX) /* POSIX locale ? */
right_prec = 2;
}
if (*flags & NEED_GROUPING)
left_prec += get_groups(left_prec, grouping);
/* convert to string */
snprintf(fmt, sizeof(fmt), "%%%d.%df", left_prec + right_prec + 1,
right_prec);
avalue_size = asprintf(&avalue, fmt, value);
if (avalue_size < 0)
return (NULL);
/* make sure that we've enough space for result string */
bufsize = strlen(avalue)*2+1;
rslt = calloc(1, bufsize);
if (rslt == NULL) {
free(avalue);
return (NULL);
}
bufend = rslt + bufsize - 1; /* reserve space for trailing '\0' */
/* skip spaces at beggining */
padded = 0;
while (avalue[padded] == ' ') {
padded++;
avalue_size--;
}
if (right_prec > 0) {
bufend -= right_prec;
memcpy(bufend, avalue + avalue_size+padded-right_prec,
right_prec);
*--bufend = decimal_point;
avalue_size -= (right_prec + 1);
}
if ((*flags & NEED_GROUPING) &&
thousands_sep != '\0' && /* XXX: need investigation */
*grouping != CHAR_MAX &&
*grouping > 0) {
while (avalue_size > (int)*grouping) {
GRPCPY(*grouping);
GRPSEP;
grouping++;
/* no more grouping ? */
if (*grouping == CHAR_MAX)
break;
/* rest grouping with same value ? */
if (*grouping == 0) {
grouping--;
while (avalue_size > *grouping) {
GRPCPY(*grouping);
GRPSEP;
}
}
}
if (avalue_size != 0)
GRPCPY(avalue_size);
padded -= groups;
} else {
bufend -= avalue_size;
memcpy(bufend, avalue+padded, avalue_size);
if (right_prec == 0)
padded--; /* decrease assumed $decimal_point */
}
/* do padding with pad_char */
if (padded > 0) {
bufend -= padded;
memset(bufend, pad_char, padded);
}
bufsize = bufsize - (bufend - rslt) + 1;
memmove(rslt, bufend, bufsize);
free(avalue);
return (rslt);
}

View File

@ -45,53 +45,12 @@ __RCSID("$NetBSD: strptime.c,v 1.28 2008/04/28 20:23:01 martin Exp $");
#include <string.h>
#include <time.h>
#include <tzfile.h>
#include "../locale/timelocal.h"
#ifdef __weak_alias
__weak_alias(strptime,_strptime)
#endif
#ifdef __CYGWIN__
typedef struct {
const char *abday[7];
const char *day[7];
const char *abmon[12];
const char *mon[12];
const char *am_pm[2];
const char *d_t_fmt;
const char *d_fmt;
const char *t_fmt;
const char *t_fmt_ampm;
} _TimeLocale;
_TimeLocale _DefaultTimeLocale =
{
{
"Sun","Mon","Tue","Wed","Thu","Fri","Sat",
},
{
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday"
},
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
},
{
"January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"
},
{
"AM", "PM"
},
"%a %b %e %H:%M:%S %Y",
"%m/%d/%y",
"%H:%M:%S",
"%I:%M:%S %p"
};
_TimeLocale *_CurrentTimeLocale = &_DefaultTimeLocale;
#endif
#define _ctloc(x) (_CurrentTimeLocale->x)
/*
@ -118,6 +77,7 @@ strptime(const char *buf, const char *fmt, struct tm *tm)
const char *new_fmt;
bp = (const u_char *)buf;
struct lc_time_T *_CurrentTimeLocale = __get_current_time_locale ();
while (bp != NULL && (c = *fmt++) != '\0') {
/* Clear `alternate' modifier prior to new conversion. */
@ -161,7 +121,7 @@ literal:
* "Complex" conversion rules, implemented through recursion.
*/
case 'c': /* Date and time, using the locale's format. */
new_fmt = _ctloc(d_t_fmt);
new_fmt = _ctloc(c_fmt);
goto recurse;
case 'D': /* The date as "%m/%d/%y". */
@ -180,7 +140,7 @@ literal:
goto recurse;
case 'r': /* The time in 12-hour clock representation. */
new_fmt =_ctloc(t_fmt_ampm);
new_fmt =_ctloc(ampm_fmt);
LEGAL_ALT(0);
goto recurse;
@ -190,11 +150,11 @@ literal:
goto recurse;
case 'X': /* The time, using the locale's format. */
new_fmt =_ctloc(t_fmt);
new_fmt =_ctloc(X_fmt);
goto recurse;
case 'x': /* The date, using the locale's format. */
new_fmt =_ctloc(d_fmt);
new_fmt =_ctloc(x_fmt);
recurse:
bp = (const u_char *)strptime((const char *)bp,
new_fmt, tm);
@ -206,16 +166,16 @@ literal:
*/
case 'A': /* The day of week, using the locale's form. */
case 'a':
bp = find_string(bp, &tm->tm_wday, _ctloc(day),
_ctloc(abday), 7);
bp = find_string(bp, &tm->tm_wday, _ctloc(weekday),
_ctloc(wday), 7);
LEGAL_ALT(0);
continue;
case 'B': /* The month, using the locale's form. */
case 'b':
case 'h':
bp = find_string(bp, &tm->tm_mon, _ctloc(mon),
_ctloc(abmon), 12);
bp = find_string(bp, &tm->tm_mon, _ctloc(month),
_ctloc(mon), 12);
LEGAL_ALT(0);
continue;

764
winsup/cygwin/nlsfuncs.cc Normal file
View File

@ -0,0 +1,764 @@
/* nlsfuncs.cc: NLS helper functions
Copyright 2010 Red Hat, Inc.
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include <stdlib.h>
#include <winnls.h>
#include <wchar.h>
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
#include "tls_pbuf.h"
/* Internal headers from newlib */
#include "../locale/timelocal.h"
#include "../locale/lnumeric.h"
#include "../locale/lmonetary.h"
static char *lc_time_buf;
static char *lc_numeric_buf;
static char *lc_monetary_buf;
#define _LC(x) &lc_##x##_ptr,lc_##x##_end-lc_##x##_ptr
#define getlocaleinfo(category,type) \
__getlocaleinfo(lcid,(type),_LC(category),f_wctomb,charset)
#define eval_datetimefmt(type,force) \
__eval_datetimefmt(lcid,(type),(force),&lc_time_ptr,\
lc_time_end-lc_time_ptr,f_wctomb, charset)
/* Vista and later. Not defined in w32api yet. */
extern "C" {
WINBASEAPI LCID WINAPI LocaleNameToLCID (LPCWSTR, DWORD);
};
static char last_locale[ENCODING_LEN + 1];
static LCID last_lcid;
/* Fetch LCID from POSIX locale specifier.
Return values:
-1: Invalid locale
0: C or POSIX
>0: LCID
*/
static LCID
__get_lcid_from_locale (const char *name)
{
char locale[ENCODING_LEN + 1];
char *c;
LCID lcid;
if (!strcmp (name, last_locale))
{
debug_printf ("LCID=0x%04x", last_lcid);
return last_lcid;
}
stpcpy (last_locale, name);
stpcpy (locale, name);
/* Drop charset and modifier */
c = strchr (locale, '.');
if (!c)
c = strchr (locale, '@');
if (c)
*c = '\0';
/* "POSIX" already converted to "C" in loadlocale. */
if (!strcmp (locale, "C"))
return 0;
/* Convert to form understood by LocaleNameToLCID */
c = strchr (locale, '_');
if (c)
*c = '-';
if (wincap.has_localenames ())
{
wchar_t wlocale[ENCODING_LEN + 1];
mbstowcs (wlocale, locale, ENCODING_LEN + 1);
lcid = LocaleNameToLCID (wlocale, 0);
last_lcid = lcid ?: (LCID) -1;
debug_printf ("LCID=0x%04x", last_lcid);
return last_lcid;
}
/* Pre-Vista we have to loop through the LCID values and see if they
match language and TERRITORY. */
if (c)
*c++ = '\0';
/* locale now points to the language, c points to the TERRITORY */
const char *language = locale;
const char *territory = c;
LCID lang, sublang;
char iso[10];
/* In theory the lang part takes 10 bits (0x3ff), but up to Windows 2003 R2
the highest lang value is 0x81. */
for (lang = 1; lang <= 0x81; ++lang)
if (GetLocaleInfo (lang, LOCALE_SISO639LANGNAME, iso, 10)
&& !strcmp (language, iso))
break;
if (lang > 0x81)
lcid = 0;
else if (!territory)
lcid = lang;
else
{
/* In theory the sublang part takes 7 bits (0x3f), but up to
Windows 2003 R2 the highest sublang value is 0x14. */
for (sublang = 1; sublang <= 0x14; ++sublang)
{
lcid = (sublang << 10) | lang;
if (GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso, 10)
&& !strcmp (territory, iso))
break;
}
if (sublang > 0x14)
lcid = 0;
}
last_lcid = lcid ?: (LCID) -1;
debug_printf ("LCID=0x%04x", last_lcid);
return last_lcid;
}
/* Never returns -1, *iff* s is not NULL. Just skips invalid chars
instead. s==NULL returns -1 since it's used to recognize invalid
strings in the used charset. */
static size_t
lc_wcstombs (wctomb_p f_wctomb, const char *charset,
char *s, const wchar_t *pwcs, size_t n)
{
char *ptr = s;
size_t max = n;
char buf[8];
size_t i, bytes, num_to_copy;
mbstate_t state;
memset (&state, 0, sizeof state);
if (s == NULL)
{
size_t num_bytes = 0;
while (*pwcs != 0)
{
bytes = f_wctomb (_REENT, buf, *pwcs++, charset, &state);
if (bytes == (size_t) -1)
return (size_t) -1;
num_bytes += bytes;
}
return num_bytes;
}
while (n > 0)
{
bytes = f_wctomb (_REENT, buf, *pwcs, charset, &state);
if (bytes == (size_t) -1)
{
memset (&state, 0, sizeof state);
++pwcs;
continue;
}
num_to_copy = (n > bytes ? bytes : n);
for (i = 0; i < num_to_copy; ++i)
*ptr++ = buf[i];
if (*pwcs == 0x00)
return ptr - s - (n >= bytes);
++pwcs;
n -= num_to_copy;
}
return max;
}
/* Never returns -1. Invalid sequences are translated to replacement
wide-chars. */
static size_t
lc_mbstowcs (mbtowc_p f_mbtowc, const char *charset,
wchar_t *pwcs, const char *s, size_t n)
{
size_t ret = 0;
char *t = (char *) s;
size_t bytes;
mbstate_t state;
memset (&state, 0, sizeof state);
if (!pwcs)
n = 1;
while (n > 0)
{
bytes = f_mbtowc (_REENT, pwcs, t, MB_CUR_MAX, charset, &state);
if (bytes == (size_t) -1)
{
state.__count = 0;
bytes = 1;
if (pwcs)
*pwcs = L' ';
}
else if (bytes == 0)
break;
t += bytes;
++ret;
if (pwcs)
{
++pwcs;
--n;
}
}
return ret;
}
static char *
__getlocaleinfo (LCID lcid, LCTYPE type, char **ptr, size_t size,
wctomb_p f_wctomb, const char *charset)
{
wchar_t wbuf[80];
size_t num;
char *ret;
GetLocaleInfoW (lcid, type, wbuf, 80);
num = lc_wcstombs (f_wctomb, charset, ret = *ptr, wbuf, size);
*ptr += num + 1;
return ret;
}
static UINT
getlocaleint (LCID lcid, LCTYPE type)
{
UINT val;
return GetLocaleInfoW (lcid, type | LOCALE_RETURN_NUMBER, (PWCHAR) &val,
sizeof val) ? val : 0;
}
static char *
__eval_datetimefmt (LCID lcid, LCTYPE type, int force, char **ptr,
size_t size, wctomb_p f_wctomb, const char *charset)
{
wchar_t buf[80];
wchar_t fc;
size_t num;
mbstate_t mb;
size_t idx;
const char *day_str = "edaA";
const char *mon_str = "mmbB";
const char *year_str = "yyyY";
const char *hour12_str = "lI";
const char *hour24_str = "kH";
const char *t_str;
char *ret = *ptr;
char *p = *ptr;
GetLocaleInfoW (lcid, type, buf, 80);
memset (&mb, 0, sizeof mb);
for (wchar_t *fmt = buf; *fmt; ++fmt)
switch (fc = *fmt)
{
case L'\'':
if (fmt[1] == L'\'')
*p++ = '\'';
else
while (fmt[1] && *++fmt != L'\'')
{
num = f_wctomb (_REENT, p, *fmt, charset, &mb);
if (num == (size_t) -1)
memset (&mb, 0, sizeof mb);
else
p += num;
}
break;
case L'd':
case L'M':
case L'y':
t_str = (fc == L'd' ? day_str : fc == L'M' ? mon_str : year_str);
if (fc == L'y')
force = 0;
for (idx = 1; fmt[1] == fc; ++idx, ++fmt);
if (--idx > 3)
idx = 3;
if (force && idx == 3)
idx = 2;
*p++ = '%';
*p++ = t_str[idx];
break;
case L'g':
break;
case L'h':
case L'H':
t_str = (fc == L'h' || force ? hour12_str : hour24_str);
idx = 0;
if (fmt[1] == fc)
{
++fmt;
idx = 1;
}
*p++ = '%';
*p++ = t_str[idx];
break;
case L'm':
case L's':
case L't':
if (fmt[1] == fc)
++fmt;
*p++ = '%';
*p++ = (fc == L'm' ? 'M' : fc == L's' ? 'S' : 'p');
break;
case L'\t':
case L'\n':
case L'%':
*p++ = '%';
*p++ = (char) fc;
break;
default:
num = f_wctomb (_REENT, p, *fmt, charset, &mb);
if (num == (size_t) -1)
memset (&mb, 0, sizeof mb);
else
p += num;
break;
}
*p++ = '\0';
*ptr = p;
return ret;
}
/* Convert Windows grouping format into POSIX grouping format. */
static char *
conv_grouping (LCID lcid, LCTYPE type, char **lc_ptr)
{
char buf[10]; /* Per MSDN max size of LOCALE_SGROUPING element incl. NUL */
bool repeat = false;
char *ptr = *lc_ptr;
char *ret = ptr;
GetLocaleInfoA (lcid, type, buf, 10);
/* Convert Windows grouping format into POSIX grouping format. */
for (char *c = buf; *c; ++c)
{
if (*c < '0' || *c > '9')
continue;
char val = *c - '0';
if (!val)
{
repeat = true;
break;
}
*ptr++ = val;
}
if (!repeat)
*ptr++ = CHAR_MAX;
*ptr++ = '\0';
*lc_ptr = ptr;
return ret;
}
/* Called from newlib's setlocale() via __time_load_locale() if category
is LC_TIME. Returns LC_TIME values fetched from Windows locale data
in the structure pointed to by _time_locale. This is subsequently
accessed by functions like nl_langinfo, strftime, strptime. */
extern "C" int
__set_lc_time_from_win (const char *name, struct lc_time_T *_time_locale,
wctomb_p f_wctomb, const char *charset)
{
LCID lcid = __get_lcid_from_locale (name);
if (!lcid || lcid == (LCID) -1)
return lcid;
char *new_lc_time_buf = (char *) malloc (4096);
const char *lc_time_end = new_lc_time_buf + 4096;
if (!new_lc_time_buf)
return -1;
char *lc_time_ptr = new_lc_time_buf;
/* mon */
for (int i = 0; i < 12; ++i)
_time_locale->mon[i] = getlocaleinfo (time, LOCALE_SABBREVMONTHNAME1 + i);
/* month and alt_month */
for (int i = 0; i < 12; ++i)
_time_locale->month[i] = _time_locale->alt_month[i]
= getlocaleinfo (time, LOCALE_SMONTHNAME1 + i);
/* wday */
_time_locale->wday[0] = getlocaleinfo (time, LOCALE_SABBREVDAYNAME7);
for (int i = 0; i < 6; ++i)
_time_locale->wday[i + 1] = getlocaleinfo (time,
LOCALE_SABBREVDAYNAME1 + i);
/* weekday */
_time_locale->weekday[0] = getlocaleinfo (time, LOCALE_SDAYNAME7);
for (int i = 0; i < 6; ++i)
_time_locale->weekday[i + 1] = getlocaleinfo (time, LOCALE_SDAYNAME1 + i);
/* X_fmt */
_time_locale->X_fmt = eval_datetimefmt (LOCALE_STIMEFORMAT, 0);
/* x_fmt */
_time_locale->x_fmt = eval_datetimefmt (LOCALE_SSHORTDATE, 0);
/* c_fmt */
_time_locale->c_fmt = eval_datetimefmt (LOCALE_SLONGDATE, 1);
--lc_time_ptr;
*lc_time_ptr++ = ' ';
eval_datetimefmt (LOCALE_STIMEFORMAT, 0);
/* AM/PM */
_time_locale->am_pm[0] = getlocaleinfo (time, LOCALE_S1159);
_time_locale->am_pm[1] = getlocaleinfo (time, LOCALE_S2359);
/* date_fmt */
_time_locale->date_fmt = eval_datetimefmt (LOCALE_SLONGDATE, 1);
--lc_time_ptr;
*lc_time_ptr++ = ' ';
eval_datetimefmt (LOCALE_STIMEFORMAT, 0);
--lc_time_ptr;
lc_time_ptr = stpcpy (lc_time_ptr, " %Z") + 1;
/* md */
{
wchar_t buf[80];
GetLocaleInfoW (lcid, LOCALE_IDATE, buf, 80);
lc_time_ptr = stpcpy (lc_time_ptr, *buf == L'1' ? "dm" : "md") + 1;
}
/* ampm_fmt */
_time_locale->ampm_fmt = eval_datetimefmt (LOCALE_STIMEFORMAT, 1);
char *tmp = (char *) realloc (new_lc_time_buf, lc_time_ptr - new_lc_time_buf);
if (!tmp)
{
free (new_lc_time_buf);
return -1;
}
if (lc_time_buf)
free (lc_time_buf);
lc_time_buf = tmp;
return 1;
}
/* Called from newlib's setlocale() via __numeric_load_locale() if category
is LC_NUMERIC. Returns LC_NUMERIC values fetched from Windows locale data
in the structure pointed to by _numeric_locale. This is subsequently
accessed by functions like nl_langinfo, localeconv, printf, etc. */
extern "C" int
__set_lc_numeric_from_win (const char *name,
struct lc_numeric_T *_numeric_locale,
wctomb_p f_wctomb, const char *charset)
{
LCID lcid = __get_lcid_from_locale (name);
if (!lcid || lcid == (LCID) -1)
return lcid;
char *new_lc_numeric_buf = (char *) malloc (48);
const char *lc_numeric_end = new_lc_numeric_buf + 48;
if (!new_lc_numeric_buf)
return -1;
char *lc_numeric_ptr = new_lc_numeric_buf;
/* decimal_point */
_numeric_locale->decimal_point = getlocaleinfo (numeric,
LOCALE_SDECIMAL);
/* thousands_sep */
_numeric_locale->thousands_sep = getlocaleinfo (numeric,
LOCALE_STHOUSAND);
/* grouping */
_numeric_locale->grouping = conv_grouping (lcid, LOCALE_SGROUPING,
&lc_numeric_ptr);
char *tmp = (char *) realloc (new_lc_numeric_buf,
lc_numeric_ptr - new_lc_numeric_buf);
if (!tmp)
{
free (new_lc_numeric_buf);
return -1;
}
if (lc_numeric_buf)
free (lc_numeric_buf);
lc_numeric_buf = tmp;
return 1;
}
/* Called from newlib's setlocale() via __monetary_load_locale() if category
is LC_MONETARY. Returns LC_MONETARY values fetched from Windows locale data
in the structure pointed to by _monetary_locale. This is subsequently
accessed by functions like nl_langinfo, localeconv, printf, etc. */
extern "C" int
__set_lc_monetary_from_win (const char *name,
struct lc_monetary_T *_monetary_locale,
wctomb_p f_wctomb, const char *charset)
{
LCID lcid = __get_lcid_from_locale (name);
if (!lcid || lcid == (LCID) -1)
return lcid;
char *new_lc_monetary_buf = (char *) malloc (256);
const char *lc_monetary_end = new_lc_monetary_buf + 256;
if (!new_lc_monetary_buf)
return -1;
char *lc_monetary_ptr = new_lc_monetary_buf;
/* int_curr_symbol */
_monetary_locale->int_curr_symbol = getlocaleinfo (monetary,
LOCALE_SINTLSYMBOL);
/* No spacing char means space. */
if (!_monetary_locale->int_curr_symbol[3])
{
lc_monetary_ptr[-1] = ' ';
*lc_monetary_ptr++ = '\0';
}
/* currency_symbol */
{
/* As on Linux: If the currency_symbol can't be represented in the
given charset, use int_curr_symbol. */
wchar_t wbuf[14];
GetLocaleInfoW (lcid, LOCALE_SCURRENCY, wbuf, 14);
if (lc_wcstombs (f_wctomb, charset, NULL, wbuf, 0) == (size_t) -1)
{
_monetary_locale->currency_symbol = lc_monetary_ptr;
lc_monetary_ptr = stpncpy (lc_monetary_ptr,
_monetary_locale->int_curr_symbol, 3);
*lc_monetary_ptr++ = '\0';
}
else
_monetary_locale->currency_symbol = getlocaleinfo (monetary,
LOCALE_SCURRENCY);
}
/* mon_decimal_point */
_monetary_locale->mon_decimal_point = getlocaleinfo (monetary,
LOCALE_SMONDECIMALSEP);
/* mon_thousands_sep */
_monetary_locale->mon_thousands_sep = getlocaleinfo (monetary,
LOCALE_SMONTHOUSANDSEP);
/* mon_grouping */
_monetary_locale->mon_grouping = conv_grouping (lcid, LOCALE_SMONGROUPING,
&lc_monetary_ptr);
/* positive_sign */
_monetary_locale->positive_sign = getlocaleinfo (monetary,
LOCALE_SPOSITIVESIGN);
/* negative_sign */
_monetary_locale->negative_sign = getlocaleinfo (monetary,
LOCALE_SNEGATIVESIGN);
/* int_frac_digits */
*lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IINTLCURRDIGITS);
_monetary_locale->int_frac_digits = lc_monetary_ptr++;
/* frac_digits */
*lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_ICURRDIGITS);
_monetary_locale->frac_digits = lc_monetary_ptr++;
/* p_cs_precedes */
*lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IPOSSYMPRECEDES);
_monetary_locale->p_cs_precedes = lc_monetary_ptr++;
/* p_sep_by_space */
*lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IPOSSEPBYSPACE);
_monetary_locale->p_sep_by_space = lc_monetary_ptr++;
/* n_cs_precedes */
*lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_INEGSYMPRECEDES);
_monetary_locale->n_cs_precedes = lc_monetary_ptr++;
/* n_sep_by_space */
*lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_INEGSEPBYSPACE);
_monetary_locale->n_sep_by_space = lc_monetary_ptr++;
/* p_sign_posn */
*lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IPOSSIGNPOSN);
_monetary_locale->p_sign_posn = lc_monetary_ptr++;
/* p_sign_posn */
*lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_INEGSIGNPOSN);
_monetary_locale->n_sign_posn = lc_monetary_ptr++;
char *tmp = (char *) realloc (new_lc_monetary_buf,
lc_monetary_ptr - new_lc_monetary_buf);
if (!tmp)
{
free (new_lc_monetary_buf);
return -1;
}
if (lc_monetary_buf)
free (lc_monetary_buf);
lc_monetary_buf = tmp;
return 1;
}
static LCID collate_lcid = 0;
static mbtowc_p collate_mbtowc = __ascii_mbtowc;
static char collate_charset[ENCODING_LEN + 1] = "ASCII";
/* Called from newlib's setlocale() if category is LC_COLLATE. Stores
LC_COLLATE locale information. This is subsequently accessed by the
below functions strcoll, strxfrm, wcscoll, wcsxfrm. */
extern "C" int
__collate_load_locale (const char *name, mbtowc_p f_mbtowc, const char *charset)
{
LCID lcid = __get_lcid_from_locale (name);
if (lcid == (LCID) -1)
return -1;
collate_lcid = lcid;
collate_mbtowc = f_mbtowc;
stpcpy (collate_charset, charset);
return 0;
}
/* We use the Windows functions for locale-specific string comparison and
transformation. The advantage is that we don't need any files with
collation information. */
extern "C" int
wcscoll (const wchar_t *ws1, const wchar_t *ws2)
{
int ret;
if (!collate_lcid)
return wcscmp (ws1, ws2);
ret = CompareStringW (collate_lcid, 0, ws1, -1, ws2, -1);
if (!ret)
set_errno (EINVAL);
return ret - CSTR_EQUAL;
}
extern "C" int
strcoll (const char *s1, const char *s2)
{
size_t n1, n2;
wchar_t *ws1, *ws2;
tmp_pathbuf tp;
int ret;
if (!collate_lcid)
return strcmp (s1, s2);
/* The ANSI version of CompareString uses the default charset of the lcid,
so we must use the Unicode version. */
n1 = lc_mbstowcs (collate_mbtowc, collate_charset, NULL, s1, 0) + 1;
ws1 = (n1 > NT_MAX_PATH ? (wchar_t *) malloc (n1 * sizeof (wchar_t))
: tp.w_get ());
lc_mbstowcs (collate_mbtowc, collate_charset, ws1, s1, n1);
n2 = lc_mbstowcs (collate_mbtowc, collate_charset, NULL, s2, 0) + 1;
ws2 = (n2 > NT_MAX_PATH ? (wchar_t *) malloc (n2 * sizeof (wchar_t))
: tp.w_get ());
lc_mbstowcs (collate_mbtowc, collate_charset, ws2, s2, n2);
ret = CompareStringW (collate_lcid, 0, ws1, -1, ws2, -1);
if (n1 > NT_MAX_PATH)
free (ws1);
if (n2 > NT_MAX_PATH)
free (ws2);
if (!ret)
set_errno (EINVAL);
return ret - CSTR_EQUAL;
}
extern "C" size_t
wcsxfrm (wchar_t *ws1, const wchar_t *ws2, size_t wsn)
{
size_t ret;
if (!collate_lcid)
return wcslcpy (ws1, ws2, wsn);
ret = LCMapStringW (collate_lcid, LCMAP_SORTKEY,
ws2, -1, ws1, wsn * sizeof (wchar_t));
/* LCMapStringW returns byte count including the terminating NUL character,
wcsxfrm is supposed to return length in wchar_t excluding the NUL.
Since the array is only single byte NUL-terminated we must make sure
the result is wchar_t-NUL terminated. */
if (ret)
{
ret = (ret + 1) / sizeof (wchar_t);
if (ret >= wsn)
return wsn;
ws1[ret] = L'\0';
return ret;
}
if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
set_errno (EINVAL);
return wsn;
}
extern "C" size_t
strxfrm (char *s1, const char *s2, size_t sn)
{
size_t ret;
size_t n2;
wchar_t *ws2;
tmp_pathbuf tp;
if (!collate_lcid)
return strlcpy (s1, s2, sn);
/* The ANSI version of LCMapString uses the default charset of the lcid,
so we must use the Unicode version. */
n2 = lc_mbstowcs (collate_mbtowc, collate_charset, NULL, s2, 0) + 1;
ws2 = (n2 > NT_MAX_PATH ? (wchar_t *) malloc (n2 * sizeof (wchar_t))
: tp.w_get ());
lc_mbstowcs (collate_mbtowc, collate_charset, ws2, s2, n2);
/* The sort key is a NUL-terminated byte string. */
ret = LCMapStringW (collate_lcid, LCMAP_SORTKEY, ws2, -1, (PWCHAR) s1, sn);
if (n2 > NT_MAX_PATH)
free (ws2);
if (ret == 0)
{
if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
set_errno (EINVAL);
return sn;
}
/* LCMapStringW returns byte count including the terminating NUL character.
strxfrm is supposed to return length excluding the NUL. */
return ret - 1;
}
/* Fetch default ANSI codepage from locale info and generate a setlocale
compatible character set code. Called from newlib's setlocale(), if the
charset isn't given explicitely in the POSIX compatible locale specifier.
The function also returns a pointer to the corresponding _mbtowc_r function
which is used subsequently. */
extern "C" void
__set_charset_from_locale (const char *locale, char *charset)
{
UINT cp;
LCID lcid = __get_lcid_from_locale (locale);
/* "C" locale, or invalid locale? */
if (lcid == 0 || lcid == (LCID) -1)
{
__small_sprintf (charset, "ASCII");
return;
}
if (!GetLocaleInfoW (lcid,
LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
(PWCHAR) &cp, sizeof cp))
cp = 0;
/* codepage to de-facto standard charset transition. */
switch (cp)
{
case 874:
__small_sprintf (charset, "CP%u", cp);
break;
case 932:
strcpy (charset, "EUCJP");
break;
case 936:
strcpy (charset, "GBK");
break;
case 949:
strcpy (charset, "EUCKR");
break;
case 950:
strcpy (charset, "BIG5");
break;
case 1250:
strcpy (charset, "ISO-8859-2");
break;
case 1251:
strcpy (charset, "ISO-8859-5");
break;
case 1252:
strcpy (charset, "ISO-8859-1");
break;
case 1253:
strcpy (charset, "ISO-8859-7");
break;
case 1254:
strcpy (charset, "ISO-8859-9");
break;
case 1255:
strcpy (charset, "ISO-8859-8");
break;
case 1256:
strcpy (charset, "ISO-8859-6");
break;
case 1257:
strcpy (charset, "ISO-8859-13");
break;
case 1258:
default:
strcpy (charset, "UTF-8");
break;
}
if (cp >= 1250 && cp <= 1257)
{
char *c = strchr (locale, '@');
if (c && !strcmp (c + 1, "euro"))
strcpy (charset, "ISO-8859-15");
}
}

View File

@ -352,87 +352,6 @@ __big5_mbtowc (struct _reent *r, wchar_t *pwc, const char *s, size_t n,
return __db_mbtowc (r, pwc, s, n, 950, state);
}
/* Convert Windows codepage to a setlocale compatible character set code.
Called from newlib's setlocale() with codepage set to 0, if the
charset isn't given explicitely in the POSIX compatible locale specifier.
The function also returns a pointer to the corresponding _mbtowc_r
function. */
extern "C" mbtowc_p
__set_charset_from_codepage (UINT cp, char *charset)
{
if (cp == 0)
cp = GetACP ();
switch (cp)
{
case 437:
case 720:
case 737:
case 775:
case 850:
case 852:
case 855:
case 857:
case 858:
case 862:
case 866:
case 874:
case 1125:
case 1250:
case 1251:
case 1252:
case 1253:
case 1254:
case 1255:
case 1256:
case 1257:
case 1258:
case 20866:
case 21866:
__small_sprintf (charset, "CP%u", cp);
return __cp_mbtowc;
case 28591:
case 28592:
case 28593:
case 28594:
case 28595:
case 28596:
case 28597:
case 28598:
case 28599:
case 28603:
case 28605:
__small_sprintf (charset, "ISO-8859-%u", cp - 28590);
return __iso_mbtowc;
case 932:
strcpy (charset, "SJIS");
return __sjis_mbtowc;
case 936:
strcpy (charset, "GBK");
return __gbk_mbtowc;
case 949:
case 51949:
strcpy (charset, "EUCKR");
return __kr_mbtowc;
case 950:
strcpy (charset, "BIG5");
return __big5_mbtowc;
case 50220:
strcpy (charset, "JIS");
return __jis_mbtowc;
case 20932:
case 51932:
strcpy (charset, "EUCJP");
return __eucjp_mbtowc;
case 65001:
strcpy (charset, "UTF-8");
return __utf8_mbtowc;
default:
break;
}
strcpy (charset, "ASCII");
return __ascii_mbtowc;
}
/* Our own sys_wcstombs/sys_mbstowcs functions differ from the
wcstombs/mbstowcs API in three ways:

View File

@ -1,6 +1,6 @@
/* wchar.h: Extra wchar defs
Copyright 2007, 2009 Red Hat, Inc.
Copyright 2007, 2009, 2010 Red Hat, Inc.
This file is part of Cygwin.
@ -44,8 +44,6 @@ extern wctomb_f __utf8_wctomb;
extern char *__locale_charset ();
extern mbtowc_p __set_charset_from_codepage (unsigned int cp, char *charset);
#ifdef __cplusplus
}
#endif

View File

@ -1,7 +1,8 @@
/* wincap.cc -- figure out on which OS we're running. Set the
capability class to the appropriate values.
Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.
Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
2009, 2010 Red Hat, Inc.
This file is part of Cygwin.
@ -57,6 +58,7 @@ wincaps wincap_unknown __attribute__((section (".cygwin_dll_common"), shared)) =
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:false,
has_localenames:false,
};
wincaps wincap_nt4 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -96,6 +98,7 @@ wincaps wincap_nt4 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:false,
has_localenames:false,
};
wincaps wincap_nt4sp4 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -135,6 +138,7 @@ wincaps wincap_nt4sp4 __attribute__((section (".cygwin_dll_common"), shared)) =
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:false,
has_localenames:false,
};
wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -174,6 +178,7 @@ wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:false,
has_localenames:false,
};
wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -213,6 +218,7 @@ wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) =
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:false,
has_localenames:false,
};
wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -252,6 +258,7 @@ wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:false,
has_localenames:false,
};
wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -291,6 +298,7 @@ wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:false,
has_localenames:false,
};
wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -330,6 +338,7 @@ wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:false,
has_localenames:false,
};
wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -369,6 +378,7 @@ wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:false,
has_localenames:false,
};
wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -408,6 +418,7 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
has_console_handle_problem:false,
has_broken_alloc_console:false,
has_always_all_codepages:true,
has_localenames:true,
};
wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -447,6 +458,7 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_console_handle_problem:true,
has_broken_alloc_console:true,
has_always_all_codepages:true,
has_localenames:true,
};
wincapc wincap __attribute__((section (".cygwin_dll_common"), shared));

View File

@ -1,6 +1,7 @@
/* wincap.h: Header for OS capability class.
Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.
Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
2009, 2010 Red Hat, Inc.
This file is part of Cygwin.
@ -49,6 +50,7 @@ struct wincaps
unsigned has_console_handle_problem : 1;
unsigned has_broken_alloc_console : 1;
unsigned has_always_all_codepages : 1;
unsigned has_localenames : 1;
};
class wincapc
@ -104,6 +106,7 @@ public:
bool IMPLEMENT (has_console_handle_problem)
bool IMPLEMENT (has_broken_alloc_console)
bool IMPLEMENT (has_always_all_codepages)
bool IMPLEMENT (has_localenames)
#undef IMPLEMENT
};