*** ./src/backend/utils/adt/formatting.c.orig 2006-11-29 21:36:14.000000000 +0100 --- ./src/backend/utils/adt/formatting.c 2006-11-29 23:59:21.000000000 +0100 *************** *** 83,88 **** --- 83,96 ---- #include "utils/numeric.h" #include "utils/pg_locale.h" + #ifdef HAVE_WCHAR_H + #include + #endif + #ifdef HAVE_WCTYPE_H + #include + #endif + #include "mb/pg_wchar.h" + #define _(x) gettext((x)) /* ---------- *************** *** 920,925 **** --- 928,935 ---- static int strdigits_len(char *str); static char *str_toupper(char *buff); static char *str_tolower(char *buff); + static char *str_towupper(char *buff); + static char *str_towlower(char *buff); /* static int is_acdc(char *str, int *len); */ static int seq_search(char *name, char **array, int type, int max, int *len); *************** *** 2188,2197 **** if (!tm->tm_mon) return -1; if (S_TM(suf)) strcpy(workbuff, localize_month_full(tm->tm_mon - 1)); else strcpy(workbuff, months_full[tm->tm_mon - 1]); ! sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, str_toupper(workbuff)); return strlen(p_inout); case DCH_Month: --- 2198,2213 ---- if (!tm->tm_mon) return -1; if (S_TM(suf)) + { strcpy(workbuff, localize_month_full(tm->tm_mon - 1)); + str_towupper(workbuff); + } else + { strcpy(workbuff, months_full[tm->tm_mon - 1]); ! str_toupper(workbuff); ! } ! sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, workbuff); return strlen(p_inout); case DCH_Month: *************** *** 2209,2218 **** if (!tm->tm_mon) return -1; if (S_TM(suf)) sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1)); else sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]); ! *inout = pg_tolower((unsigned char) *inout); return strlen(p_inout); case DCH_MON: --- 2225,2239 ---- if (!tm->tm_mon) return -1; if (S_TM(suf)) + { sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1)); + str_towlower(inout); + } else + { sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]); ! *inout = pg_tolower((unsigned char) *inout); ! } return strlen(p_inout); case DCH_MON: *************** *** 2220,2229 **** if (!tm->tm_mon) return -1; if (S_TM(suf)) strcpy(inout, localize_month(tm->tm_mon - 1)); else strcpy(inout, months[tm->tm_mon - 1]); ! str_toupper(inout); return strlen(p_inout); case DCH_Mon: --- 2241,2255 ---- if (!tm->tm_mon) return -1; if (S_TM(suf)) + { strcpy(inout, localize_month(tm->tm_mon - 1)); + str_towupper(inout); + } else + { strcpy(inout, months[tm->tm_mon - 1]); ! str_toupper(inout); ! } return strlen(p_inout); case DCH_Mon: *************** *** 2241,2250 **** if (!tm->tm_mon) return -1; if (S_TM(suf)) strcpy(inout, localize_month(tm->tm_mon - 1)); else strcpy(inout, months[tm->tm_mon - 1]); ! *inout = pg_tolower((unsigned char) *inout); return strlen(p_inout); case DCH_MM: --- 2267,2281 ---- if (!tm->tm_mon) return -1; if (S_TM(suf)) + { strcpy(inout, localize_month(tm->tm_mon - 1)); + str_towlower(inout); + } else + { strcpy(inout, months[tm->tm_mon - 1]); ! *inout = pg_tolower((unsigned char) *inout); ! } return strlen(p_inout); case DCH_MM: *************** *** 2272,2281 **** case DCH_DAY: INVALID_FOR_INTERVAL; if (S_TM(suf)) strcpy(workbuff, localize_day_full(tm->tm_wday)); else strcpy(workbuff, days[tm->tm_wday]); ! sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, str_toupper(workbuff)); return strlen(p_inout); case DCH_Day: --- 2303,2318 ---- case DCH_DAY: INVALID_FOR_INTERVAL; if (S_TM(suf)) + { strcpy(workbuff, localize_day_full(tm->tm_wday)); + str_towupper(workbuff); + } else + { strcpy(workbuff, days[tm->tm_wday]); ! str_toupper(workbuff); ! } ! sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, workbuff); return strlen(p_inout); case DCH_Day: *************** *** 2289,2307 **** case DCH_day: INVALID_FOR_INTERVAL; if (S_TM(suf)) sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday)); else sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]); ! *inout = pg_tolower((unsigned char) *inout); return strlen(p_inout); case DCH_DY: INVALID_FOR_INTERVAL; if (S_TM(suf)) strcpy(inout, localize_day(tm->tm_wday)); else strcpy(inout, days_short[tm->tm_wday]); ! str_toupper(inout); return strlen(p_inout); case DCH_Dy: --- 2326,2354 ---- case DCH_day: INVALID_FOR_INTERVAL; if (S_TM(suf)) + { sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday)); + str_towlower(inout); + } else + { sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]); ! *inout = pg_tolower((unsigned char) *inout); ! } return strlen(p_inout); case DCH_DY: INVALID_FOR_INTERVAL; if (S_TM(suf)) + { strcpy(inout, localize_day(tm->tm_wday)); + str_towupper(inout); + } else + { strcpy(inout, days_short[tm->tm_wday]); ! str_toupper(inout); ! } return strlen(p_inout); case DCH_Dy: *************** *** 2315,2324 **** case DCH_dy: INVALID_FOR_INTERVAL; if (S_TM(suf)) strcpy(inout, localize_day(tm->tm_wday)); else strcpy(inout, days_short[tm->tm_wday]); ! *inout = pg_tolower((unsigned char) *inout); return strlen(p_inout); case DCH_DDD: --- 2362,2376 ---- case DCH_dy: INVALID_FOR_INTERVAL; if (S_TM(suf)) + { strcpy(inout, localize_day(tm->tm_wday)); + str_towlower(inout); + } else + { strcpy(inout, days_short[tm->tm_wday]); ! *inout = pg_tolower((unsigned char) *inout); ! } return strlen(p_inout); case DCH_DDD: *************** *** 5122,5124 **** --- 5174,5453 ---- NUM_TOCHAR_finish; PG_RETURN_TEXT_P(result); } + + /* + * str_towupper and str_towlower convert string with wide chars support. For + * details look to oracle_compat.c file. This functions are str version + * from oracle_compat. + */ + + #if defined(HAVE_WCSTOMBS) && defined(HAVE_TOWLOWER) + #define USE_WIDE_UPPER_LOWER + #endif + + + #ifdef USE_WIDE_UPPER_LOWER + + /* + * Convert a str value into a palloc'd wchar string. + */ + static wchar_t * + strtowcs(const char *str) + { + int nbytes = strlen(str); + wchar_t *result; + size_t ncodes; + + /* Overflow paranoia */ + if (nbytes < 0 || + nbytes > (int) (INT_MAX / sizeof(wchar_t)) - 1) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + + /* Output workspace cannot have more codes than input bytes */ + result = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t)); + + /* Do the conversion */ + ncodes = mbstowcs(result, str, nbytes + 1); + + if (ncodes == (size_t) -1) + { + /* + * Invalid multibyte character encountered. We try to give a useful + * error message by letting pg_verifymbstr check the string. But it's + * possible that the string is OK to us, and not OK to mbstowcs --- + * this suggests that the LC_CTYPE locale is different from the + * database encoding. Give a generic error message if verifymbstr + * can't find anything wrong. + */ + pg_verifymbstr(str, nbytes, false); + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("invalid multibyte character for locale"), + errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding."))); + } + + Assert(ncodes <= (size_t) nbytes); + + return result; + } + + + /* + * Convert a wchar string into a palloc'd str value. The wchar string + * must be zero-terminated, but we also require the caller to pass the string + * length, since it will know it anyway in current uses. + */ + static char * + wcstostr(const wchar_t *str, int ncodes) + { + char *result; + size_t nbytes; + + /* Overflow paranoia */ + if (ncodes < 0 || + ncodes > (int) ((INT_MAX - VARHDRSZ) / MB_CUR_MAX) - 1) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + + /* Make workspace certainly large enough for result */ + result = (char *) palloc((ncodes + 1) * MB_CUR_MAX); + + /* Do the conversion */ + nbytes = wcstombs((char *) result, str, + (ncodes + 1) * MB_CUR_MAX); + + if (nbytes == (size_t) -1) + { + /* Invalid multibyte character encountered ... shouldn't happen */ + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("invalid multibyte character for locale"))); + } + + Assert(nbytes <= (size_t) (ncodes * MB_CUR_MAX)); + + return result; + } + #endif /* USE_WIDE_UPPER_LOWER */ + + + /* + * On Windows, the "Unicode" locales assume UTF16 not UTF8 encoding. + * To make use of the upper/lower functionality, we need to map UTF8 to + * UTF16, which for some reason mbstowcs and wcstombs won't do for us. + * This conversion layer takes care of it. + */ + + #ifdef WIN32 + + /* strtowcs for the case of UTF8 to UTF16 */ + static wchar_t * + win32_utf8_strtowcs(const char *str) + { + int nbytes = strlen(str); + wchar_t *result; + int r; + + /* Overflow paranoia */ + if (nbytes < 0 || + nbytes > (int) (INT_MAX / sizeof(wchar_t)) - 1) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + + /* Output workspace cannot have more codes than input bytes */ + result = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t)); + + /* stupid Microsloth API does not work for zero-length input */ + if (nbytes == 0) + r = 0; + else + { + /* Do the conversion */ + r = MultiByteToWideChar(CP_UTF8, 0, str, nbytes, + result, nbytes); + + if (!r) /* assume it's NO_UNICODE_TRANSLATION */ + { + /* see notes above about error reporting */ + pg_verifymbstr(str, nbytes, false); + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("invalid multibyte character for locale"), + errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding."))); + } + } + + Assert(r <= nbytes); + result[r] = 0; + + return result; + } + + /* wcstotext for the case of UTF16 to UTF8 */ + static char * + win32_utf8_wcstostr(const wchar_t *str) + { + text *result; + int nbytes; + int r; + + nbytes = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + if (nbytes == 0) /* shouldn't happen */ + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("UTF-16 to UTF-8 translation failed: %lu", + GetLastError()))); + + result = palloc(nbytes); + + r = WideCharToMultiByte(CP_UTF8, 0, str, -1, result, nbytes, + NULL, NULL); + if (r == 0) /* shouldn't happen */ + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("UTF-16 to UTF-8 translation failed: %lu", + GetLastError()))); + + return result; + } + + /* interface layer to check which encoding is in use */ + + static wchar_t * + win32_strtowcs(const char *str) + { + if (GetDatabaseEncoding() == PG_UTF8) + return win32_utf8_strtowcs(str); + else + return texttowcs(str); + } + + static char * + win32_wcstostr(const wchar_t *str, int ncodes) + { + if (GetDatabaseEncoding() == PG_UTF8) + return win32_utf8_wcstostr(str); + else + return wcstostr(str, ncodes); + } + + /* use macros to cause routines below to call interface layer */ + + #define texttowcs win32_strtowcs + #define wcstotext win32_wcstostr + #endif /* WIN32 */ + + /* + * Returns string, with all letters forced to lowercase. + */ + static char * + str_towlower(char *buff) + { + #ifdef USE_WIDE_UPPER_LOWER + + /* + * Use wide char code only when max encoding length > 1 and ctype != C. + * Some operating systems fail with multi-byte encodings and a C locale. + * Also, for a C locale there is no need to process as multibyte. + */ + if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c()) + { + wchar_t *workspace; + int i; + + workspace = strtowcs(buff); + + for (i = 0; workspace[i] != 0; i++) + workspace[i] = towlower(workspace[i]); + + strcpy(buff, wcstostr(workspace, i)); + pfree(workspace); + + return buff; + } + else + #endif /* USE_WIDE_UPPER_LOWER */ + { + return str_tolower(buff); + } + } + + + /* + * Returns string, with all letters forced to uppercase. + */ + static char * + str_towupper(char *buff) + { + #ifdef USE_WIDE_UPPER_LOWER + + /* + * Use wide char code only when max encoding length > 1 and ctype != C. + * Some operating systems fail with multi-byte encodings and a C locale. + * Also, for a C locale there is no need to process as multibyte. + */ + if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c()) + { + wchar_t *workspace; + int i; + + workspace = strtowcs(buff); + + for (i = 0; workspace[i] != 0; i++) + workspace[i] = towupper(workspace[i]); + + strcpy(buff, wcstostr(workspace, i)); + pfree(workspace); + + return buff; + } + else + #endif /* USE_WIDE_UPPER_LOWER */ + { + return str_toupper(buff); + } + }