44 *
55 * Portions Copyright (c) 2002-2010, PostgreSQL Global Development Group
66 *
7- * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.51 2010/01/02 16:57:54 momjian Exp $
7+ * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.52 2010/02/27 20: 16:17 momjian Exp $
88 *
99 *-----------------------------------------------------------------------
1010 */
@@ -386,6 +386,70 @@ free_struct_lconv(struct lconv * s)
386386 free (s -> positive_sign );
387387}
388388
389+ #ifdef WIN32
390+ static char * db_strdup (const char * item , const char * str )
391+ {
392+ int db_encoding = GetDatabaseEncoding ();
393+ size_t wchars , ilen , wclen , dstlen ;
394+ int utflen , bytes_per_char ;
395+ wchar_t * wbuf ;
396+ char * dst ;
397+
398+ if (!str [0 ])
399+ return strdup (str );
400+ ilen = strlen (str ) + 1 ;
401+ wclen = ilen * sizeof (wchar_t );
402+ wbuf = (wchar_t * ) palloc (wclen );
403+
404+ /* convert multi-byte string to a wide-character string */
405+ wchars = mbstowcs (wbuf , str , ilen );
406+ if (wchars == (size_t ) -1 )
407+ elog (ERROR ,
408+ "could not convert string to wide characters: error %lu" , GetLastError ());
409+
410+ /* allocate target string */
411+ bytes_per_char = pg_encoding_max_length (PG_UTF8 );
412+ if (pg_encoding_max_length (db_encoding ) > bytes_per_char )
413+ bytes_per_char = pg_encoding_max_length (db_encoding );
414+ dstlen = wchars * bytes_per_char + 1 ;
415+ if ((dst = malloc (dstlen )) == NULL )
416+ elog (ERROR , "could not allocate a destination buffer" );
417+
418+ /* Convert wide string to UTF8 */
419+ utflen = WideCharToMultiByte (CP_UTF8 , 0 , wbuf , wchars , dst , dstlen , NULL , NULL );
420+ if (utflen == 0 )
421+ elog (ERROR ,
422+ "could not convert string %04x to UTF-8: error %lu" , wbuf [0 ], GetLastError ());
423+ pfree (wbuf );
424+
425+ dst [utflen ] = '\0' ;
426+ if (db_encoding != PG_UTF8 )
427+ {
428+ PG_TRY ();
429+ {
430+ char * convstr = pg_do_encoding_conversion (dst , utflen , PG_UTF8 , db_encoding );
431+ if (dst != convstr )
432+ {
433+ strlcpy (dst , convstr , dstlen );
434+ pfree (convstr );
435+ }
436+ }
437+ PG_CATCH ();
438+ {
439+ FlushErrorState ();
440+ dst [0 ] = '\0' ;
441+ }
442+ PG_END_TRY ();
443+ }
444+
445+ return dst ;
446+ }
447+ #else
448+ static char * db_strdup (const char * item , const char * str )
449+ {
450+ return strdup (str );
451+ }
452+ #endif /* WIN32 */
389453
390454/*
391455 * Return the POSIX lconv struct (contains number/money formatting
@@ -398,6 +462,9 @@ PGLC_localeconv(void)
398462 struct lconv * extlconv ;
399463 char * save_lc_monetary ;
400464 char * save_lc_numeric ;
465+ #ifdef WIN32
466+ char * save_lc_ctype = NULL ;
467+ #endif
401468
402469 /* Did we do it already? */
403470 if (CurrentLocaleConvValid )
@@ -413,30 +480,83 @@ PGLC_localeconv(void)
413480 if (save_lc_numeric )
414481 save_lc_numeric = pstrdup (save_lc_numeric );
415482
416- setlocale (LC_MONETARY , locale_monetary );
417- setlocale (LC_NUMERIC , locale_numeric );
483+ #ifdef WIN32
484+ /*
485+ * WIN32 returns an inaccurately encoded symbol, e.g. Euro,
486+ * when the LC_CTYPE does not match the numeric or monetary
487+ * lc types, so we switch to matching LC_CTYPEs as we access them.
488+ */
489+
490+ if ((save_lc_ctype = setlocale (LC_CTYPE , NULL )) != NULL )
491+ {
492+ /* Save for later restore */
493+ save_lc_ctype = pstrdup (save_lc_ctype );
494+
495+ /* Set LC_CTYPE to match LC_MONETARY? */
496+ if (pg_strcasecmp (save_lc_ctype , locale_monetary ) != 0 )
497+ setlocale (LC_CTYPE , locale_monetary );
498+ }
499+ else
500+ /* LC_CTYPE not set, unconditionally set it */
501+ setlocale (LC_CTYPE , locale_monetary );
418502
419- /* Get formatting information */
503+ /*
504+ * If LC_NUMERIC and LC_MONETARY match, we can set it now and
505+ * avoid a second localeconv() call.
506+ */
507+ if (pg_strcasecmp (locale_numeric , locale_monetary ) == 0 )
508+ #else
509+ setlocale (LC_NUMERIC , locale_numeric );
510+ #endif
511+
512+ setlocale (LC_MONETARY , locale_monetary );
513+ /*
514+ * Get formatting information for LC_MONETARY, and LC_NUMERIC if they
515+ * are the same.
516+ */
420517 extlconv = localeconv ();
421518
422519 /*
423- * Must copy all values since restoring internal settings may overwrite
520+ * Must copy all values since restoring internal settings might overwrite
424521 * localeconv()'s results.
425522 */
426523 CurrentLocaleConv = * extlconv ;
427- CurrentLocaleConv .currency_symbol = strdup (extlconv -> currency_symbol );
428- CurrentLocaleConv .decimal_point = strdup (extlconv -> decimal_point );
429- CurrentLocaleConv .grouping = strdup (extlconv -> grouping );
430- CurrentLocaleConv .thousands_sep = strdup (extlconv -> thousands_sep );
431- CurrentLocaleConv .int_curr_symbol = strdup (extlconv -> int_curr_symbol );
432- CurrentLocaleConv .mon_decimal_point = strdup (extlconv -> mon_decimal_point );
524+
525+ /* The first argument of db_strdup() is only used on WIN32 */
526+ CurrentLocaleConv .currency_symbol = db_strdup ("currency_symbol" , extlconv -> currency_symbol );
527+ CurrentLocaleConv .int_curr_symbol = db_strdup ("int_curr_symbol" , extlconv -> int_curr_symbol );
528+ CurrentLocaleConv .mon_decimal_point = db_strdup ("mon_decimal_point" , extlconv -> mon_decimal_point );
433529 CurrentLocaleConv .mon_grouping = strdup (extlconv -> mon_grouping );
434- CurrentLocaleConv .mon_thousands_sep = strdup ( extlconv -> mon_thousands_sep );
435- CurrentLocaleConv .negative_sign = strdup ( extlconv -> negative_sign );
436- CurrentLocaleConv .positive_sign = strdup ( extlconv -> positive_sign );
530+ CurrentLocaleConv .mon_thousands_sep = db_strdup ( "mon_thousands_sep" , extlconv -> mon_thousands_sep );
531+ CurrentLocaleConv .negative_sign = db_strdup ( "negative_sign" , extlconv -> negative_sign );
532+ CurrentLocaleConv .positive_sign = db_strdup ( "positive_sign" , extlconv -> positive_sign );
437533 CurrentLocaleConv .n_sign_posn = extlconv -> n_sign_posn ;
438534
439- /* Try to restore internal settings */
535+ #ifdef WIN32
536+ /* Do we need to change LC_CTYPE to match LC_NUMERIC? */
537+ if (pg_strcasecmp (locale_numeric , locale_monetary ) != 0 )
538+ {
539+ setlocale (LC_CTYPE , locale_numeric );
540+ setlocale (LC_NUMERIC , locale_numeric );
541+ /* Get formatting information for LC_NUMERIC */
542+ extlconv = localeconv ();
543+ }
544+ #endif
545+
546+ CurrentLocaleConv .decimal_point = db_strdup ("decimal_point" , extlconv -> decimal_point );
547+ CurrentLocaleConv .grouping = strdup (extlconv -> grouping );
548+ CurrentLocaleConv .thousands_sep = db_strdup ("thousands_sep" , extlconv -> thousands_sep );
549+
550+ /*
551+ * Restore internal settings
552+ */
553+ #ifdef WIN32
554+ if (save_lc_ctype )
555+ {
556+ setlocale (LC_CTYPE , save_lc_ctype );
557+ pfree (save_lc_ctype );
558+ }
559+ #endif
440560 if (save_lc_monetary )
441561 {
442562 setlocale (LC_MONETARY , save_lc_monetary );
0 commit comments