mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-28 02:47:18 +02:00
Fix string's length calculation for recoding, fix strlower() to avoid wrong assumption about length of recoded string (was: recoded string is no greater that source, it may not true for multibyte encodings) Thanks to Thomas H. <me@alternize.com> and Magnus Hagander <mha@sollentuna.net>
175 lines
3.3 KiB
C
175 lines
3.3 KiB
C
#include "ts_locale.h"
|
|
|
|
#include "utils/builtins.h"
|
|
#include "utils/pg_locale.h"
|
|
#include "mb/pg_wchar.h"
|
|
|
|
|
|
#ifdef TS_USE_WIDE
|
|
|
|
#ifdef WIN32
|
|
|
|
size_t
|
|
wchar2char(char *to, const wchar_t *from, size_t len)
|
|
{
|
|
if (GetDatabaseEncoding() == PG_UTF8)
|
|
{
|
|
int r;
|
|
|
|
if (len == 0)
|
|
return 0;
|
|
|
|
r = WideCharToMultiByte(CP_UTF8, 0, from, -1, to, len,
|
|
NULL, NULL);
|
|
|
|
if (r == 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
|
|
errmsg("UTF-16 to UTF-8 translation failed: %lu",
|
|
GetLastError())));
|
|
Assert(r <= len);
|
|
|
|
return r;
|
|
}
|
|
|
|
return wcstombs(to, from, len);
|
|
}
|
|
|
|
size_t
|
|
char2wchar(wchar_t *to, const char *from, size_t len)
|
|
{
|
|
if (GetDatabaseEncoding() == PG_UTF8)
|
|
{
|
|
int r;
|
|
|
|
if (len == 0)
|
|
return 0;
|
|
|
|
r = MultiByteToWideChar(CP_UTF8, 0, from, len, to, len);
|
|
|
|
if (!r)
|
|
{
|
|
pg_verifymbstr(from, strlen(from), 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 <= len);
|
|
|
|
return r;
|
|
}
|
|
|
|
return mbstowcs(to, from, len);
|
|
}
|
|
#endif /* WIN32 */
|
|
|
|
int
|
|
_t_isalpha(const char *ptr)
|
|
{
|
|
wchar_t character;
|
|
|
|
char2wchar(&character, ptr, 1);
|
|
|
|
return iswalpha((wint_t) character);
|
|
}
|
|
|
|
int
|
|
_t_isprint(const char *ptr)
|
|
{
|
|
wchar_t character;
|
|
|
|
char2wchar(&character, ptr, 1);
|
|
|
|
return iswprint((wint_t) character);
|
|
}
|
|
#endif /* TS_USE_WIDE */
|
|
|
|
char *
|
|
lowerstr(char *str)
|
|
{
|
|
char *ptr = str;
|
|
char *out;
|
|
int len = strlen(str);
|
|
|
|
if ( len == 0 )
|
|
return pstrdup("");
|
|
|
|
#ifdef TS_USE_WIDE
|
|
|
|
/*
|
|
* 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. From
|
|
* backend/utils/adt/oracle_compat.c Teodor
|
|
*/
|
|
if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
|
|
{
|
|
wchar_t *wstr,
|
|
*wptr;
|
|
int wlen;
|
|
|
|
/*
|
|
*alloc number of wchar_t for worst case, len contains
|
|
* number of bytes <= number of characters and
|
|
* alloc 1 wchar_t for 0, because wchar2char(wcstombs in really)
|
|
* wants zero-terminated string
|
|
*/
|
|
wptr = wstr = (wchar_t *) palloc(sizeof(wchar_t) * (len+1));
|
|
|
|
/*
|
|
* str SHOULD be cstring, so wlen contains number
|
|
* of converted character
|
|
*/
|
|
wlen = char2wchar(wstr, str, len);
|
|
if ( wlen < 0 )
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
|
|
errmsg("transalation failed from server encoding to wchar_t")));
|
|
|
|
Assert(wlen<=len);
|
|
wstr[wlen] = 0;
|
|
|
|
while (*wptr)
|
|
{
|
|
*wptr = towlower((wint_t) *wptr);
|
|
wptr++;
|
|
}
|
|
|
|
/*
|
|
* Alloc result string for worst case + '\0'
|
|
*/
|
|
len = sizeof(char)*pg_database_encoding_max_length()*(wlen+1);
|
|
out = (char*)palloc(len);
|
|
|
|
/*
|
|
* wlen now is number of bytes which is always >= number of characters
|
|
*/
|
|
wlen = wchar2char(out, wstr, len);
|
|
pfree(wstr);
|
|
|
|
if ( wlen < 0 )
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
|
|
errmsg("transalation failed from wchar_t to server encoding %d", errno)));
|
|
Assert(wlen<=len);
|
|
out[wlen]='\0';
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
char *outptr;
|
|
|
|
outptr = out = (char*)palloc( sizeof(char) * (len+1) );
|
|
while (*ptr)
|
|
{
|
|
*outptr++ = tolower(*(unsigned char *) ptr);
|
|
ptr++;
|
|
}
|
|
*outptr = '\0';
|
|
}
|
|
|
|
return out;
|
|
}
|