diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index ef89c70a1..fc312b759 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,29 @@ +2010-01-22 Corinna Vinschen + + * 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 * strfuncs.cc: Remove needless includes. diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index d23bde24a..6da49fabc 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -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 \ diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index bac1c1979..eb16961c9 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -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) diff --git a/winsup/cygwin/cygwin.din b/winsup/cygwin/cygwin.din index b2d2b1e47..46f03582c 100644 --- a/winsup/cygwin/cygwin.din +++ b/winsup/cygwin/cygwin.din @@ -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 diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 156d32ebd..f846ac22e 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -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 diff --git a/winsup/cygwin/include/monetary.h b/winsup/cygwin/include/monetary.h new file mode 100644 index 000000000..8b525e961 --- /dev/null +++ b/winsup/cygwin/include/monetary.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2001 Alexey Zelkin + * 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 +#include + +__BEGIN_DECLS +ssize_t strfmon(char * __restrict, size_t, const char * __restrict, ...); +__END_DECLS + +#endif /* !_MONETARY_H_ */ diff --git a/winsup/cygwin/libc/strfmon.c b/winsup/cygwin/libc/strfmon.c new file mode 100644 index 000000000..2c2523ee8 --- /dev/null +++ b/winsup/cygwin/libc/strfmon.c @@ -0,0 +1,621 @@ +/*- + * Copyright (c) 2001 Alexey Zelkin + * 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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} diff --git a/winsup/cygwin/libc/strptime.cc b/winsup/cygwin/libc/strptime.cc index e2f710ae9..e96de5917 100644 --- a/winsup/cygwin/libc/strptime.cc +++ b/winsup/cygwin/libc/strptime.cc @@ -45,53 +45,12 @@ __RCSID("$NetBSD: strptime.c,v 1.28 2008/04/28 20:23:01 martin Exp $"); #include #include #include +#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; diff --git a/winsup/cygwin/nlsfuncs.cc b/winsup/cygwin/nlsfuncs.cc new file mode 100644 index 000000000..073aa1329 --- /dev/null +++ b/winsup/cygwin/nlsfuncs.cc @@ -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 +#include +#include +#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"); + } +} diff --git a/winsup/cygwin/strfuncs.cc b/winsup/cygwin/strfuncs.cc index b62139409..849bd5dc9 100644 --- a/winsup/cygwin/strfuncs.cc +++ b/winsup/cygwin/strfuncs.cc @@ -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: diff --git a/winsup/cygwin/wchar.h b/winsup/cygwin/wchar.h index 1398238c1..820519af5 100644 --- a/winsup/cygwin/wchar.h +++ b/winsup/cygwin/wchar.h @@ -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 diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index 2fc76b166..56aac2453 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -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)); diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index 74955ff63..6a698d2f0 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -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 };